Android自定义星星评分控件

亦凉 2022-12-04 01:13 405阅读 0赞

下面为控件的实现历程:
此控件高效,直接使用ondraw绘制,先亮照:
这里写图片描述这里写图片描述
由于Android自身的星星评分控件样式可以改,但是他的大小不好调整的缺点,只能用small normal这样的style调整,自定义不强,因此击发了我自定义星星控件的欲望。
星星评分控件的设计,大体规划为:
需要两张图片,一颗亮星星,一颗空星星;(当然图片不一定是星星,其他图片也可以,现在实验就用星星就好了)星星数量,间距可以自定义,星星的最小步进为0.1,在用户使用的时候与Android自带的方法一样。
星星控件大体分为两层,第一层空星星,第二层亮星星,第一层固定,第二层动态绘制,这样就可以实现评分。
在画星星的时候,由于在xml得出回来的对象是drawable,不必再转换为bitmap绘制,故直接绘制drawable,并且提升效率。
绘制drawable需要两个方法就够了
1、设置绘制到那里:
setBounds(int left ,int top , int right ,int bottom);
2、绘制:
draw(Canvas canvas);
设置错误setBounds会导致绘制变形:
这里写图片描述这里写图片描述
把setbounds设置好后就一切正常:
这里写图片描述 这里写图片描述
经过一个for循环,五颗空星星就出来了,哈哈

  1. for (int i = 0;i < starCount;i++) {
  2. starEmptyDrawable.setBounds(starSize * i, 0, starSize * (i + 1), starSize);
  3. starEmptyDrawable.draw(canvas);
  4. }

这里写图片描述

  1. for (int i = 0;i < starCount;i++) {
  2. starEmptyDrawable.setBounds(starSize * i, 0, starSize * (i + 1), starSize);
  3. starEmptyDrawable.draw(canvas);
  4. }
  5. for (int i = 0;i < starCount -1;i++) {
  6. starFillDrawable.setBounds(starSize * i, 0, starSize * (i + 1), starSize);
  7. starFillDrawable.draw(canvas);
  8. }

上面几行代码成功强行装成了一个评了4分的这里写图片描述
现在,显示几颗几颗的星星无压力,但是我们目标是需要步进为0.1的星星。
But
经过一系列的实验,发现Drawable对象没有能指定绘制需要的部分,也就是不能绘制半颗星星(反正找不到,找到可以评论告诉我),然后就采用了折中的方法,把Drawable对象变为Bitmap这样就好办了,再利用BitmapShader,想绘制多少就绘制多上(就是实现0.1步进),下面为1/3颗的效果:
这里写图片描述
转换方法:

  1. private Bitmap drawableToBitmap(Drawable drawable)
  2. {
  3. if (drawable == null)return null;
  4. Bitmap bitmap = Bitmap.createBitmap(starSize, starSize, Bitmap.Config.ARGB_8888);
  5. Canvas canvas = new Canvas(bitmap);
  6. drawable.setBounds(0, 0, starSize, starSize);
  7. drawable.draw(canvas);
  8. return bitmap;
  9. }

把Bitmap转换为画笔绘制:

  1. paint = new Paint();
  2. paint.setAntiAlias(true);
  3. paint.setShader(new BitmapShader(starFillBitmap, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP));
  4. ondraw()方法绘制(三分之一个)
  5. canvas.drawRect(0,0,starSize/3,starSize,paint);

原理就是这样,剩下就是逻辑问题了,以下为星星控件代码:

  1. package com.dming.starbar;
  2. import android.content.Context;
  3. import android.content.res.TypedArray;
  4. import android.graphics.Bitmap;
  5. import android.graphics.BitmapShader;
  6. import android.graphics.Canvas;
  7. import android.graphics.Paint;
  8. import android.graphics.drawable.Drawable;
  9. import android.util.AttributeSet;
  10. import android.view.MotionEvent;
  11. import android.view.View;
  12. /**
  13. * Created by DMing on 2016/7/18.
  14. *
  15. */
  16. public class StarBar extends View{
  17. private int starDistance = 0; //星星间距
  18. private int starCount = 5; //星星个数
  19. private int starSize; //星星高度大小,星星一般正方形,宽度等于高度
  20. private float starMark = 0.0F; //评分星星
  21. private Bitmap starFillBitmap; //亮星星
  22. private Drawable starEmptyDrawable; //暗星星
  23. private OnStarChangeListener onStarChangeListener;//监听星星变化接口
  24. private Paint paint; //绘制星星画笔
  25. private boolean integerMark = false;
  26. public StarBar(Context context, AttributeSet attrs) {
  27. super(context, attrs);
  28. init(context, attrs);
  29. }
  30. public StarBar(Context context, AttributeSet attrs, int defStyleAttr) {
  31. super(context, attrs, defStyleAttr);
  32. init(context, attrs);
  33. }
  34. /**
  35. * 初始化UI组件
  36. *
  37. * @param context
  38. * @param attrs
  39. */
  40. private void init(Context context, AttributeSet attrs){
  41. setClickable(true);
  42. TypedArray mTypedArray = context.obtainStyledAttributes(attrs, R.styleable.RatingBar);
  43. this.starDistance = (int) mTypedArray.getDimension(R.styleable.RatingBar_starDistance, 0);
  44. this.starSize = (int) mTypedArray.getDimension(R.styleable.RatingBar_starSize, 20);
  45. this.starCount = mTypedArray.getInteger(R.styleable.RatingBar_starCount, 5);
  46. this.starEmptyDrawable = mTypedArray.getDrawable(R.styleable.RatingBar_starEmpty);
  47. this.starFillBitmap = drawableToBitmap(mTypedArray.getDrawable(R.styleable.RatingBar_starFill));
  48. mTypedArray.recycle();
  49. paint = new Paint();
  50. paint.setAntiAlias(true);
  51. paint.setShader(new BitmapShader(starFillBitmap, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP));
  52. }
  53. /**
  54. * 设置是否需要整数评分
  55. * @param integerMark
  56. */
  57. public void setIntegerMark(boolean integerMark){
  58. this.integerMark = integerMark;
  59. }
  60. /**
  61. * 设置显示的星星的分数
  62. *
  63. * @param mark
  64. */
  65. public void setStarMark(float mark){
  66. if (integerMark) {
  67. starMark = (int)Math.ceil(mark);
  68. }else {
  69. starMark = Math.round(mark * 10) * 1.0f / 10;
  70. }
  71. if (this.onStarChangeListener != null) {
  72. this.onStarChangeListener.onStarChange(starMark); //调用监听接口
  73. }
  74. invalidate();
  75. }
  76. /**
  77. * 获取显示星星的数目
  78. *
  79. * @return starMark
  80. */
  81. public float getStarMark(){
  82. return starMark;
  83. }
  84. /**
  85. * 定义星星点击的监听接口
  86. */
  87. public interface OnStarChangeListener {
  88. void onStarChange(float mark);
  89. }
  90. /**
  91. * 设置监听
  92. * @param onStarChangeListener
  93. */
  94. public void setOnStarChangeListener(OnStarChangeListener onStarChangeListener){
  95. this.onStarChangeListener = onStarChangeListener;
  96. }
  97. @Override
  98. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  99. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  100. setMeasuredDimension(starSize * starCount + starDistance * (starCount - 1), starSize);
  101. }
  102. @Override
  103. protected void onDraw(Canvas canvas) {
  104. super.onDraw(canvas);
  105. if (starFillBitmap == null || starEmptyDrawable == null) {
  106. return;
  107. }
  108. for (int i = 0;i < starCount;i++) {
  109. starEmptyDrawable.setBounds((starDistance + starSize) * i, 0, (starDistance + starSize) * i + starSize, starSize);
  110. starEmptyDrawable.draw(canvas);
  111. }
  112. if (starMark > 1) {
  113. canvas.drawRect(0, 0, starSize, starSize, paint);
  114. if(starMark-(int)(starMark) == 0) {
  115. for (int i = 1; i < starMark; i++) {
  116. canvas.translate(starDistance + starSize, 0);
  117. canvas.drawRect(0, 0, starSize, starSize, paint);
  118. }
  119. }else {
  120. for (int i = 1; i < starMark - 1; i++) {
  121. canvas.translate(starDistance + starSize, 0);
  122. canvas.drawRect(0, 0, starSize, starSize, paint);
  123. }
  124. canvas.translate(starDistance + starSize, 0);
  125. canvas.drawRect(0, 0, starSize * (Math.round((starMark - (int) (starMark))*10)*1.0f/10), starSize, paint);
  126. }
  127. }else {
  128. canvas.drawRect(0, 0, starSize * starMark, starSize, paint);
  129. }
  130. }
  131. @Override
  132. public boolean onTouchEvent(MotionEvent event) {
  133. int x = (int) event.getX();
  134. if (x < 0) x = 0;
  135. if (x > getMeasuredWidth()) x = getMeasuredWidth();
  136. switch(event.getAction()){
  137. case MotionEvent.ACTION_DOWN: {
  138. setStarMark(x*1.0f / (getMeasuredWidth()*1.0f/starCount));
  139. break;
  140. }
  141. case MotionEvent.ACTION_MOVE: {
  142. setStarMark(x*1.0f / (getMeasuredWidth()*1.0f/starCount));
  143. break;
  144. }
  145. case MotionEvent.ACTION_UP: {
  146. break;
  147. }
  148. }
  149. invalidate();
  150. return super.onTouchEvent(event);
  151. }
  152. /**
  153. * drawable转bitmap
  154. *
  155. * @param drawable
  156. * @return
  157. */
  158. private Bitmap drawableToBitmap(Drawable drawable)
  159. {
  160. if (drawable == null)return null;
  161. Bitmap bitmap = Bitmap.createBitmap(starSize, starSize, Bitmap.Config.ARGB_8888);
  162. Canvas canvas = new Canvas(bitmap);
  163. drawable.setBounds(0, 0, starSize, starSize);
  164. drawable.draw(canvas);
  165. return bitmap;
  166. }
  167. }

attrs的文件:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <resources>
  3. <declare-styleable name="RatingBar">
  4. <!--星星间距-->
  5. <attr format="dimension" name="starDistance"/>
  6. <!--星星大小-->
  7. <attr format="dimension" name="starSize"/>
  8. <!--星星个数-->
  9. <attr format="integer" name="starCount"/>
  10. <!--星星空图-->
  11. <attr format="reference" name="starEmpty"/>
  12. <!--星星满图-->
  13. <attr format="reference" name="starFill"/>
  14. </declare-styleable>
  15. </resources>
  16. XML的使用方式:
  17. <com.dming.starbar.StarBar
  18. android:id="@+id/starBar"
  19. android:layout_below="@+id/display"
  20. android:layout_width="wrap_content"
  21. android:layout_height="wrap_content"
  22. ratingbar:starEmpty="@drawable/star_empty"
  23. ratingbar:starFill="@drawable/star_full"
  24. ratingbar:starDistance="5dp"
  25. ratingbar:starCount="8"
  26. ratingbar:starSize="30dp"/>

完!!!

发表评论

表情:
评论列表 (有 0 条评论,405人围观)

还没有评论,来说两句吧...

相关阅读

    相关 android 定义

    Android自定义View实现很简单 继承View,重写构造函数、onDraw,(onMeasure)等函数。 如果自定义的View需要有自定义的属性,需要在values