自定义RatingBar

在这里插入图片描述
效果图是这样的,下面贴上代码

package com.example.airinstallation.view;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import com.example.airinstallation.R;

public class AirRatingBar extends View {
    private static final String TAG = "AirRatingBar";
    /**
     * 默认,半个,全部选中
     */
    private Bitmap mRatingNormal, mRatingHalf, mRatingSelected;
    /**
     * 总数
     */
    private int mRatingTotalNum = 5;
    /**
     * 选中的数量
     */
    private float mRatingSelectedNumber;
    /**
     * 是否画全部
     */
    private Status status = Status.FULL;
    /**
     * 是否画全部
     * true 画全部
     * false 画半个
     */
    private boolean isFull;
    /**
     * 状态变化监听
     */
    private OnStarChangeListener mOnStarChangeListener;
    /**
     * 画笔
     */
    private Paint mPaint;
    /**
     * 间距
     */
    private int mRatingDistance;
    /**
     * 宽和高
     */
    private float mRatingWidth;
    private float mRatingHeight;

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

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

    public AirRatingBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initPaint();
        initTypeArray(context, attrs);
    }

    /**
     * 画笔
     */
    private void initPaint() {
        mPaint = new Paint();
    }

    /**
     * 属性
     *
     * @param context
     * @param attrs
     */
    private void initTypeArray(Context context, AttributeSet attrs) {
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.AirRatingBar);

        // 未选中的图片资源
        int starNormalId = array.getResourceId(R.styleable.AirRatingBar_ratingDefault, 0);
        if (starNormalId == 0) {
            throw new IllegalArgumentException("请设置属性 starNormal");
        }
        mRatingNormal = getBitmap(context, starNormalId);
        Log.e(TAG, "AirRatingBar: " + starNormalId);
        Log.e(TAG, "AirRatingBar: " + mRatingNormal);
        // 选中一半的图片资源
        int starHalfId = array.getResourceId(R.styleable.AirRatingBar_ratingHalf, 0);
        if (starHalfId != 0) {
            mRatingHalf = getBitmap(context, starHalfId);
        }
        Log.e(TAG, "AirRatingBar: " + starHalfId);

        // 选中全部的图片资源
        int starSelectedId = array.getResourceId(R.styleable.AirRatingBar_ratingSelect, 0);
        if (starSelectedId == 0) {
            throw new IllegalArgumentException("请设置属性 starSelected");
        }
        mRatingSelected = getBitmap(context, starSelectedId);
        Log.e(TAG, "AirRatingBar: " + starSelectedId);
        Log.e(TAG, "AirRatingBar: " + mRatingSelected);
        // 如果没设置一半的图片资源,就用全部的代替
        if (starHalfId == 0) {
            mRatingHalf = mRatingSelected;
        }

        mRatingTotalNum = array.getInt(R.styleable.AirRatingBar_ratingTotalNum, mRatingTotalNum);
        mRatingSelectedNumber = array.getFloat(R.styleable.AirRatingBar_ratingDefaultSelectNum, mRatingSelectedNumber);
        mRatingDistance = (int) array.getDimension(R.styleable.AirRatingBar_ratingDistance, 0);
        mRatingWidth = array.getDimension(R.styleable.AirRatingBar_ratingWidth, 0);
        mRatingHeight = array.getDimension(R.styleable.AirRatingBar_ratingHeight, 0);
        isFull = array.getBoolean(R.styleable.AirRatingBar_ratingIsFull, true);
        array.recycle();

        // 如有指定宽高,获取最大值 去改变星星的大小(星星是正方形)
        int starWidth = (int) Math.max(mRatingWidth, mRatingHeight);
        if (starWidth > 0) {
            mRatingNormal = resetBitmap(mRatingNormal, starWidth);
            mRatingSelected = resetBitmap(mRatingSelected, starWidth);
            mRatingHalf = resetBitmap(mRatingHalf, starWidth);
        }

        // 计算一半还是全部(小数部分小于等于0.5就只是显示一半)
        if (!isFull) {
            int num = (int) mRatingSelectedNumber;
            if (mRatingSelectedNumber <= (num + 0.5f)) {
                status = Status.HALF;
            }
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        // 循环绘制
        for (int i = 0; i < mRatingTotalNum; i++) {
            float left = getPaddingLeft();
            // 从第二个星星开始,给它设置星星的间距
            if (i > 0) {
                left = getPaddingLeft() + i * (mRatingNormal.getWidth() + mRatingDistance);
            }
            float top = getPaddingTop();
            // 绘制选中的星星
            if (i < mRatingSelectedNumber) {
                // 比当前选中的数量小
                if (i < mRatingSelectedNumber - 1) {
                    canvas.drawBitmap(mRatingSelected, left, top, mPaint);
                } else {
                    // 在这里判断是不是要绘制满的
                    if (status == Status.FULL) {
                        canvas.drawBitmap(mRatingSelected, left, top, mPaint);
                    } else {
                        canvas.drawBitmap(mRatingHalf, left, top, mPaint);
                    }
                }
            } else {
                // 绘制正常的星星
                canvas.drawBitmap(mRatingNormal, left, top, mPaint);
            }
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //减少绘制
        if (event.getAction() == MotionEvent.ACTION_MOVE) {
            // 获取用户触摸的x位置
            float x = event.getX();
            // 一个星星占的宽度
            int startWidth = getWidth() / mRatingTotalNum;
            // 计算用户触摸星星的位置
            int position = (int) (x / startWidth + 1);
            if (position < 0) {
                position = 0;
            }
            if (position > mRatingTotalNum) {
                position = mRatingTotalNum;
            }
            // 计算绘制的星星是不是满的
            float result = x - startWidth * (position - 1);
            Status statu;
            // 结果大于一半就是满的
            if (result > startWidth * 0.5f) {
                // 满的
                statu = Status.FULL;
            } else {
                // 一半的
                statu = Status.HALF;
            }
            if (isFull) {
                statu = Status.FULL;
            }
            //减少绘制
            if (mRatingSelectedNumber != position || statu != status) {
                mRatingSelectedNumber = position;
                status = statu;
                invalidate();
                if (mOnStarChangeListener != null) {
                    position = (int) (mRatingSelectedNumber - 1);
                    // 选中的数量:满的就回调(1.0这种),一半就(0.5这种)
                    float selectedNumber = status == Status.FULL ? mRatingSelectedNumber
                            : (mRatingSelectedNumber - 0.5f);
                    mOnStarChangeListener.OnStarChanged(selectedNumber,
                            position < 0 ? 0 : position);
                }
            }
        } else if (event.getAction() == MotionEvent.ACTION_DOWN) {
            // 获取用户触摸的x位置
            float x = event.getX();
            // 一个星星占的宽度
            int startWidth = getWidth() / mRatingTotalNum;
            // 计算用户触摸星星的位置
            int position = (int) (x / startWidth + 1);
            if (position < 0) {
                position = 0;
            }
            if (position > mRatingTotalNum) {
                position = mRatingTotalNum;
            }
            // 计算绘制的星星是不是满的
            float result = x - startWidth * (position - 1);
            Status statu;
            // 结果大于一半就是满的
            if (result > startWidth * 0.5f) {
                // 满的
                statu = Status.FULL;
            } else {
                // 一半的
                statu = Status.HALF;
            }
            if (isFull) {
                statu = Status.FULL;
            }
            //减少绘制
            if (mRatingSelectedNumber != position || statu != status) {
                mRatingSelectedNumber = position;
                status = statu;
                invalidate();
                if (mOnStarChangeListener != null) {
                    position = (int) (mRatingSelectedNumber - 1);
                    // 选中的数量:满的就回调(1.0这种),一半就(0.5这种)
                    float selectedNumber = status == Status.FULL ? mRatingSelectedNumber
                            : (mRatingSelectedNumber - 0.5f);
                    mOnStarChangeListener.OnStarChanged(selectedNumber,
                            position < 0 ? 0 : position);
                }
            }
        }
        return true;
    }

    /**
     * 如果用户设置了图片的宽高,就重新设置图片
     */
    public Bitmap resetBitmap(Bitmap bitMap, int startWidth) {
        // 得到新的图片
        return Bitmap.createScaledBitmap(bitMap, startWidth, startWidth, true);
    }

    /**
     * 设置选中星星的数量
     */
    public void setSelectedNumber(int selectedNumber) {
        if (selectedNumber >= 0 && selectedNumber <= mRatingTotalNum) {
            this.mRatingSelectedNumber = selectedNumber;
            invalidate();
        }
    }

    /**
     * 设置星星的总数量
     */
    public void setStartTotalNumber(int startTotalNumber) {
        if (startTotalNumber > 0) {
            this.mRatingTotalNum = startTotalNumber;
            invalidate();
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        // 用正常的一个星星图片去测量高
        int height = getPaddingTop() + getPaddingBottom() + mRatingNormal.getHeight();
        // 宽 = 星星的宽度*总数 + 星星的间距*(总数-1) +padding
        int width = getPaddingLeft() + getPaddingRight() + mRatingNormal.getWidth() * mRatingTotalNum + mRatingDistance * (mRatingTotalNum - 1);
        setMeasuredDimension(width, height);
    }

    /**
     * 获得bitmap
     *
     * @param context
     * @param vectorDrawableId
     * @return
     */
    private Bitmap getBitmap(Context context, int vectorDrawableId) {
        Bitmap bitmap;
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {
            Drawable vectorDrawable = context.getDrawable(vectorDrawableId);
            bitmap = Bitmap.createBitmap(vectorDrawable.getIntrinsicWidth(),
                    vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
            Canvas canvas = new Canvas(bitmap);
            vectorDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
            vectorDrawable.draw(canvas);
        } else {
            bitmap = BitmapFactory.decodeResource(context.getResources(), vectorDrawableId);
        }
        return bitmap;
    }


    /**
     * 全部 or 半个
     */
    private enum Status {
        FULL, HALF
    }

    public void setOnStarChangeListener(OnStarChangeListener onStarChangeListener) {
        mOnStarChangeListener = onStarChangeListener;
    }

    public interface OnStarChangeListener {
        void OnStarChanged(float selectedNumber, int position);
    }
}

属性文件,新建一个命名为attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="AirRatingBar">
        <!--未选中-->
        <attr name="ratingDefault" format="reference" />
        <!--选中半个-->
        <attr name="ratingHalf" format="reference" />
        <!--选中-->
        <attr name="ratingSelect" format="reference" />
        <!--总数-->
        <attr name="ratingTotalNum" format="integer" />
        <!--默认选中的数量 -->
        <attr name="ratingDefaultSelectNum" format="float" />
        <!--间距-->
        <attr name="ratingDistance" format="dimension" />
        <!--宽度-->
        <attr name="ratingWidth" format="dimension" />
        <!--高度-->
        <attr name="ratingHeight" format="dimension" />
        <!--整个 or 半个-->
        <attr name="ratingIsFull" format="boolean" />
    </declare-styleable>
</resources>

在布局里引用就可以了,下面举例我自己的,注意包名要改成自己项目的

            <com.example.airinstallation.view.AirRatingBar
                android:id="@+id/rb_assessment"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_centerVertical="true"
                app:ratingDefault="@drawable/svg_star_c126"
                app:ratingDefaultSelectNum="0"
                app:ratingDistance="4dp"
                app:ratingIsFull="true"
                android:layout_marginTop="20dp"
                app:ratingSelect="@drawable/svg_star_c201"
                app:ratingTotalNum="5" />

实现根据选择的星星的数量显示不同文字

mRatingBar.setOnStarChangeListener((selectedNumber, position) -> {
            if (1.0== selectedNumber) {
                mTvLevel.setText("非常不满意");
            } else if (2.0 == selectedNumber) {
                mTvLevel.setText("不满意");
            } else if (3.0 == selectedNumber) {
                mTvLevel.setText("一般");
            } else if (4.0 == selectedNumber) {
                mTvLevel.setText("满意");
            } else if (5.0 == selectedNumber) {
                mTvLevel.setText("非常满意");
            }
        });
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值