博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android自定义控件--仿安全卫士中的一键加速
阅读量:4290 次
发布时间:2019-05-27

本文共 3791 字,大约阅读时间需要 12 分钟。

2016-10-13 a10615
鸿洋
鸿洋

hongyangAndroid

你好,欢迎关注鸿洋的公众号,每天为您推送高质量文章,让你每天都能涨知识。点击历史消息,查看所有已推送的文章,喜欢可以置顶本公众号。此外,本公众号支持投稿,如果你有原创的文章,希望通过本公众号发布,欢迎投稿。

本文由a10615投稿。

a10615的博客地址:

http://blog.csdn.net/a10615


1

行动由来

在开发交流群中,一童鞋说要实现这个进度条,但在网上没有找到开源项目。 

看到这个图片,很熟悉吧?有木有想点它的冲动?觉得有点意思,可以研究一下,再说也有一段时间没写自定义控件了,正好复习复习(说实话,一段时间没写,思路有,但就是不知道从哪开始)。昨天花了一天的时间才把它搞定,先看效果图:

3种显示模式: 

模拟进度动画效果: 


说说它的特点:

  1. 显示模式有三种可选。如上展示;

  2. 全显在控件范围内。控件宽高不一时,会以小的为边界;

  3. 所有颜色都可自定义;

  4. 百分比、单位、按钮的字体大小都可自定义;

  5. 单位和按钮的文字可自定义;

  6. 单位与百分比的对齐方式可选。详见下面的“绘制单位”;

  7. 按钮与百分比垂直间距可调;

  8. 按钮可设置点击事件(好像是废话,不然为什么叫按钮);

  9. 可在子线程中直接更新进度。

2

设计剖析

看到这个图,首先可以确定,它包括四个部分:

  1. 最外层的圆点

  2. 百分比

  3. 单位

  4. 按钮

当然先得知道自定义控件的步骤,参考了这位牛人的博文(http://blog.csdn.net/xiaanming/article/details/10298163)。

先总结下自定义控件的步骤:

  1. 分析控件的每个部分,设定其属性。在res/values/attrs.xml中定义好;

  2. 实现基本的三个构造方法,并在构造方法中获取属性值或默认值;

  3. 重写onDraw(Canvas canvas)方法,在此方法中绘制界面;

  4. 【可选】处理触摸事件;

  5. 在布局文件中使用。在基布局中别忘了添加命名空间(如:xmlns:zjun=”http://schemas.android.com/apk/res-auto”),这样才能使用自定义属性。

然后逐个分析。。。

1、绘制最外层的圆点

最外层的圆点,看上就很多,一圈下来感觉有100个,它当前百分比是65,数了下,确实是65个白点,灰点就不用数了,肯定就是剩下的35个。这样,一圈上点的总个数确定下来,就是100个。

点与点之间的空白距离,看上去就是一个点的空间。一圈下来,就是200个连续圆点。圆点应该内切控件边界。 


  
它们的关系是: R0 = R1 + r; ······ ① 


其中R0控件大小(min(getWidth(), getHeight()))的一半;R1就是圆点的圆心组成的圆轨迹的半径;r是圆点半径;

另外200个连续圆点在以R1为半径的圆上,圆点与圆点都是外切关系。所以每个圆点所占的角度就是360°/200 = 1.8° 


  

它们的关系: 
sin(0.9°)=rR1 ······ ②

由①和②可知,r与R0的关系: 
r=sin(0.9°)sin(0.9°)+1R0

这样出来的效果,实际圆点间距离太大,最后把sin(0.9°)提高到sin(1°)。这样就比较饱满了。

求出了圆点半径,圆点圆心所在的位置也知道了。画圆点,一圈共100个,可以使用了canvas的旋转来绘制,每绘制一个圆点,就旋转3.6°(360°/100)。

至于进度,可以先画已完成进度,再画未完成进度。也可以先把未完成进度画完,把其当背景,再画已完成进度。 

我这选择了第一种方法,理由:为了不重复绘制,另一个就是防止已完成进度的颜色是带透明度的,这样叠加就不是想要的颜色了。

2、绘制百分比

百分比就是文字,直接用canvas.drawText()绘制就好了。

mPaint.setTextSize(percentTextSize);mPaint.setColor(percentTextColor);canvas.drawText(percent + "", centerX - textWidth / 2, baseline, mPaint);

绘制的时候,要找好开始绘制的位置,注意两个方向的对齐方法:

2.1 水平对齐

这个简单,先测量一下文字的宽度,左边起始位置就是centerX - textWidth/2。上面用textWidth是因为还要加上单位字体的宽度。

mPaint.setTextSize(percentTextSize);float percentTextWidth = mPaint.measureText(percent + "");

2.2 垂直对齐

baseline是y方向的起始位置,最初设置的是centerY + percentTextSize/2。在默认显示模式下,结果是酱紫: 


擦,这哪是垂直对齐啊。难道有万有引力? 
后来找到了原因

参考:http://blog.csdn.net/carrey1989/article/details/10399727)。

因为它的y坐标位置是baseline,而不是我们想象中的bottom。请看下图(ascent与参考的博客有点不一样,但不用怀疑我这的准确性,因为这图是直接通过自定义控件绘制出来的) 



说明:

  • 这些top、bottom、ascent、descent都可以直接从FontMetrics中获取,这些值都是以baseline为基准。

  • baseline就是canvas.drawText(text, x, y, paint)中的y。

  • center是ascent和descent的中间线,即(ascent + descent)/2。这里可以看出,center就是文字的中间线。

而FontMetrics在设置字体大小后可以获取到:

mPaint.setTextSize(textHeight);Paint.FontMetrics fm = mPaint.getFontMetrics();

所以要居中对齐,baseline比较准确的值是:

baseline = centerY + (fm.descent - fm.ascent)/2 - fm.descent;

这我理解了半天才弄明白。可以这么理解:当文字在正中间的时候,center线的y坐标值就是centerY,加上(fm.descent - fm.ascent)/2就到了descent线(要区分fm.descent的值),减去fm.descent就到了baseline线。

修正后的结果: 


另一个比较准确的值:

baseline = centerY + (fm.bottom- fm.top)/2 - fm.bottom;

3、绘制单位

绘制方式同百分比,他绘制的起始x坐标紧接着百分比的。

但,,,如果单位不是%,而是“分”,这样就不能与百分比的数字在底部对齐了。看需求吧,有的地方允许这样,但在强迫症的驱动下,又给自己整了一壶。

看看上面baseline的分析,特别是那张图。是不是发现字母、数字、汉字的底部对齐方式不太一样(里面的文字是有用途的,不是闹着玩的)。

所以添加了一个这样独特的属性:unitTextAlignMode,意为单位与百分比的对齐方式。有三种方式:

  1. DEFAULT:只要与baseline线底部对齐的都可以用这个,eg: %, a, b, …

  2. CN:中文,就用它

  3. EN:主要针对英文字母和字符中带尾巴的,eg: g, j, p, q, y, [, ], {, }, |, …

原理就是根据文字大小,进行了微调:

效果咋样?别急,客官,我们这里又展品,看完再选也不迟: 
百分比与单位字体大小一样: 


  

百分比字体比单位字体大: 


4、绘制按钮

  

主要是背景,仔细看,可以这么去绘制:先把背景分三部分,中间一个长方形,两边各一个半圆。长方形的长等于字体的宽度,高是字体高度的2倍。

绘制的时候,可通过Path来一次性绘制完毕:

5、处理按钮的点击事件

要处理按钮的点击事件,首先要判断触摸点是否在按钮中,跟绘制一样,分别判断是否在方形、左半圆、右半圆中。

方形好判断,左半圆和右半圆的判断,我们利用解析几何的办法:判断一个点(x, y)是否在圆( x0,y0, r)内,先把点的坐标减去圆心的坐标,这样相当于把坐标系的原点移动到了圆心,如果在圆中,必然满足: 
(x−x0)2+(y−y0)2<=r2

代码实现如下:

按钮的处理事件,在onTouchEvent()中进行拦截,这样能区别按钮被点击,和控件(除按钮区域)被点击

3

核心代码

最后看一下绘制的细节代码:

源码下载:

  • https://github.com/zjun615/ProgressBar


掘金是一个高质量的技术社区,从 RxJava 到 React Native,性能优化到优秀开源库,让你不错过 Android 开发的每一个技术干货。长按图片二维码识别或者各大应用市场搜索「掘金」,技术干货尽在掌握中。

如果你有好的文章想和大家分享,欢迎投稿,直接向我投递文章链接即可。

欢迎长按下图->识别图中二维码或者扫一扫关注我的公众号:

你可能感兴趣的文章
程序员每天早上早来10分钟的好处
查看>>
互联网30年,泡沫如梦,一个个泡沫和风口过后,会是什么样的结局
查看>>
升级centos 6.8 服务器的gcc
查看>>
API网关在微服务架构中的应用,这一篇就够了
查看>>
JVM发生内存溢出的8种原因、及解决办法
查看>>
SpringBoot2.0 基础案例(12):基于转账案例,演示事务管理操作
查看>>
高性能负载均衡:nginx搭建tomcat集群
查看>>
Spring切面中的正则表达式
查看>>
一直再说高并发,多少QPS才算高并发?
查看>>
Git恢复之前版本的两种方法reset、revert(图文详解)
查看>>
Maven打包的三种方式
查看>>
电商场景:并发扣库存,怎么保证不超卖又不影响并发性能
查看>>
分布式事务处理方式总结
查看>>
延迟队列有哪些实现方案?说说你的看法
查看>>
厉害了!我们老大半小时把我的springboot项目并发提升几倍
查看>>
Spring 中Bean 的生命周期
查看>>
为什么要用枚举实现单例模式(避免反射、序列化问题)
查看>>
微服务架构下的分布式限流方案思考
查看>>
全网最详细的一篇SpringCloud总结
查看>>
消息中间件中的有序消息,其实是排队但是不能插队
查看>>