自定义RatingBar

2019-04-02  本文已影响0人  飞奔吧牛牛

自定义RatingBar,可设置图片大小、间隔,是否可触摸改变等级

先看下效果:当手指滑动(或点击)时,RatingBar的等级会跟着改变

QQ截图20190402143805.png

第一步:自定义属性

    <declare-styleable name="MyRatingBar">
        <attr name="touchable" format="boolean" />
        <attr name="level" format="integer" />
        <attr name="item_size" format="dimension" />
        <attr name="item_gap" format="dimension" />
        <attr name="item_select_img" format="reference" />
        <attr name="item_unselect_img" format="reference" />
    </declare-styleable>

touchable :是否可触摸,改变level
level :等级
item_size :每个图片大小
item_gap :图片之间的间隔
item_select_img :选中时的图片
item_unselect_img :未选中时的图片

第二步:自定义View-MyRatingBar继承View,获取自定义属性

    public MyRatingBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.MyRatingBar);
        touchable = ta.getBoolean(R.styleable.MyRatingBar_touchable, false);
        itemSize = ta.getDimensionPixelSize(R.styleable.MyRatingBar_item_size, 60);
        itemGap = ta.getDimensionPixelSize(R.styleable.MyRatingBar_item_gap, 0);
        level = ta.getInt(R.styleable.MyRatingBar_level, 0);
        selectImage = ta.getDrawable(R.styleable.MyRatingBar_item_select_img);
        unselectImage = ta.getDrawable(R.styleable.MyRatingBar_item_unselect_img);
        paint = new Paint();
    }

第三步:重写onMeasure方法

当设置宽高大小为warp_content的时候,需要手动计算控件的大小

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        int width = 0;
        switch (widthMode) {
            case MeasureSpec.AT_MOST:
                width = itemSize * 5 + itemGap * 4 + getPaddingLeft() + getPaddingRight();
                break;
            case MeasureSpec.EXACTLY:
                width = widthSize;
                break;
            case MeasureSpec.UNSPECIFIED:
                width = itemSize * 5 + itemGap * 4 + getPaddingLeft() + getPaddingRight();
        }
        int height = 0;
        switch (heightMode) {
            case MeasureSpec.AT_MOST:
                height = itemSize + getPaddingTop() + getPaddingBottom();
                break;
            case MeasureSpec.EXACTLY:
                height = heightSize;
                break;
            case MeasureSpec.UNSPECIFIED:
                height = itemSize + getPaddingTop() + getPaddingBottom();
                break;
        }
        setMeasuredDimension(width, height);
    }

第四步:重写onDraw方法

先整理下思路,

  1. 我打算用for循环画出5个星星,每画一个,就将画布向左移动一段距离(itemSize+itemGap),接着画下一个,直到画完。
  2. 画每一个时要判断这个应该画哪个图片
    当touchable=false,比较简单,小于level的星星画成selectImage,大于level的为unselectImage.
    当touchable=true,应该根据当前手指触摸的位置到左边的距离,判断当前位置应该画哪个图片,并设置等级level。
@Override
    protected void onDraw(Canvas canvas) {
        if (selectImage != null && unselectImage != null) {
            int left = getPaddingLeft();
            for (int i = 0; i < 5; i++) {
                canvas.save();
                canvas.translate(left, 0);
                //是画选中状态的还是未选中状态的
                Drawable drawable = null;
                if (touchable && touchX != -1) {
                    if (touchX > getPaddingLeft() && touchX > left) {
                        if (touchX < left + itemSize + itemGap) {
                            level = i + 1;
                        }
                        drawable = selectImage;
                    } else {
                        drawable = unselectImage;
                    }
                } else {
                    drawable = i < level ? selectImage : unselectImage;
                }
                int intrinsicWidth = drawable.getIntrinsicWidth();
                int intrinsicHeight = drawable.getIntrinsicHeight();
                //获取bitmap
                Bitmap bitmap = Bitmap.createBitmap(intrinsicWidth, intrinsicHeight, Bitmap.Config.ARGB_8888);
                Canvas bitmapCanvas = new Canvas(bitmap);
                drawable.setBounds(0, 0, intrinsicWidth, intrinsicHeight);
                drawable.draw(bitmapCanvas);
                //伸缩图片,并绘制
                Matrix matrix = new Matrix();
                matrix.postScale((float) itemSize / intrinsicWidth, (float) itemSize / intrinsicHeight);
                canvas.drawBitmap(bitmap, matrix, paint);
                left += (itemSize + itemGap);
                canvas.restore();
            }
        }
    }

第五步:重写onTouchEvent方法

获取触摸点的x坐标

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        touchX = (int) event.getX();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                invalidate();
                break;
            case MotionEvent.ACTION_MOVE:
                invalidate();
                break;
            case MotionEvent.ACTION_UP:
                invalidate();
                break;
        }
        return true;
    }

第六步:向外提供getter方法

    public int getLevel() {
        return level;
    }

使用

在xml中:

 <com.aomiao.ratingbar.MyRatingBar
        android:id="@+id/ratingBar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@color/colorPrimary"
        app:item_gap="10dp"
        app:item_select_img="@mipmap/ic_rating_select"
        app:item_size="30dp"
        app:item_unselect_img="@mipmap/ic_rating"
        app:level="3"
        app:touchable="true" />

    <Button
        android:id="@+id/btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="等级" />
Toast.makeText(MainActivity.this,
                        "等级" + ratingBar.getLevel(),
                        Toast.LENGTH_SHORT).show();

RatingBar完整代码


import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

public class MyRatingBar extends View {
    private boolean touchable;
    private int itemSize;
    private int itemGap;
    private int level;
    private Drawable selectImage;
    private Drawable unselectImage;

    private Paint paint;
    //触摸时的x坐标
    private int touchX = -1;

    public MyRatingBar(Context context) {
        this(context, null);
    }

    public MyRatingBar(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MyRatingBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.MyRatingBar);
        touchable = ta.getBoolean(R.styleable.MyRatingBar_touchable, false);
        itemSize = ta.getDimensionPixelSize(R.styleable.MyRatingBar_item_size, 60);
        itemGap = ta.getDimensionPixelSize(R.styleable.MyRatingBar_item_gap, 0);
        level = ta.getInt(R.styleable.MyRatingBar_level, 0);
        selectImage = ta.getDrawable(R.styleable.MyRatingBar_item_select_img);
        unselectImage = ta.getDrawable(R.styleable.MyRatingBar_item_unselect_img);
        paint = new Paint();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        int width = 0;
        switch (widthMode) {
            case MeasureSpec.AT_MOST:
                width = itemSize * 5 + itemGap * 4 + getPaddingLeft() + getPaddingRight();
                break;
            case MeasureSpec.EXACTLY:
                width = widthSize;
                break;
            case MeasureSpec.UNSPECIFIED:
                width = itemSize * 5 + itemGap * 4 + getPaddingLeft() + getPaddingRight();
        }
        int height = 0;
        switch (heightMode) {
            case MeasureSpec.AT_MOST:
                height = itemSize + getPaddingTop() + getPaddingBottom();
                break;
            case MeasureSpec.EXACTLY:
                height = heightSize;
                break;
            case MeasureSpec.UNSPECIFIED:
                height = itemSize + getPaddingTop() + getPaddingBottom();
                break;
        }
        setMeasuredDimension(width, height);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if (selectImage != null && unselectImage != null) {
            int left = getPaddingLeft();
            for (int i = 0; i < 5; i++) {
                canvas.save();
                canvas.translate(left, 0);
                //是画选中状态的还是未选中状态的
                Drawable drawable = null;
                if (touchable && touchX != -1) {
                    if (touchX > getPaddingLeft() && touchX > left) {
                        if (touchX < left + itemSize + itemGap) {
                            level = i + 1;
                        }
                        drawable = selectImage;
                    } else {
                        drawable = unselectImage;
                    }
                } else {
                    drawable = i < level ? selectImage : unselectImage;
                }
                int intrinsicWidth = drawable.getIntrinsicWidth();
                int intrinsicHeight = drawable.getIntrinsicHeight();
                //获取bitmap
                Bitmap bitmap = Bitmap.createBitmap(intrinsicWidth, intrinsicHeight, Bitmap.Config.ARGB_8888);
                Canvas bitmapCanvas = new Canvas(bitmap);
                drawable.setBounds(0, 0, intrinsicWidth, intrinsicHeight);
                drawable.draw(bitmapCanvas);
                //伸缩图片,并绘制
                Matrix matrix = new Matrix();
                matrix.postScale((float) itemSize / intrinsicWidth, (float) itemSize / intrinsicHeight);
                canvas.drawBitmap(bitmap, matrix, paint);
                left += (itemSize + itemGap);
                canvas.restore();
            }
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        touchX = (int) event.getX();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                invalidate();
                break;
            case MotionEvent.ACTION_MOVE:
                invalidate();
                break;
            case MotionEvent.ACTION_UP:
                invalidate();
                break;
        }
        return true;
    }

    public int getLevel() {
        return level;
    }
}

上一篇下一篇

猜你喜欢

热点阅读