#Android自定义控件–评分星级View
在开发电商项目中经常都会遇到一些星级评分控件的需求,有需求就必然有开发。废话不多说,没图没真相,上图:
#确定需求:
- 可以控制子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函数测量子控件大小,然后设置当前控件大小
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上
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();}elsefor (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();}elsefor (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方法去监听事件
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.StarBarViewandroid: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