Android 自定义控件RatingBarView(星星评分)

Android有个RatingBar的评分控件,不过在实际项目里局限性非常明显(无法准确并动态设置控件的大小)。所以,我有必要写个控件(不继承RatingBar),来弥补这个缺陷。

自定义控件一定要用到自定义属性(attrs.xml里设置),这将会大幅度提高开发效率。以下是RatingBarView用到的自定义属性:

    <declare-styleable name="RatingBarView">
        <!-- 星星宽度 -->
        <attr name="starWidth" format="dimension" />
        <!-- 星星高度 -->
        <attr name="starHeight" format="dimension" />
        <!-- 星星数量 -->
        <attr name="starNum" format="integer" />
        <!-- 星星当前进度 -->
        <attr name="starRating" format="dimension" />
        <!-- 星星之间的空隙 -->
        <attr name="starSpace" format="dimension" />
        <!--进度方式,整星还是半星-->
        <attr name="starStep">
            <enum name="Full" value="1" />
            <enum name="Half" value="2" />
        </attr>
        <!-- 没选中 -->
        <attr name="starDrawableEmpty" format="reference|color"/>
        <!-- 选中,进度是半星 -->
        <attr name="starDrawableHalf" format="reference|color"/>
        <!-- 选中,进度是整星 -->
        <attr name="starDrawableFill" format="reference|color"/>
    </declare-styleable>

设置好自定义属性,在自定义控件(xxView extends View)的构造方法xxView(Context context, @Nullable AttributeSet attrs, int defStyleAttr)中获取自己定义好的属性

    //无论调用哪个构造方法,都会调用第三个
    //一般我是直接在.xml中使用控件,不会去用代码创建
    public RatingBarView(Context context) {
        this(context, null);
    }

    public RatingBarView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public RatingBarView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mGradientHelper = new GradientHelper(context,attrs);
        if (attrs != null) {
            TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RatingBarView);
            mStarWidth = typedArray.getDimensionPixelSize(R.styleable.RatingBarView_starWidth, 30);
            mStarHeight = typedArray.getDimensionPixelSize(R.styleable.RatingBarView_starHeight, 30);
            mStarNum = typedArray.getInt(R.styleable.RatingBarView_starNum, 5);
            mStarSpace = typedArray.getDimensionPixelSize(R.styleable.RatingBarView_starSpace, 0);
            mStarStep = typedArray.getDimensionPixelSize(R.styleable.RatingBarView_starStep, 1);
            mStarRating = typedArray.getDimension(R.styleable.RatingBarView_starRating, 3);
            mStarDrawableEmpty = typedArray.getDrawable(R.styleable.RatingBarView_starDrawableEmpty);
            mStarDrawableHalf = typedArray.getDrawable(R.styleable.RatingBarView_starDrawableHalf);
            mStarDrawableFill = typedArray.getDrawable(R.styleable.RatingBarView_starDrawableFill);

            typedArray.recycle();
        }
}

到这里,我需要的条件已经集齐,可以绘制星星了。

目标效果图(不包括触摸事件):

实现上图,需要:星星宽高、星星数量、当前星星显示数量、星星之间的空隙、星星选择与未选中时的图片(自定义属性已经包含了)

实现思路:

  1. 根据星星数量和空隙外加控件的padding计算控件的宽高(宽度=星星宽度*星星数量+(星星数量-1)*星星间隙+ getPaddingLeft() + getPaddingRight(),高度=星星高度+ getPaddingTop() + getPaddingBottom())
  2. 计算星星的显示位置
  3. 按位置绘制星星

 实现核心代码:

    //设置控件的宽高    
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //宽度=星星宽度*星星数量+(星星数量-1)*星星间隙+ getPaddingLeft() + getPaddingRight()
        int starSumWidth = mStarWidth*mStarNum + (mStarNum-1)*mStarSpace + getPaddingLeft() + getPaddingRight();
        int measureWidth = MeasureSpec.makeMeasureSpec(starSumWidth, MeasureSpec.EXACTLY);
        int measureHeight = MeasureSpec.makeMeasureSpec(mStarHeight + getPaddingTop() + getPaddingBottom(), MeasureSpec.EXACTLY);
        setMeasuredDimension(measureWidth, measureHeight);
    }

    
    //记录每个星星的位置
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        int left = getPaddingLeft();
        int top = getPaddingTop();
        for (int i=0;i<mStarNum;i++) {
            Rect rect = new Rect(left, top, mStarWidth+left,mStarHeight+top);
            mStarRect[i] = rect;
            left += (mStarWidth+mStarSpace);
        }
    }


    //按星星位置绘制星星,在onDraw()中使用
    private void drawStar(Canvas canvas) {
        for (int i=0;i<mStarRect.length;i++) {
            Drawable drawable = new BitmapDrawable(null,drawableToBitmap(mStarDrawableEmpty));
            if (i <= mStarRating-1) {
                drawable = new BitmapDrawable(null,drawableToBitmap(mStarDrawableFill));
            }
            Log.e("第"+i,""+new Gson().toJson(mStarRect[i]));
            drawable.setBounds(mStarRect[i]);
            drawable.draw(canvas);
        }
    }

圆角背景实现:我是使用GradientDrawable来实现圆角背景(自定义TextView中用到过)

    gradientColorArr[0] = gradientColorStart;//渐变开始颜色
    gradientColorArr[1] = gradientColorEnd;//渐变结束颜色
    //从左到右的渐变
    GradientDrawable gradientDrawable = new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT,gradientColorArr);
    gradientDrawable.setBounds(bounds);
    gradientDrawable.setShape(GradientDrawable.RECTANGLE);
    gradientDrawable.setCornerRadii(radiusGradientArray);//圆角设置

运行效果图

代码链接:https://github.com/wudengwei/RatingBarView

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值