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

项目github地址: https://github.com/zhangke301...

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

1、自定义属性的使用。
2、shader的使用
3、自定义View中对onmeasure的处理
4、增深对PathMeasure工具类的了解
5、最主要的是对自定义View有个比较清晰的思路认识

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

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

  2. 再加入刻度条效果;

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

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

  5. 控件使用方法介绍

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

1、测量绘制外部圆环

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

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

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

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

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

@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {setMeasuredDimension(resolveMeasured(widthMeasureSpec, nDesired), resolveMeasured(heightMeasureSpec, nDesired));}/*** * @param measureSpec * @param desired * @return*/private int resolveMeasured(int measureSpec, int desired)  {  int result = 0;  int specSize = MeasureSpec.getSize(measureSpec);  switch (MeasureSpec.getMode(measureSpec)) {  case MeasureSpec.UNSPECIFIED: // result = desired;  break;  case MeasureSpec.AT_MOST:  //wrap-contentresult = Math.min(specSize, desired);  break;  case MeasureSpec.EXACTLY:  //match-contentdefault:  result = specSize;  }  return result;  } 

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

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

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

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

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

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

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

2、加入刻度效果

接下来要加入刻度效果,实现思路是这样的,我先默认实现两个圆弧(注意这两个圆弧只是我们假定添加的,并不是真正加在控件中显示),然后获取相同角度,根据相对位置获取两个圆环上的点进行连线,将这两个点连起的刻度线封装成对象添加在集合中,最后在onDraw方法中遍历集合,进行绘制。

oval2 = new RectF();//内环oval2.left = getWidth()/2-radiu/4f*3;oval2.top = getHeight()/2-radiu/4f*3;oval2.right = getWidth()/2+radiu/4f*3;oval2.bottom = getHeight()/2+radiu/4f*3;oval3 = new RectF();//外环oval3.left = getWidth()/2-radiu/8f*7;oval3.top = getHeight()/2-radiu/8f*7;oval3.right = getWidth()/2+radiu/8f*7;oval3.bottom = getHeight()/2+radiu/8f*7;//然后初始化数据/*** 初始化数据*/private void initData() {mLinesList.clear();Path path = new Path();Path path1 = new Path();//从startAngle开始 绘制180角度path.addArc(oval2, mStartAngle, mGraduationSweepAngle);path1.addArc(oval3, mStartAngle, mGraduationSweepAngle);PathMeasure pm = new PathMeasure(path, false);float itemLength = pm.getLength()/(nGraduationCount-1);PathMeasure pm1 = new PathMeasure(path1, false);float[] pos = new float[2];float[] postemp = new float[2];for (int i = 0; i < nGraduationCount; i++) {pm.getPosTan(itemLength*i, pos , null );pm1.getPosTan(itemLength*i/pm.getLength()*pm1.getLength(), postemp , null);Line line = new Line();line.p1.x = pos[0];line.p1.y = pos[1];line.p2.x = postemp[0];line.p2.y = postemp[1];mLinesList.add(line);}}//ondraw方法:
for (int i = 0; i < mLinesList.size(); i++) {Line line = mLinesList.get(i);canvas.drawLine(line.p1.x, line.p1.y, line.p2.x, line.p2.y, mRollPaint);}

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

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

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

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

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

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

/*** 设置进度监听* @param mOnProgressListener*/public void setOnProgressListener(OnProgressListener mOnProgressListener) {this.mOnProgressListener = mOnProgressListener;}/*** 用于外部判断当前进度状态*/interface OnProgressListener{/*** 返回中间部分文字内容* @param max* @param progress* @return  */String OnProgress(int max,int progress);}

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

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

attrs.xml:

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

layout文件:

 xmlns:app="http://schemas.android.com/apk/res-auto"<!--设置命名空间 --><com.mrzk.circleloadinglibrary.CircleLoadingViewandroid:id="@+id/lv_loading"android:layout_width="250dp"android:layout_height="250dp"android:layout_centerInParent="true" app:textSize="35sp"app:textColor="#f60"app:strokeWidth="10dp"app:isShowGraduationBackground="true"app:startAngle="0"app:max="300"app:progress="100"app:graduationBackgroundColor="#ccc"app:graduationWidth="5dp"app:graduationCount="10"app:isShowOutRoll="false"/>

获取自定义属性值:

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

5、使用方法

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

项目github地址: https://github.com/zhangke301...

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

  1. 从一个简洁的进度刻度绘制中了解自定义View的思路流程

    先看效果(原谅我的渣像素),进度的刻度.宽度.颜色可以随意设定: [项目github地址: https://github.com/zhangke3016/CircleLoading] 实现起来并不难, ...

  2. Android中自定义View的研究 -- 在XML中引用自定义View

    如果在一直使用SetContentView(new HellwView(this)觉得总是少了一点东西,少了什么了,失去了Android中使用XML定义组件的便携性,这种感觉让人很不爽,呵呵,在这节里 ...

  3. android 在xml文件中引用自定义View

    在xml中引用自定义view 方法一: [java] view plaincopy <com.test.copytext.CopyText android:layout_width=" ...

  4. android 自定义view xml ,Android实现在xml文件中引用自定义View的方法分析

    本文实例讲述了Android实现在xml文件中引用自定义View的方法.分享给大家供大家参考,具体如下: 在xml中引用自定义view 方法一: android:layout_width=" ...

  5. android自定义进度条_Android中的自定义进度栏

    android自定义进度条 Custom progress bar in android application gives it a personal touch. In this tutorial ...

  6. android canvas绘制圆角_Android自定义View撸一个渐变的温度指示器(TmepView)

    秦子帅明确目标,每天进步一点点..... 作者 |  andy 地址 |  blog.csdn.net/Andy_l1/article/details/82910061 1.概述 自定义View对需要 ...

  7. Android中的自定义view和自定义属性TypedArray的使用

    先总结下自定义View的步骤: 1.自定义View的属性 2.在View的构造方法中获得我们自定义的属性 [ 3.重写onMesure ] 4.重写onDraw 我把3用[]标出了,所以说3不一定是必 ...

  8. Android中的自定义View(一)

    文章收藏的好句子:成功从来不是一蹴而就,而是日积月累的努力叠加的结果. ps:本文是基于 Android API 31 来分析的,文章写的 demo 是用 AndroidStudio 工具来开发的. ...

  9. Android中的自定义View以及绘图工具

    1.1自定义view的简介 为什么要使用自定义view 在Android开发中有很多业务场景,原生的控件是无法满足应用,并且经常也会遇到一个UI在多处 重复使用情况,那么就需要通过自定义View的方式 ...

最新文章

  1. 行为模式之Strategy模式
  2. python中文对齐_Python中英文对齐终极解决方案
  3. 代理模式(为对象提供相同的接口)
  4. 如何做相册_腾讯相册管家再创相册管理软件新纪元
  5. 【转】TCP和UDP的区别
  6. python最简单的wsgi例子
  7. Kaggle新赛 | HuBMAP: 识别人体肾脏组织图像中的肾小球~总奖金 6 万美金
  8. 走近汇编理解与内核编程
  9. PSP3000破解原理——缓冲区溢出漏洞随谈
  10. 饿了么UI框架表单验证
  11. [附源码]Java计算机毕业设计SSM大学生志愿者管理系统
  12. python暂停命令_命令行-Python中的暂停
  13. Roaring64NavigableMap(Bitmap)简单使用
  14. Fabric Block区块结构解析
  15. 怎样使用菜单栏中的Apple图标在苹果Mac上强制退出应用程序?
  16. MTK 6761平台 android O bootloader启动之 Pre-loader - Lk
  17. wps在线预览接口_金山文档在线编辑 - 快速接入 - 《WPS开放平台技术文档》 - 书栈网 · BookStack...
  18. Flink入门教程(四)——窗口(二)
  19. linux安装matlab2016,ubuntu16.04 安装matlab2016b教程
  20. ASP.NET债务管理系统源码

热门文章

  1. C/S简易UI框架开发总结(2)
  2. windows64下安装MySQLdb连接数据库
  3. 21_resultMap和resultType总结
  4. Strom小实例,大小写转换
  5. 开发外包注意事项——iOS APP的开发
  6. LALR(1)语法分析生成器--xbytes
  7. Nginx虚拟目录(转)
  8. C# Thread开启线程几种方式
  9. 关于HBITMAP,CBITMAP,BITMAP的转换以及图像显示的一点归纳
  10. java调用 火眼臻睛,连接创新,发现未来,臻识科技受邀CCF-GAIR,论道浪潮之巅...