美文网首页
自定义View - 11. 评分控件RatingBar

自定义View - 11. 评分控件RatingBar

作者: zsj1225 | 来源:发表于2018-06-24 20:21 被阅读65次

    今天我们要来实现这样的效果.


    xoane-n83ct.gif

    这个需要可交互的控件,可交互的控件一般分成两步来实现.

      1. 刚进来初始化的样子
      1. 处理用户交互部分(onTouch())

    1. 刚进来初始化的样子

    1.1 自定义属性 2张图片资源,评分的等级数量

     public RatingBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
    //        context.obtainStyledAttributes()
            //1.1 自定义属性 2张图片资源,评分的等级数量
            TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RatingBar);
            int starNormalId = typedArray.getResourceId(R.styleable.RatingBar_starNormal, 0);
            if (starNormalId == 0) {
                throw new RuntimeException("starNormal 属性没有设置");
            }
            mStarNormal = BitmapFactory.decodeResource(getResources(), starNormalId);
    
            int starFocusId = typedArray.getResourceId(R.styleable.RatingBar_starFocus, 0);
            if (starFocusId == 0) {
                throw new RuntimeException("starFocus 属性没有设置");
            }
            mStarFocus = BitmapFactory.decodeResource(getResources(), starFocusId);
    
            mGradeNumber = typedArray.getInt(R.styleable.RatingBar_gradeNumber, mGradeNumber);
            typedArray.recycle();
        }
    

    1.2 测量大小 onMeasure

        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            //1.2 测量大小
            //高度 = 星星的高度
            int height = mStarNormal.getHeight();
    
            //宽度 = 每一个星星的宽度 *  星星的个数 + Padding +间隔
            int width = mStarNormal.getWidth() * mGradeNumber;
    
            setMeasuredDimension(width, height);
        }
    

    1.3 绘制刚进来的样子

     @Override
        protected void onDraw(Canvas canvas) {
            //1.3 绘制刚进来的样子
            for (int i = 0; i < mGradeNumber; i++) {
                //paddingTop
                float top = 0;
                // 第一个星星: 0 , 第二个星星:一个星星的宽度
                float left = i * mStarNormal.getWidth();
    
                /**
                 * 如果mCurrentGrade = 0 就绘制默认状态的星星 ==>  mCurrentGrade=0 > i=0 为 false
                 * 如果mCurrentGrade = 1 就第一个绘制触摸的星星和其他位置绘制默认状态的星星
                 * ==>  mCurrentGrade=1 > i=0 为 true 绘制触摸状态的星星
                 * ==> mCurrentGrade=1 > i=1 为 false 绘制默认状态的星星
                 */
                if (mCurrentGrade > i) {
                    //绘制触摸状态的星星
                    canvas.drawBitmap(mStarFocus, left, top, null);
                } else {
                    //绘制默认状态的星星
                    canvas.drawBitmap(mStarNormal, left, top, null);
                }
    
            }
        }
    

    2. 处理用户交互部分(onTouch())

       @Override
        public boolean onTouchEvent(MotionEvent event) {
            //2. 处理用户交互部分(onTouch())
            Log.d(TAG, "onTouchEvent event =" + event.getAction());
            switch (event.getAction()) {
                //由于按下,移动,抬起都是处理一样的逻辑.所以写在一处
                case MotionEvent.ACTION_DOWN:
                case MotionEvent.ACTION_MOVE:
    //            case MotionEvent.ACTION_UP:   //2.3.1 尽量减少onDraw()的调用
                    //2.1 计算当前的触摸的分数.
                    /**
                     * 假设每一个星星的宽度是50
                     *
                     * 触摸的位置是40 --> 1 分  <==>  event.getX()/宽度 +1
                     * 触摸的位置是80 --> 2分   <==>  event.getX()/宽度 +1
                     */
                    int currentGrade = (int) (event.getX() / mStarNormal.getWidth()) + 1;
    
                    //范围问题
                    if (currentGrade < 0) {
                        currentGrade = 0;
                    }
    
                    if (currentGrade > mGradeNumber) {
                        currentGrade = mCurrentGrade;
                    }
    
                    //2.3.2如果分数和上一次是一致的就无需触发onDraw() 方法
                    if (currentGrade == mCurrentGrade) {
                        return true;
                    }
    
                    mCurrentGrade = currentGrade;
                    //再去刷新显示
                    invalidate();// 2.3 invalidate() 会触发layout布局所有view的Draw方法-> onDraw()方法.尽量减少触发
                    break;
    
                default:
                    break;
            }
    
    
            return true;//2.2 super.onTouchEvent(event)默认值是false,不消费,第一次进来DOWN,之后DOWN以后的事件是进不来的
        }
    

    完整代码:ratingbar

    相关文章

      网友评论

          本文标题:自定义View - 11. 评分控件RatingBar

          本文链接:https://www.haomeiwen.com/subject/izusyftx.html