先看效果(原谅我的渣像素),进度的刻度、宽度、颜色可以随意设定:

【项目github地址: https://github.com/zhangke3016/CircleLoading

实现起来并不难,通过本文,我们可以学到:

1、自定义属性的使用。

2、shader的使用

3、自定义View中对onmeasure的处理

4、增深对PathMeasure工具类的了解

5、最主要的是对自定义View有个比较清晰的思路认识

一、原理介绍

做这样一个进度效果,我们可以拆分如下步骤来实现:

1、从外部圆环开始测量绘制;

2、再加入刻度条效果;

3、再加入刻度随进度增加而增加效果;

4、增加自定义属性增加可定制性;

5、控件使用方法介绍

【zhangke3016 http://blog.csdn.net/zhangke3016

OK,有了这个思路,那我们开始吧:

1、测量绘制外部圆环

首先我们要开始绘制外部的圆环,这步很简单,主要是使用canvas的drawArc()方法,

  1. /*
  2. * @param oval 画弧线矩形区域
  3. * @param startAngle 开始的角度
  4. * @param sweepAngle 划过的角度
  5. * @param useCenter 如果为true 为实心圆弧
  6. * @param paint 画笔
  7. * /
  8. public void drawArc(RectF oval, float startAngle, float sweepAngle,boolean useCenter,Paint paint)

这个相对简单,主要是确定开始角度,并不断增加绘制划过角度,圆弧就出现在界面中了,这里需要注意的是RectF oval的大小确定:

在确定RectF oval之前,我们要先测量确定当前控件的宽高,根据当前控件的宽高来确定oval的合适大小。

测量当前控件的大小一般我们在onmeasure()方法中处理,resolveMeasured()方法传递两个参数,第一各参数为widthMeasureSpec或者heightMeasureSpec,第二个参数为期望值也就是默认值。

  1. @Override
  2. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  3. setMeasuredDimension(resolveMeasured(widthMeasureSpec, nDesired), resolveMeasured(heightMeasureSpec, nDesired));
  4. }
  5. /**
  6. *
  7. * @param measureSpec
  8. * @param desired
  9. * @return
  10. */
  11. private int resolveMeasured(int measureSpec, int desired)
  12. {
  13. int result = 0;
  14. int specSize = MeasureSpec.getSize(measureSpec);
  15. switch (MeasureSpec.getMode(measureSpec)) {
  16. case MeasureSpec.UNSPECIFIED: //
  17. result = desired;
  18. break;
  19. case MeasureSpec.AT_MOST: //wrap-content
  20. result = Math.min(specSize, desired);
  21. break;
  22. case MeasureSpec.EXACTLY: //match-content
  23. default:
  24. result = specSize;
  25. }
  26. return result;
  27. }

在测量之后我们就可以来求oval的具体大小 ,我把这步操作放在了onSizeChanged方法中,求出最小一边的一半减去圆环的宽度,得到圆弧的半径,然后根据半径以控件中心为中心绘制我们所需要的矩形区域。

  1. radiu = (int) ((Math.min(getWidth(), getHeight()))/2-mPaint.getStrokeWidth());
  2. oval.left = getWidth()/2-radiu;
  3. oval.top = getHeight()/2-radiu;
  4. oval.right = getWidth()/2+radiu;
  5. oval.bottom = getHeight()/2+radiu;

这样下来,基本上就可以绘制比较理想的弧线了,在这里也把绘制中心文字也说下吧,主要通过getTextBounds()方法获取文字区域的宽高,然后在drawText()方法中将坐标进行适当偏移以使文字居中显示。

  1. String strProgressText = "";
  2. if(mOnProgressListener !=null){//如果不为空 则为接口返回的值
  3. strProgressText = mOnProgressListener.OnProgress(mMax, mProgress);
  4. }else{
  5. strProgressText = mProgress+"/"+mMax;
  6. }
  7. mTextPaint.getTextBounds(strProgressText, 0, strProgressText.length(), bounds);
  8. canvas.drawText(strProgressText, oval.centerX()-bounds.width()/2, oval.centerY()+bounds.height()/2, mTextPaint);

最后还有一个小点就是渐变色的绘制,用的SweepGradient,我们可以看下Shader的子类,shader类是很强大的,类似与圆形图片、渐变效果都可以用它来实现,这里就不过多展开了:

  1. SweepGradient sweepGradient = new SweepGradient(getWidth()/2, getHeight()/2, colors, null);
  2. p.setShader(sweepGradient);

到这里为止,我们的圆环已经绘制好了,包括中间的文字以及圆环的渐变效果都已经实现了,就是这样的:

2、加入刻度效果

  1. 接下来要加入刻度效果,实现思路是这样的,我先默认实现两个圆弧(注意这两个圆弧只是我们假定添加的,并不是真正加在控件中显示),然后获取相同角度,根据相对位置获取两个圆环上的点进行连线,将这两个点连起的刻度线封装成对象添加在集合中,最后在onDraw方法中遍历集合,进行绘制。
  1. oval2 = new RectF();//内环
  2. oval2.left = getWidth()/2-radiu/4f*3;
  3. oval2.top = getHeight()/2-radiu/4f*3;
  4. oval2.right = getWidth()/2+radiu/4f*3;
  5. oval2.bottom = getHeight()/2+radiu/4f*3;
  6. oval3 = new RectF();//外环
  7. oval3.left = getWidth()/2-radiu/8f*7;
  8. oval3.top = getHeight()/2-radiu/8f*7;
  9. oval3.right = getWidth()/2+radiu/8f*7;
  10. oval3.bottom = getHeight()/2+radiu/8f*7;
  11. //然后初始化数据
  12. /**
  13. * 初始化数据
  14. */
  15. private void initData() {
  16. mLinesList.clear();
  17. Path path = new Path();
  18. Path path1 = new Path();
  19. //从startAngle开始 绘制180角度
  20. path.addArc(oval2, mStartAngle, mGraduationSweepAngle);
  21. path1.addArc(oval3, mStartAngle, mGraduationSweepAngle);
  22. PathMeasure pm = new PathMeasure(path, false);
  23. float itemLength = pm.getLength()/(nGraduationCount-1);
  24. PathMeasure pm1 = new PathMeasure(path1, false);
  25. float[] pos = new float[2];
  26. float[] postemp = new float[2];
  27. for (int i = 0; i < nGraduationCount; i++) {
  28. pm.getPosTan(itemLength*i, pos , null );
  29. pm1.getPosTan(itemLength*i/pm.getLength()*pm1.getLength(), postemp , null);
  30. Line line = new Line();
  31. line.p1.x = pos[0];
  32. line.p1.y = pos[1];
  33. line.p2.x = postemp[0];
  34. line.p2.y = postemp[1];
  35. mLinesList.add(line);
  36. }
  37. }
  38. //ondraw方法:
  39. for (int i = 0; i < mLinesList.size(); i++) {
  40. Line line = mLinesList.get(i);
  41. canvas.drawLine(line.p1.x, line.p1.y, line.p2.x, line.p2.y, mRollPaint);
  42. }

这里用到了PathMeasure这个辅助工具,这里简单讲一下:

  1. public PathMeasure()
  2. //path:需要测量的path forceClosed:是否关闭path
  3. public PathMeasure(Path path, boolean forceClosed)
  4. //指定需要测量的path
  5. public void setPath(Path path, boolean forceClosed)
  6. //返回当前path的总长度。
  7. getLength()
  8. //返回值是boolean,如过path为空,则返回false 传入参数有三个:
  9. //distance:传入距离起点的距离。
  10. //pos[]:意思是position,分别对应点的x,y坐标
  11. //tan[]:获取切线值,不常用。
  12. public boolean getPosTan(float distance, float pos[], float tan[])
  13. //返回一个处理好的matrix,但是这个matrix是以左上角作为旋转点,所以需要将这个点移动到中心点。 其中一个参数flags,指这个martrix需要什么信息。flags的值有如下两个
  14. PathMeasure.POSITION_MATRIX_FLAG:位置信息
  15. pathMeasure.TANGENT_MATRIX_FLAG:切边信息,方位角信息
  16. public boolean getMatrix(float distance, Matrix matrix, int flags)
  17. //这个方法返回boolean,如果截取的长度为0则返回false,否则为true。参数如下
  18. //startD:起始距离
  19. //stopD:终点距离
  20. //dst:接收截取的path
  21. //startWithMoveTo:是否把截取的path,moveto到起始点。
  22. public boolean getSegment(float startD, float stopD, Path dst, boolean startWithMoveTo)

3、加入刻度随进度增加而增加效果,并增加进度变化回调方便操作

加入刻度随进度增加而增加,我们可以这样想,首先我总的刻度数是一定的,判断划过的角度占圆周的百分比,随之就可以得到划过刻度数占总刻度数的百分比,进而就求出划过的刻度数了。

  1. for (int i = 0; i < Math.round(mSweepAngle*nGraduationCount/360f); i++) {
  2. if(i<mLinesList.size()){
  3. Line line = mLinesList.get(i);
  4. canvas.drawLine(line.p1.x, line.p1.y, line.p2.x, line.p2.y, mRollDrawPaint);
  5. }
  6. }

将划过的刻度数用画笔再绘制一次,随进度增加的刻度效果就出现啦!

  1. /**
  2. * 设置进度监听
  3. * @param mOnProgressListener
  4. */
  5. public void setOnProgressListener(OnProgressListener mOnProgressListener) {
  6. this.mOnProgressListener = mOnProgressListener;
  7. }
  8. /**
  9. * 用于外部判断当前进度状态
  10. */
  11. interface OnProgressListener{
  12. /**
  13. * 返回中间部分文字内容
  14. * @param max
  15. * @param progress
  16. * @return
  17. */
  18. String OnProgress(int max,int progress);
  19. }

设置回调监听,这样在每次进度变化的时候,可以随意变化中间部分文字显示的内容。

4、增加自定义属性增加可定制性

attrs.xml:

  1. <declare-styleable name="LoadingStyle">
  2. <attr name="textSize" format="dimension|reference"/><!-- 字体大小 -->
  3. <attr name="textColor" format="color|reference"/><!-- 字体颜色 -->
  4. <attr name="strokeWidth" format="dimension|reference"/><!-- 圆环大小 -->
  5. <attr name="isShowGraduationBackground" format="boolean"/><!-- 是否显示背景刻度 -->
  6. <attr name="isShowOutRoll" format="boolean"/><!-- 是否显示外部进度框 -->
  7. <attr name="startAngle" format="integer|reference"/><!-- 开始的角度 -->
  8. <attr name="max" format="integer|reference"/><!-- 最大值 -->
  9. <attr name="progress" format="integer|reference"/><!-- 默认进度值 -->
  10. <attr name="graduationBackgroundColor" format="color|reference"/><!-- 刻度的背景颜色 -->
  11. <attr name="graduationWidth" format="dimension|reference"/><!-- 刻度的宽度 -->
  12. <attr name="graduationCount" format="integer|reference"/><!-- 刻度的个数 -->
  13. </declare-styleable>

layout文件:

  1. xmlns:app="http://schemas.android.com/apk/res-auto"<!--设置命名空间 -->
  2. <com.mrzk.circleloadinglibrary.CircleLoadingView
  3. android:id="@+id/lv_loading"
  4. android:layout_width="250dp"
  5. android:layout_height="250dp"
  6. android:layout_centerInParent="true"
  7. app:textSize="35sp"
  8. app:textColor="#f60"
  9. app:strokeWidth="10dp"
  10. app:isShowGraduationBackground="true"
  11. app:startAngle="0"
  12. app:max="300"
  13. app:progress="100"
  14. app:graduationBackgroundColor="#ccc"
  15. app:graduationWidth="5dp"
  16. app:graduationCount="10"
  17. app:isShowOutRoll="false"
  18. />

获取自定义属性值:

  1. TypedArray typedArray = getResources().obtainAttributes(attrs, R.styleable.LoadingStyle);
  2. mTextSize = (int) typedArray.getDimension(R.styleable.LoadingStyle_textSize, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 20, getResources().getDisplayMetrics()));
  3. mStrokeWidth = (int) typedArray.getDimension(R.styleable.LoadingStyle_strokeWidth, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5, getResources().getDisplayMetrics()));
  4. mGraduationWidth = (int) typedArray.getDimension(R.styleable.LoadingStyle_graduationWidth, mStrokeWidth/2);
  5. mTextColor = (int) typedArray.getColor(R.styleable.LoadingStyle_textColor, Color.BLACK);
  6. mGraduationBackgroundColor = (int) typedArray.getColor(R.styleable.LoadingStyle_graduationBackgroundColor, Color.BLACK);
  7. mStartAngle = (int) typedArray.getInt(R.styleable.LoadingStyle_startAngle, 180);
  8. mMax = (int) typedArray.getInt(R.styleable.LoadingStyle_max, 0);
  9. mProgress = (int) typedArray.getInt(R.styleable.LoadingStyle_progress, 0);
  10. nGraduationCount = (int) typedArray.getInt(R.styleable.LoadingStyle_graduationCount, 35);
  11. isShowGraduationBackground = typedArray.getBoolean(R.styleable.LoadingStyle_isShowGraduationBackground, true);
  12. isShowOutRoll = typedArray.getBoolean(R.styleable.LoadingStyle_isShowOutRoll, true);
  13. typedArray.recycle();

5、使用方法

  1. int[] colors = {0xFFE5BD7D, 0xFFFAAA64,
  2. 0xFFFFFFFF, 0xFF6AE2FD,
  3. 0xFF8CD0E5, 0xFFA3CBCB,
  4. 0xFFBDC7B3, 0xFFD1C299, 0xFFE5BD7D};
  5. lv_loading.setTextColor(Color.BLACK);//设置中心文字颜色
  6. lv_loading.setMax(500);//设置最大进度
  7. lv_loading.setShowGraduationBackgroundEnable(true);//是否显示刻度背景
  8. lv_loading.setGraduationBackgroundColor(Color.GRAY);//刻度的背景颜色
  9. lv_loading.setStartAngle(180);//设置开始旋转角度
  10. lv_loading.setGraduationCount(10);//设置刻度数
  11. lv_loading.setGraduationWidth(5);//设置刻度的宽度
  12. lv_loading.setOutColors(colors);//设置外部圆环颜色
  13. lv_loading.setInnerGraduationColors(colors);//设置内部刻度进度颜色
  14. lv_loading.setTextSize(35);//设置内部文字字体大小
  15. lv_loading.setShowOutRollEnable(false);//设置是否显示外部进度框
  16. lv_loading.setOnProgressListener(new OnProgressListener() {
  17. @Override
  18. public String OnProgress(int max, int progress) {
  19. return progress*100f/max+"%";
  20. }
  21. });

二、源码附上

  1. /**
  2. * 进度视图
  3. * @author zhang
  4. *
  5. */
  6. public class CircleLoadingView extends View{
  7. /** 圆环的画笔 */
  8. private Paint mPaint;
  9. /** 文字的画笔 */
  10. private Paint mTextPaint;
  11. /** 刻度的画笔 */
  12. private Paint mRollPaint;
  13. //进度刻度的画笔
  14. private Paint mRollDrawPaint;
  15. /** 圆环的宽度 */
  16. private int mStrokeWidth = 0;
  17. /** 字体的大小 */
  18. private int mTextSize = 0;
  19. /** 字体的颜色 */
  20. private int mTextColor = 0;
  21. /** 圆环所在区域 */
  22. private RectF oval;
  23. private Rect bounds;//获取文字的宽高 使文字居中
  24. private float mStartAngle = 180;//开始的角度
  25. private float mSweepAngle = 0;//划过的角度
  26. /** 刻度的背景色 */
  27. private int mGraduationBackgroundColor = Color.BLACK;
  28. /** 刻度的宽度 */
  29. private int mGraduationWidth = 0;
  30. private float mGraduationSweepAngle = 359.9f;//刻度划过的角度 如果为360度 获取刻度会默认从右边划过
  31. private int mMax = 0;//设置的最大值
  32. private int mProgress = 0;//设置的进度
  33. //分段颜色 外环
  34. private int[] OUT_SECTION_COLORS = {
  35. 0xFFE5BD7D, 0xFFFAAA64,
  36. 0xFFFFFFFF, 0xFF6AE2FD,
  37. 0xFF8CD0E5, 0xFFA3CBCB,
  38. 0xFFBDC7B3, 0xFFD1C299,
  39. 0xFFE5BD7D};
  40. //内部刻度
  41. private int[] INNER_SECTION_COLORS = {
  42. 0xFFE5BD7D, 0xFFFAAA64,
  43. 0xFFFFFFFF, 0xFF6AE2FD,
  44. 0xFF8CD0E5, 0xFFA3CBCB,
  45. 0xFFBDC7B3, 0xFFD1C299,
  46. 0xFFE5BD7D};
  47. /** 宽高的默认值 */
  48. private int nDesired = 0;
  49. private RectF oval2;//临时的内圆
  50. private RectF oval3;//临时的外圆
  51. /** 刻度的个数 */
  52. private int nGraduationCount = 35;
  53. /** 所有线的集合 */
  54. private List<Line> mLinesList = new ArrayList<LoadingView.Line>();
  55. /** 进度监听器 */
  56. private OnProgressListener mOnProgressListener;
  57. /**
  58. * 是否显示进度条的背景 默认为
  59. * @see #setShowGraduationBackgroundEnable(boolean)
  60. * */
  61. private boolean isShowGraduationBackground = true;
  62. /**
  63. * 是否显示外部进度框
  64. * @see #setShowOutRollEnable(boolean)
  65. * */
  66. private boolean isShowOutRoll = true;
  67. public CircleLoadingView(Context context) {
  68. this(context,null);
  69. }
  70. public CircleLoadingView(Context context, AttributeSet attrs) {
  71. this(context, attrs,0);
  72. }
  73. public CircleLoadingView(Context context, AttributeSet attrs, int defStyle) {
  74. super(context, attrs, defStyle);
  75. TypedArray typedArray = getResources().obtainAttributes(attrs, R.styleable.LoadingStyle);
  76. mTextSize = (int) typedArray.getDimension(R.styleable.LoadingStyle_textSize, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 20, getResources().getDisplayMetrics()));
  77. mStrokeWidth = (int) typedArray.getDimension(R.styleable.LoadingStyle_strokeWidth, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5, getResources().getDisplayMetrics()));
  78. mGraduationWidth = (int) typedArray.getDimension(R.styleable.LoadingStyle_graduationWidth, mStrokeWidth/2);
  79. mTextColor = (int) typedArray.getColor(R.styleable.LoadingStyle_textColor, Color.BLACK);
  80. mGraduationBackgroundColor = (int) typedArray.getColor(R.styleable.LoadingStyle_graduationBackgroundColor, Color.BLACK);
  81. mStartAngle = (int) typedArray.getInt(R.styleable.LoadingStyle_startAngle, 180);
  82. mMax = (int) typedArray.getInt(R.styleable.LoadingStyle_max, 0);
  83. mProgress = (int) typedArray.getInt(R.styleable.LoadingStyle_progress, 0);
  84. nGraduationCount = (int) typedArray.getInt(R.styleable.LoadingStyle_graduationCount, 35);
  85. isShowGraduationBackground = typedArray.getBoolean(R.styleable.LoadingStyle_isShowGraduationBackground, true);
  86. isShowOutRoll = typedArray.getBoolean(R.styleable.LoadingStyle_isShowOutRoll, true);
  87. typedArray.recycle();
  88. init();
  89. }
  90. /**
  91. * 设置进度监听
  92. * @param mOnProgressListener
  93. */
  94. public void setOnProgressListener(OnProgressListener mOnProgressListener) {
  95. this.mOnProgressListener = mOnProgressListener;
  96. }
  97. private void init() {
  98. mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);//消除锯齿
  99. mPaint.setStyle(Paint.Style.STROKE);
  100. mPaint.setColor(0xFF0099CC);
  101. mPaint.setStrokeWidth(mStrokeWidth);
  102. mPaint.setStrokeCap(Paint.Cap.ROUND);
  103. mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  104. mPaint.setStyle(Paint.Style.STROKE);
  105. mTextPaint.setColor(mTextColor);
  106. mTextPaint.setFakeBoldText(true);//设置字体加粗
  107. mTextPaint.setTextSize(mTextSize);
  108. mRollPaint = new Paint(mPaint);
  109. mRollPaint.setColor(mGraduationBackgroundColor);
  110. // mRollPaint.setStrokeWidth(mStrokeWidth/2);
  111. mRollPaint.setStrokeWidth(mGraduationWidth);
  112. mRollDrawPaint = new Paint(mPaint);
  113. mRollDrawPaint.setStrokeWidth(mGraduationWidth);
  114. oval = new RectF();
  115. bounds = new Rect();
  116. nDesired = dip2px(60);
  117. }
  118. /**
  119. * 初始化数据
  120. */
  121. private void initData() {
  122. mLinesList.clear();
  123. Path path = new Path();
  124. Path path1 = new Path();
  125. //从startAngle开始 绘制180角度
  126. path.addArc(oval2, mStartAngle, mGraduationSweepAngle);
  127. path1.addArc(oval3, mStartAngle, mGraduationSweepAngle);
  128. PathMeasure pm = new PathMeasure(path, false);
  129. float itemLength = pm.getLength()/(nGraduationCount-1);
  130. PathMeasure pm1 = new PathMeasure(path1, false);
  131. float[] pos = new float[2];
  132. float[] postemp = new float[2];
  133. for (int i = 0; i < nGraduationCount; i++) {
  134. pm.getPosTan(itemLength*i, pos , null );
  135. pm1.getPosTan(itemLength*i/pm.getLength()*pm1.getLength(), postemp , null);
  136. Line line = new Line();
  137. line.p1.x = pos[0];
  138. line.p1.y = pos[1];
  139. line.p2.x = postemp[0];
  140. line.p2.y = postemp[1];
  141. mLinesList.add(line);
  142. }
  143. }
  144. public int dip2px(int dip){
  145. return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, getResources().getDisplayMetrics());
  146. }
  147. @Override
  148. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  149. setMeasuredDimension(resolveMeasured(widthMeasureSpec, nDesired), resolveMeasured(heightMeasureSpec, nDesired));
  150. }
  151. /**
  152. * 设置渐变颜色
  153. */
  154. private void setSweepShader(int[] colors,Paint p) {
  155. SweepGradient sweepGradient = new SweepGradient(getWidth()/2, getHeight()/2, colors, null);
  156. p.setShader(sweepGradient);
  157. }
  158. /** 设置层叠颜色 */
  159. public void setOutColors(int[] colors){
  160. OUT_SECTION_COLORS = colors;
  161. setSweepShader(OUT_SECTION_COLORS,mPaint);
  162. }
  163. /** 设置层叠颜色 */
  164. public void setInnerGraduationColors(int[] colors){
  165. INNER_SECTION_COLORS = colors;
  166. setSweepShader(INNER_SECTION_COLORS,mRollDrawPaint);
  167. }
  168. /**
  169. *
  170. * @param measureSpec
  171. * @param desired
  172. * @return
  173. */
  174. private int resolveMeasured(int measureSpec, int desired)
  175. {
  176. int result = 0;
  177. int specSize = MeasureSpec.getSize(measureSpec);
  178. switch (MeasureSpec.getMode(measureSpec)) {
  179. case MeasureSpec.UNSPECIFIED: //
  180. result = desired;
  181. break;
  182. case MeasureSpec.AT_MOST: //wrap
  183. result = Math.min(specSize, desired);
  184. break;
  185. case MeasureSpec.EXACTLY: //match
  186. default:
  187. result = specSize;
  188. }
  189. return result;
  190. }
  191. @Override
  192. protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  193. super.onSizeChanged(w, h, oldw, oldh);
  194. int radiu = 0;
  195. if(oval.bottom<=0){
  196. radiu = (int) ((Math.min(getWidth(), getHeight()))/2-mPaint.getStrokeWidth());
  197. oval.left = getWidth()/2-radiu;
  198. oval.top = getHeight()/2-radiu;
  199. oval.right = getWidth()/2+radiu;
  200. oval.bottom = getHeight()/2+radiu;
  201. oval2 = new RectF();
  202. oval2.left = getWidth()/2-radiu/4f*3;
  203. oval2.top = getHeight()/2-radiu/4f*3;
  204. oval2.right = getWidth()/2+radiu/4f*3;
  205. oval2.bottom = getHeight()/2+radiu/4f*3;
  206. oval3 = new RectF();
  207. oval3.left = getWidth()/2-radiu/8f*7;
  208. oval3.top = getHeight()/2-radiu/8f*7;
  209. oval3.right = getWidth()/2+radiu/8f*7;
  210. oval3.bottom = getHeight()/2+radiu/8f*7;
  211. }
  212. //初始化数据
  213. initData();
  214. //设置渐变色
  215. setSweepShader(OUT_SECTION_COLORS,mPaint);
  216. setSweepShader(INNER_SECTION_COLORS,mRollDrawPaint);
  217. }
  218. @Override
  219. protected void onDraw(Canvas canvas) {
  220. if(isShowOutRoll){
  221. canvas.drawArc(oval, mStartAngle, mSweepAngle, false, mPaint);
  222. }else{
  223. canvas.drawArc(oval, mStartAngle, 360, false, mPaint);
  224. }
  225. if(isShowGraduationBackground){
  226. for (int i = 0; i < mLinesList.size(); i++) {
  227. Line line = mLinesList.get(i);
  228. canvas.drawLine(line.p1.x, line.p1.y, line.p2.x, line.p2.y, mRollPaint);
  229. }
  230. }
  231. // int degree = (int) (Math.round(mGraduationSweepAngle)/ITEMCOUNT);
  232. // for (int i = 0; i < Math.round(mSweepAngle/degree); i++) {
  233. for (int i = 0; i < Math.round(mSweepAngle*nGraduationCount/360f); i++) {
  234. if(i<mLinesList.size()){
  235. Line line = mLinesList.get(i);
  236. canvas.drawLine(line.p1.x, line.p1.y, line.p2.x, line.p2.y, mRollDrawPaint);
  237. }
  238. }
  239. String strProgressText = "";
  240. if(mOnProgressListener !=null){//如果不为空 则为接口返回的值
  241. strProgressText = mOnProgressListener.OnProgress(mMax, mProgress);
  242. }else{
  243. strProgressText = mProgress+"/"+mMax;
  244. }
  245. mTextPaint.getTextBounds(strProgressText, 0, strProgressText.length(), bounds);
  246. canvas.drawText(strProgressText, oval.centerX()-bounds.width()/2, oval.centerY()+bounds.height()/2, mTextPaint);
  247. }
  248. /**
  249. * 设置是否显示外部进度条
  250. * @param isShowOutRoll
  251. */
  252. public void setShowOutRollEnable(boolean isShowOutRoll){
  253. this.isShowOutRoll = isShowOutRoll;
  254. }
  255. /**
  256. * 设置是否显示进度条的背景
  257. * @param isShowGraduationBackground
  258. */
  259. public void setShowGraduationBackgroundEnable(boolean isShowGraduationBackground){
  260. this.isShowGraduationBackground = isShowGraduationBackground;
  261. }
  262. /**
  263. * 设置显示进度数量
  264. * @param nGraduationCount
  265. */
  266. public void setGraduationCount(int nGraduationCount){
  267. this.nGraduationCount = nGraduationCount;
  268. }
  269. /**
  270. * 设置进度的背景颜色
  271. * @param mGraduationBackgroundColor
  272. */
  273. public void setGraduationBackgroundColor(int mGraduationBackgroundColor){
  274. this.mGraduationBackgroundColor = mGraduationBackgroundColor;
  275. mRollPaint.setColor(mGraduationBackgroundColor);
  276. }
  277. /**
  278. * 设置刻度的宽度
  279. * @param mGraduationWidth
  280. */
  281. public void setGraduationWidth(int mGraduationWidth){
  282. this.mGraduationWidth = mGraduationWidth;
  283. mRollPaint.setStrokeWidth(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, mGraduationWidth, getResources().getDisplayMetrics()));
  284. mRollDrawPaint.setStrokeWidth(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, mGraduationWidth, getResources().getDisplayMetrics()));
  285. }
  286. /**
  287. * 设置最大进度值
  288. * @param max
  289. */
  290. public void setMax(int max){
  291. this.mMax = max;
  292. }
  293. /**
  294. * 设置进度
  295. * @param progress
  296. */
  297. public void setProgress(int progress){
  298. this.mProgress = progress;
  299. if(mMax==0){
  300. throw new IllegalArgumentException("Max不能为0!");
  301. }
  302. mSweepAngle = 360f*mProgress/mMax;
  303. postInvalidate();
  304. }
  305. /**
  306. * 设置开始的角度 可以控制开始的位置 默认为180 即从左边开始
  307. * @param mStartAngle
  308. */
  309. public void setStartAngle(float mStartAngle){
  310. this.mStartAngle = mStartAngle;
  311. }
  312. /**
  313. * 设置字体颜色
  314. * @param mTextColor
  315. */
  316. public void setTextColor(int mTextColor){
  317. this.mTextColor = mTextColor;
  318. mTextPaint.setColor(mTextColor);
  319. }
  320. /**
  321. * 设置字体大小
  322. * @param mTextSize
  323. */
  324. public void setTextSize(int mTextSize){
  325. this.mTextSize = mTextSize;
  326. mTextPaint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, mTextSize, getResources().getDisplayMetrics()));
  327. }
  328. /**
  329. * 用于外部判断当前进度状态
  330. */
  331. interface OnProgressListener{
  332. /**
  333. * 返回中间部分文字内容
  334. * @param max
  335. * @param progress
  336. * @return
  337. */
  338. String OnProgress(int max,int progress);
  339. }
  340. /**
  341. * 刻度对象
  342. */
  343. class Line{
  344. PointF p1 = new PointF();
  345. PointF p2 = new PointF();
  346. }
  347. }

从一个简洁的进度刻度绘制中了解自定义View的思路流程的更多相关文章

  1. Dialog详解(包括进度条、PopupWindow、自定义view、自定义样式的对话框)

    Dialog详解(包括进度条.PopupWindow.自定义view.自定义样式的对话框)   Android中提供了多种对话框,在实际应用中我们可能会需要修改这些已有的对话框.本实例就是从实际出发, ...

  2. Android自定义View研究--View中的原点坐标和XML中布局自定义View时View触摸原点问题

    这里只做个汇总~.~独一无二 文章出处:http://blog.csdn.net/djy1992/article/details/9715047 Android自定义View研究--View中的原点坐 ...

  3. iOS 在UITableViewCell中加入自定义view时view的frame设定注意

    由于需要重用同一个布局,于是在cellForRowAtIndexPath中把自定义view加在了cell上,我是这样设定view的frame的 var screenFrame = UIScreen.m ...

  4. Android中使用自定义View实现下载进度的显示

    一般有下载功能的应用都会有这样一个场景,需要一个图标来标识不同的状态.之前在公司的项目中写过一个,今天抽空来整理一下. 一般下载都会有这么几种状态:未开始.等待.正在下载.下载结束,当然有时候会有下载 ...

  5. iOS中 xib自定义View在storyboard中的使用

    1,创建UIView 的SubClass 命名为MyView 2, new一个名为MyView的xib p1 3,配置xib的属性 p2 4,为View 添加背景色,添加一个按钮并定制按钮约束,这里我 ...

  6. Android中实现自定义View组件并使其能跟随鼠标移动

    场景 实现效果如下 注: 博客: https://blog.csdn.net/badao_liumang_qizhi 关注公众号 霸道的程序猿 获取编程相关电子书.教程推送与免费下载. 实现 新建An ...

  7. iOS开发小技巧--获取自定义的BarButtonItem中的自定义View的方法(customView)

    如果BarButtonItem是通过[[UIBarButtonItem alloc] initWithCustomView:(nonnull UIView *)]方法设置的.某些情况下需要修改BarB ...

  8. 我的Android进阶之旅------>Android自定义View实现带数字的进度条(NumberProgressBar)

    今天在Github上面看到一个来自于 daimajia所写的关于Android自定义View实现带数字的进度条(NumberProgressBar)的精彩案例,在这里分享给大家一起来学习学习!同时感谢 ...

  9. 自定义View实战--实现一个清新美观的加载按钮

    本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 在 Dribble 上偶然看到了一组交互如下: 当时在心里问自己能不能做,答案肯定是能做的,不过我比较懒,觉得中间那个伸缩变化要编写 ...

随机推荐

  1. bzoj 1925: [Sdoi2010]地精部落

    Description 传说很久以前,大地上居住着一种神秘的生物:地精. 地精喜欢住在连绵不绝的山脉中.具体地说,一座长度为 N 的山脉 H可分 为从左到右的 N 段,每段有一个独一无二的高度 Hi, ...

  2. NOIWC颓废记

    NOIWC大概就干了3件事情:吃.睡.浪. 吃: 目测绍兴一中的饭比二中的好吃多了,每天都有挺多的肉菜,还有一些甜品,而且是自助,不错的,但是一个不好的是排队时间太长了,于是我这么珍惜时间急着回宿舍的 ...

  3. 在Cisco Catalyst 3750端口做策略限速 QOS

    今天任务是在3750上限制端口的速率,本来以为是很简单的事,speed命令搞定,敲进去才知道speed命令只能叫端口速率改成10M或100M,也就是说只能起到端口高低速率的切换功能,不能自定义速率,后 ...

  4. postgresql 安装使用

    www.postgresql.org去下载你需要的版本,我下载的9.6.8 安装过程中会让你输入一次密码,其余的默认就ok 默认超级用户名postgres 打开 pgadmin4,我们将语言改为中文会 ...

  5. [ Java学习基础 ] Java的抽象类与接口

    一.抽象类 1. 抽象类 Java语言提供了两种类:一种是具体类:另一种是抽象子类. 2. 抽象类概念: 在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的 ...

  6. 利用生产者消费者模型和MQ模型写一个自己的日志系统-并发设计里一定会用到的手段

    一:前言 写这个程序主要是用来理解生产者消费者模型,以及通过这个Demo来理解Redis的单线程取原子任务是怎么实现的和巩固一下并发相关的知识:这个虽然是个Demo,但是只要稍加改下Appender部 ...

  7. 使用CSS让多出来的字变为省略号

    <style> .text1 { width:200px; overflow:hidden; text-overflow:ellipsis; -o-text-overflow:ellips ...

  8. JS基础(二)

    一.JS中的循环结构 循环结构的执行步骤 1.声明循环变量: 2.判断循环条件: 3.执行循环体操作: 4.更新循环变量 然后,循环执行2-4,直到条件不成立时,跳出循环. while循环()中的表达 ...

  9. easyui datagrid属性和方法

    本文可以当做api来使用 使用方法(Usage Example) 从现有的表单元素创建数据表格,定义在html中的行,列和数据. <table class="easyui-datagr ...

  10. SQL Server 2016 非域Aways On环境搭建

    一.优点 aways on的优点,a. 构建主从数据库,分摊单点数据库压力.b.可以减少数据同步时间,提升用户体验.c.可以实现高可用,自动平滑切换. 二.缺点 及时同步最多只能提交3台,及时同步会导 ...