Android自定义控件--评分星级View

原创 2016年08月30日 15:11:28
在开发电商项目中经常都会遇到一些星级评分控件的需求,有需求就必然有开发。废话不多说,没图没真相,上图:

image

确定需求:

  • 可以控制子item之间的边距
  • 自定义选中图片和未选中图片
  • 摆放纵向或者横向
  • 可选择选中数量

基本绘制流程:

  • 1 自定义属性
<!--星星控件属性-->
    <declare-styleable name="StarBarView">
        <!--设置星星间的间隔-->
        <attr name="space_width" format="dimension" />
        <!--星星间宽度-->
        <attr name="star_width" format="dimension" />
        <!--星星间高度-->
        <attr name="star_height" format="dimension" />
        <!--最大数量-->
        <attr name="star_max" format="integer" />
        <!--选中数量-->
        <attr name="star_rating" format="float" />
        <!--未选中图片-->
        <attr name="star_hollow" format="reference" />
        <!--选中图片-->
        <attr name="star_solid" format="reference" />
        <!--是否可以滑动改变选中数量-->
        <attr name="star_isIndicator" format="boolean" />
        <!--排列方向-->
        <attr name="star_orientation" format="enum">
            <enum name="vertical" value="1" />
            <enum name="horizontal" value="0" />
        </attr>
    </declare-styleable>
  • 2 构造函数中获取自定义属性值
    //星星水平排列
    public static final int HORIZONTAL = 0;
    //星星垂直排列
    public static final int VERTICAL = 1;
    //实心图片
    private Bitmap mSolidBitmap;
    //空心图片
    private Bitmap mHollowBitmap;
    private Context context;
    //最大的数量
    private int starMaxNumber;
    private float starRating;
    private Paint paint;
    private int mSpaceWidth;//星星间隔
    private int mStarWidth;//星星宽度
    private int mStarHeight;//星星高度
    private boolean isIndicator;//是否是一个指示器(用户无法进行更改)
    private int mOrientation;

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

    public StarBarView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        paint = new Paint();
        this.context = context;
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.StarBarView, defStyle, 0);
        mSpaceWidth = a.getDimensionPixelSize(R.styleable.StarBarView_space_width, 0);
        mStarWidth = a.getDimensionPixelSize(R.styleable.StarBarView_star_width, 0);
        mStarHeight = a.getDimensionPixelSize(R.styleable.StarBarView_star_height, 0);
        starMaxNumber = a.getInt(R.styleable.StarBarView_star_max, 0);
        starRating = a.getFloat(R.styleable.StarBarView_star_rating, 0);
        mSolidBitmap = getZoomBitmap(BitmapFactory.decodeResource(context.getResources(), a.getResourceId(R.styleable.StarBarView_star_solid, 0)));
        mHollowBitmap = getZoomBitmap(BitmapFactory.decodeResource(context.getResources(), a.getResourceId(R.styleable.StarBarView_star_hollow, 0)));
        mOrientation = a.getInt(R.styleable.StarBarView_star_orientation, HORIZONTAL);
        isIndicator = a.getBoolean(R.styleable.StarBarView_star_isIndicator, false);
        a.recycle();
    }
  • 3 获取图片bitmap,设置宽高
/**
     * 获取缩放图片
     *
     * @param bitmap
     * @return
     */
    public Bitmap getZoomBitmap(Bitmap bitmap) {
        if (mStarWidth == 0 || mStarHeight == 0) {
            return bitmap;
        }
        // 获得图片的宽高
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();

        // 设置想要的大小
        int newWidth = mStarWidth;
        int newHeight = mStarHeight;
        // 计算缩放比例
        float scaleWidth = ((float) newWidth) / width;
        float scaleHeight = ((float) newHeight) / height;
        // 取得想要缩放的matrix参数
        Matrix matrix = new Matrix();
        matrix.postScale(scaleWidth, scaleHeight);
        // 得到新的图片
        Bitmap newbm = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
        return newbm;
    }
  • 4 onMeasure函数测量子控件大小,然后设置当前控件大小
@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (mOrientation == HORIZONTAL) {
            //判断是横向还是纵向,测量长度
            setMeasuredDimension(measureLong(widthMeasureSpec), measureShort(heightMeasureSpec));
        } else {
            setMeasuredDimension(measureShort(widthMeasureSpec), measureLong(heightMeasureSpec));
        }
    }

    private int measureLong(int measureSpec) {
        int result;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        if ((specMode == MeasureSpec.EXACTLY)) {
            result = specSize;
        } else {
            result = (int) (getPaddingLeft() + getPaddingRight() + (mSpaceWidth + mStarWidth) * (starMaxNumber));
            if (specMode == MeasureSpec.AT_MOST) {
                result = Math.min(result, specSize);
            }
        }
        return result;
    }

    private int measureShort(int measureSpec) {
        int result;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        if (specMode == MeasureSpec.EXACTLY) {
            result = specSize;
        } else {
            result = (int) (mStarHeight + getPaddingTop() + getPaddingBottom());
            if (specMode == MeasureSpec.AT_MOST) {
                result = Math.min(result, specSize);
            }
        }
        return result;
    }
  • 5 确定好数据后开始描绘bitmap到Canvas上
@Override
    protected void onDraw(Canvas canvas) {
        if (mHollowBitmap == null || mSolidBitmap == null) {
            return;
        }
        //绘制实心进度
        int solidStarNum = (int) starRating;
        //绘制实心的起点位置
        int solidStartPoint = 0;
        if (mOrientation == HORIZONTAL)
            for (int i = 1; i <= solidStarNum; i++) {
                canvas.drawBitmap(mSolidBitmap, solidStartPoint, 0, paint);
                solidStartPoint = solidStartPoint + mSpaceWidth + mSolidBitmap.getWidth();
            }
        else
            for (int i = 1; i <= solidStarNum; i++) {
                canvas.drawBitmap(mSolidBitmap, 0, solidStartPoint, paint);
                solidStartPoint = solidStartPoint + mSpaceWidth + mSolidBitmap.getHeight();
            }
        //虚心开始位置
        int hollowStartPoint = solidStartPoint;
        //多出的实心部分起点
        int extraSolidStarPoint = hollowStartPoint;
        //虚心数量
        int hollowStarNum = starMaxNumber - solidStarNum;
        if (mOrientation == HORIZONTAL)
            for (int j = 1; j <= hollowStarNum; j++) {
                canvas.drawBitmap(mHollowBitmap, hollowStartPoint, 0, paint);
                hollowStartPoint = hollowStartPoint + mSpaceWidth + mHollowBitmap.getWidth();
            }
        else
            for (int j = 1; j <= hollowStarNum; j++) {
                canvas.drawBitmap(mHollowBitmap, 0, hollowStartPoint, paint);
                hollowStartPoint = hollowStartPoint + mSpaceWidth + mHollowBitmap.getWidth();
            }
        //多出的实心长度
        int extraSolidLength = (int) ((starRating - solidStarNum) * mHollowBitmap.getWidth());
        Rect rectSrc = new Rect(0, 0, extraSolidLength, mHollowBitmap.getHeight());
        Rect dstF = new Rect(extraSolidStarPoint, 0, extraSolidStarPoint + extraSolidLength, mHollowBitmap.getHeight());
        canvas.drawBitmap(mSolidBitmap, rectSrc, dstF, paint);
    }
  • 6 最后通过onTouchEvent方法去监听事件
@Override
    public boolean onTouchEvent(MotionEvent event) {
        if (!isIndicator) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    if (mOrientation == HORIZONTAL) {
                        float TotalWidth = starMaxNumber * (mStarWidth + mSpaceWidth);
                        if (event.getX() <= TotalWidth) {
                            float newStarRating = (int) event.getX() / (mStarWidth + mSpaceWidth) + 1;
                            setStarRating(newStarRating);
                        }
                    }else{
                        float TotalHeight = starMaxNumber * (mStarHeight + mSpaceWidth);
                        if (event.getY() <= TotalHeight) {
                            float newStarRating = (int) event.getY() / (mStarHeight + mSpaceWidth) + 1;
                            setStarRating(newStarRating);
                        }
                    }
                    break;
                case MotionEvent.ACTION_MOVE:
//                    float starTotalWidth = starMaxNumber * (mStarWidth + mSpaceWidth);
//                    if (event.getX() <= starTotalWidth) {
//                        float newStarRating = (int) event.getX() / (mStarWidth + mSpaceWidth) + 1;
//                    setStarRating(newStarRating);
//                    }
                    break;
                case MotionEvent.ACTION_UP:
                    break;
                case MotionEvent.ACTION_CANCEL:
                    break;
            }
        }
        return super.onTouchEvent(event);
    }

如何使用:

  • xml布局
<com.caption.starbarexample.widget.StarBarView
        android:id="@+id/sbv_starbar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:layout_margin="10dp"
        app:space_width="1dp"
        app:star_height="25dp"
        app:star_hollow="@mipmap/ic_star_yellow_normal"
        app:star_isIndicator="false"
        app:star_max="5"
        app:star_orientation="horizontal"
        app:star_rating="2"
        app:star_solid="@mipmap/ic_star_yellow_selected"
        app:star_width="25dp" />
  • 代码添加
    //拿到当前星星数量
    mStarbar.getStarRating();

github:https://github.com/GHdeng/StarBarExample

Android 自定义星级评分控件

自定义Android 星级评分控件,先上一下效果图 关于控件的制作步骤: 1.首先要确定控件所需要用到的属性,那位作为一个星级评分的控件,能想到的就是大小,形状,数量,评分设置标准,是否允许点击操...
  • z690798364
  • z690798364
  • 2017年01月12日 14:44
  • 960

Android星级评分条控件RatingBar

Android开发中,时不时的就有要实现星星的评分效果,比如某宝,某团,相信大家也都见过,当然了我们可以自己去画,也可以用美工给切的图去实现,其实在Android原生的控件中就可以来实现这样的效果,它...
  • ming_147
  • ming_147
  • 2016年08月24日 14:32
  • 4679

Android 自定义 RatingBar (评分控件)

RatingBar是基于SeekBar(拖动条)和ProgressBar(状态条)的扩展,用星形来显示等级评定,默认的效果是几颗绿色的星星,效果比较难看,大小只有两种风格,一种是ratingBarSt...
  • qq_29757893
  • qq_29757893
  • 2016年04月29日 11:14
  • 799

android自定义评分条(RatingBar)

android自定义评分条(RatingBar)本自定义控件是因为本人在开发过程中,评分条的适配出现问题而诞生,在网上找了些自定义控件,发现都不是自己想要的,所以只能自己动手。 原生的Rat...
  • liu_xiong
  • liu_xiong
  • 2017年01月13日 16:19
  • 2562

Android自定义星星评分控件,高效

下面为控件的实现历程: 先亮照: 由于Android自身的星星评分控件样式可以改,但是他的大小不好调整的缺点,只能用small normal这样的style调整,自定义不强,...
  • a756213932
  • a756213932
  • 2016年07月18日 09:53
  • 10892

Android 自定义星星可滑动评分控件

此控件通过线性布局结合ImageView来实现。具有展示分数,滑动评分功能,可设置0-10分,自行设置星星图片,是否可点击与滑动,星星间距。...
  • LX_LE
  • LX_LE
  • 2016年11月10日 23:48
  • 1386

IOS-一步一步教你自定义评分星级条RatingBar

由于项目的需要,需要设计能评分、能显示评分数据的星级评分条,但是IOS上好像没有这个控件,Android是有RatingBar这个控件的(又发现一个IOS不如Android好的),那就只能自定义了,在...
  • hanhailong726188
  • hanhailong726188
  • 2015年01月02日 20:55
  • 24604

iOS第三方星级视图控件——HCSStarRatingView

iOS第三方星级视图控件——HCSStarRatingViewHCStarRatingView是一款十分小巧的星级视图控件,其通过原生画图的方式来渲染星级视图页面,同时,其也支持开发者对星级图片的自定...
  • shenhaifeiniao
  • shenhaifeiniao
  • 2016年09月14日 11:14
  • 1313

安卓RatingBar的使用(评分控件)

安卓RatingBar的使用(评分控件)
  • Jeff169
  • Jeff169
  • 2016年08月21日 11:05
  • 5347

android自定义控件(星级评分)

一、背景 视觉过来提了一个需求,要求完成一个星级评分控件,该控件中的星星的颜色需要实现渐变的效果,并且没有渐变的规律,也就是说各个星星的颜色需要不一样,效果如下: 二、问题分析 星星控件...
  • xiao_nian
  • xiao_nian
  • 2017年02月28日 14:43
  • 21095
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Android自定义控件--评分星级View
举报原因:
原因补充:

(最多只允许输入30个字)