1.实现效果
可以点击或滑动选评分
准备: 1.需要选中和未选中的图片资源。 2.自定义CustomRatingBar, 确定自定义属性
<declare-styleable name="CustomRatingBar"
<!-- 总评分大小, 选中和未选中图片资源,评分item之间的间隔-->
<attr name="gradeNumber" format="integer" />
<attr name="startNormal" format="reference" />
<attr name="startFocus" format="reference" />
<attr name="itemPadding" format="dimension" />
</declare-styleable>
3.自定义view:CustomRatingBar (1)绘制初始状态,即未选中的状态 (2)处理交互的效果,即处理点击和移动的事件,计算出需绘制的评分个数
代码:
public class CustomRatingBar extends View {
private static final int DEFAULT_GRADE_NUMBER = 5;
/** 评分的等级数量*/
private int mGradeNumber = DEFAULT_GRADE_NUMBER;
/** 每个星级评分之间的间隔*/
private int mItemPadding = 0;
/** 当前选中的评分*/
private int mCurrentFocusNumber = 0;
private Bitmap mStarNormalBitmap;
private Bitmap mStarFocusBitmap;
public CustomRatingBar(Context context) {
this(context, null);
}
public CustomRatingBar(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomRatingBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.CustomRatingBar);
int starNormalId = array.getResourceId(R.styleable.CustomRatingBar_startNormal, R.drawable.rating_star_on_normal);
mStarNormalBitmap = BitmapFactory.decodeResource(context.getResources(), starNormalId);
int focusNormalId = array.getResourceId(R.styleable.CustomRatingBar_startFocus, R.drawable.rating_star_on_focus);
mStarFocusBitmap = BitmapFactory.decodeResource(context.getResources(), focusNormalId);
mGradeNumber = array.getInt(R.styleable.CustomRatingBar_gradeNumber, DEFAULT_GRADE_NUMBER);
float itemPaddingDp = array.getDimension(R.styleable.CustomRatingBar_itemPadding, 0);
mItemPadding = dip2px(context, itemPaddingDp);
array.recycle();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// 以图片的高度作为控件的高度
int height = mStarNormalBitmap.getHeight();
int width = MeasureSpec.getSize(widthMeasureSpec);
setMeasuredDimension(width, height);
}
@Override
protected void onDraw(Canvas canvas) {
for (int i = 0; i < mGradeNumber; i++) {
int itemPadding = mItemPadding;
if (i == 0) {
itemPadding = 0;
}
// 绘制图片的x坐标:图片宽度 + 间隔(绘制第一个时不需要加上间隔)
int x = (mStarNormalBitmap.getWidth() + itemPadding) * i;
Bitmap drawBitmap = i < mCurrentFocusNumber ? mStarFocusBitmap : mStarNormalBitmap;
canvas.drawBitmap(drawBitmap, x, 0, null);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
int x = (int) event.getX();
int currentFocusNumber = (int) (x * 1.0 / (mStarNormalBitmap.getWidth() + mItemPadding)) + 1;
// 减少绘制次数,评分不同时才重绘
if (mCurrentFocusNumber != currentFocusNumber) {
mCurrentFocusNumber = currentFocusNumber;
invalidate();
}
}
// 第一次接收到down事件,return true,后续的move和up事件才能传递到当前view。 比如可以单独针对down事件return true,也是可以正常绘制的
return true;
}
// 根据手机的分辨率从 dp 的单位 转成为 px(像素)
public static int dip2px(Context context, float dpValue) {
// 获取当前手机的像素密度(1个dp对应几个px)
float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f); // 四舍五入取整
}
}