文档章节

【安卓自定义控件系列】自绘控件打造界面超炫功能超强的圆形进度条

htq
 htq
发布于 2016/07/29 12:28
字数 2224
阅读 59
收藏 0

码上生花,ECharts 作品展示赛正式启动!>>>


在前面我们讲过了安卓自定义控件三种方式中的组合控件,现在我们来讲解一下通过自绘的方式来实现自定义控件,本博客将以自定义圆形进度条为例向大家讲解自定义控件的知识,首先来看一下效果图吧,这个是本人自定义圆形进度条demo工程的运行截图:



首先说一下自己这个自定义圆形进度条要达到的目标:

1能够支持设置进度条各种属性,如圆环的大小,颜色,进度条的大小,颜色,进度条的颜色支持设置三种颜色来达到渐变色的效果。

2圆形进度条的内部支持设置三层文本,即上层的标题,如上图的“您的等级超越全国”,中间层的进度值,如上图的“700”,下层的附带内容,如上图的“万的用户”

3支持设置三层文本的大小与颜色,如上图标题与底部文本为黑色,中间文本为红色

4支持进度条从任意位置开始显示,为何要支持该功能,是因为在不同的场合,进度条开始显示的位置一般是不同的,如在某些手机助手类下载App的应用中显示下载进度的时候都是从圆环的顶部开始,以顺时针为方向逐渐递增显示,本例的第三个小圆环即是模仿的该场合,但是因为截的动态图上传出错,只能上传几张图片,所以看的不是很清楚,而在某些计步器类的app中进度的绘制一般是从左下角开始显示,然后以顺时针为方向达到对称的位置,本例的最后一个大圆环即是模仿的该场合。

5支持设置部分圆弧,而不是整个圆,如本例的最后一个大圆环的进度条显示效果,因为在某些场合是不需要绘制整个圆的,如在模拟汽车速度表盘的场合。

6具备极强的自适应能力,即wrap_content参数要能够比较完美的适应用户输入的文本的长度。


在做这个自定义控件的工程时也遇到了一坑,不过都一一解决了,其中最难的就是最后一条,要求具备极强的自适应能力,即当我们在xml文件中指定该自定义控件的宽度与高度为wrap_content时如何完美的适应用户输入的文本,这个是关键,这涉及到安卓中控件的绘制过程的知识和一些绘图API的使用,主要是paint与draw这两个类的API,而本人完美解决wrap_content涉及到了paint类中某些不常用的API,因此说这也是难以解决的一个原因,你必须对paint类的API非常熟悉,即使是相对而言很少使用的。

因为本人已打算将该组件开源,上传到我的github上,大家可以到我的github上去fork我的代码,因此本博客不是对整个自定义圆形进度条做讲解,因为这没啥难度,另外网上很多这些方面的博客,本博客重点讲解如何解决极强的自适应能力,因此下面重点说一下如何解决wrap_content.


在贴出自己解决方案的代码之前,先给大家普及一下关于安卓中控件的绘制过程,因为你明白了这个过程才知道如何去解决wrap_content.

我们知道安卓中一个控件要显示在界面上要经过三个过程,即测量,布局与绘制,对应onMeasure,onLayout,onDraw这三个函数,很显然我们要解决的是测量过程,即当在xml中设置wrap_content的时候如何较准确的测量出我们自定义控件的大小,在安卓中一个控件的大小用MeasureSpec这个类来表示所,测量过程实际上可以说是得到该控件的MeasureSpec的过程。下面是一些关于MeasureSpec类的常识:

- MeasureSpec是通过将一个int(32)的数组成而成的,本质是一个int数,MeasureSpec由两部分组成:SepcMode 和 SpecSize 。其中SpecMode为MeasueSpec的高2位,SpecSize为MeasureSpec的低30位。SpecMode有3类:

 - UNSPECIFIED:父容器不对View有任何限制,要多大有多大,这种情况一般用于系统内容。在我们使用过程中,一般不考虑这种模式。
 - EXACTLY:父容器已经检测到了View所需要的精确大小,这个时候View的最终大小就是SpecSize所指定的大小。它对应的LayotParams中的match_parent 和具体的数值。
 - AT_MOST: 父容器指定了一个可用大小SpecSize,View的大小不能大于这个值,它对应于LayoutParams中的wrap_content。

一个子控件的MeasureSpec与父容器的MeasureSpec和自身的LayoutParams相关,由二者共同决定。一个子控件的onMeasure方法一般由其父容器所调用,代码如下:

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
    }
public static int getDefaultSize(int size, int measureSpec) {
        int result = size;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
        switch (specMode) {
        case MeasureSpec.UNSPECIFIED:
            result = size;
            break;
        case MeasureSpec.AT_MOST:
        case MeasureSpec.EXACTLY:
            result = specSize;
            break;
        }
        return result;
    }

从上面的代码可以看到AT_MOST和EXACTLY两种模式都会被设置成specSize。我们也知道AT_MOST对应于wrap_content,而EXACTLY对应于match_parent和具体数值情况。也就说默认情况下wrap_content和match_parent是具有相同的效果的,这也是为何说在我们通过继承自View类自绘的方式自定义控件的时候为何需要自己支持wrap_content的原因。

知道了原因我们就知道要解决的话则应该重写自定义控件的onMeasure函数,在该函数中为自定义控件设置一个默认的宽与高即可,但是为了做到极强的自适应,这个宽与高必须非常恰当,那么怎样才算恰当呢?这个就和自定义控件的功能相关,如本人自定义控件中要求支持三层文本显示,那么很显然所谓的恰当就是刚好能够容纳这三行文本中的最大长度的文本,因此此时解决思路就转换为了如何求自绘文本的长度,这个涉及到了Paint类中的一个API getTextBounds,通过该API就可以知道文本的长度,从而较好的支持wrap_content,下面是解决wrap_content的完整代码,注释很详细,大家应该可以看懂。

//必须重写该方法,否则在xml文件中定义warp_content与match_parent效果相同
	@Override
	 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

		 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		 int desiredWidth ;
		 int desiredHeight ;
		 
     //设置了文本,且属性为wrap_content,那么以输入的文本的长度为宽度
		 if (isSetToptitle) {
		 Rect rect = new Rect();  
		 titlePaint.getTextBounds(topTitle, 0, topTitle.length(), rect); 
//		 desiredWidth =(int) (rect.width()+4*progressWidth);
//		 desiredHeight = (int) (rect.width()+4*progressWidth);
		 desiredWidth =(int) (1.5*rect.width());
		 desiredHeight = (int) (1.5*rect.width());
		 }
		 else//没设置文本那么不可能设置wrap_content属性,事实上这些设置无效
		 {
			 desiredWidth =500;
			 desiredHeight = 500;
		 }
		    
		    int widthMode = MeasureSpec.getMode(widthMeasureSpec);
		    int widthSize = MeasureSpec.getSize(widthMeasureSpec);
		    int heightMode = MeasureSpec.getMode(heightMeasureSpec);
		    int heightSize = MeasureSpec.getSize(heightMeasureSpec);

		    int width;
		    int height;

		    //Measure Width
		    if (widthMode == MeasureSpec.EXACTLY) {
		        //Must be this size
		        width = widthSize;
		    } else if (widthMode == MeasureSpec.AT_MOST) {
		        //Can't be bigger than...
		        width = Math.min(desiredWidth, widthSize);
		    } else {
		        //Be whatever you want
		        width = desiredWidth;
		    }

		    //Measure Height
		    if (heightMode == MeasureSpec.EXACTLY) {
		        //Must be this size
		        height = heightSize;
		    } else if (heightMode == MeasureSpec.AT_MOST) {
		        //Can't be bigger than...
		        height = Math.min(desiredHeight, heightSize);
		    } else {
		        //Be whatever you want
		        height = desiredHeight;
		    }

		    //MUST CALL THIS
		    setMeasuredDimension(width, height);
		    
		    center = getWidth()/2; //该方法必须在onDraw或者onMeasure中调用,否则不起作用d
	        
			//圆环的半径 ,此处必须是progressWidth与circleWidth中较大的一个
		    //radius = (int) (center - progressWidth/2); 
			if(progressWidth>circleWidth)
			  radius=(int)(center-progressWidth/2);
			else
			  {radius=(int)(center-circleWidth/2);}
	        
	       
	        sweepGradient = new SweepGradient(0, 0, colors, null);
	        hideRect=new RectF(center - radius, center - radius, center  
	                + radius, center + radius); 

		}

至于其它属性因为很简单,无非就是在atrs文件中定义属性,在构造函数中通过TypedArray获取属性,重写onDraw函数,在该函数中处理这些获取到的属性而已,不涉及到很多原理上的思考,另外网上关于这方面的资料也很多,所以没贴出代码,大家如果感兴趣可以follow我的github账号,本人将上传该项目工程到我的github上,这个应该是目前功能最强大的自定义圆形进度条了,欢迎大家follow,star与fork。


我的github:https://github.com/HuTianQi

如果大家觉得不错记得小手一抖点个赞哦,欢迎大家关注我的博客账号,将会不定期为大家分享技术干货,福利多多哦!

本文转载自:http://blog.csdn.net/htq__/article/details/52054261

htq

htq

粉丝 20
博文 67
码字总数 1007
作品 3
武汉
私信 提问
加载中
请先登录后再评论。
25000字总结Android优秀的第三方框架、各种学习资料汇集

说明 以下内容博主很多也没有用过 分享出来大家一起学习 用到的时候 可以来查看 有问题可以沟通 希望大家不要污染了学习环境 如果觉得有帮助 请点赞 关注 收藏 一个礼貌的☺ 系统控件 TextVi...

吕氏春秋i
05/19
0
0
仿MIUI音量变化环形进度条实现

Android中使用环形进度条的业务场景其实蛮多的,比如下载文件的时候使用环形进度条,会给用户眼前一亮的感觉;再比如我大爱的MIUI系统,它的音量进度条就是使用环形进度条,尽显小米"为发烧而...

Jack_1900
2014/07/25
806
0
自定义控件及效果

Android 动画效果定值范围选择控件 实现固定值的范围选择, 并添加动态效果, 使用方便 项目需求讨论 - Android 自定义 Dialog 实现步骤及封装 根据实际项目需求出发。因为项目中的对话框要配合...

掘金官方
2018/01/09
0
0
Android酷炫动画效果

Android自定义View:一个精致的打钩小动画 一个精致的打钩动画。 Android 自定义水平进度条圆角进度 项目中实现进度条进度过程中显示圆角样式 ViewPager系列之 仿魅族应用的广告BannerView ...

掘金官方
2017/12/19
0
0
GitHub上受欢迎的Android UI Library

GitHub上受欢迎的Android UI Library 内容 抽屉菜单 ListView WebView SwitchButton 按钮 点赞按钮 进度条 TabLayout 图标 下拉刷新 ViewPager 图表(Chart) 菜单(Menu) 浮动菜单 对话框 空白...

贵公子
04/01
0
0

没有更多内容

加载失败,请刷新页面

加载更多

通过使用模块名称(字符串)来调用模块的功能 - Calling a function of a module by using its name (a string)

问题: What is the best way to go about calling a function given a string with the function's name in a Python program. 在Python程序中,给定带有函数名称的字符串的最佳方法是什么?......

javail
35分钟前
31
0
在JavaScript中生成随机字符串/字符 - Generate random string/characters in JavaScript

问题: I want a 5 character string composed of characters picked randomly from the set [a-zA-Z0-9] . 我想要一个由从[a-zA-Z0-9]随机挑选的字符组成的5个字符串。 What's the best wa......

fyin1314
今天
20
0
在GitHub上将图像添加到README.md - Add images to README.md on GitHub

问题: Recently I joined GitHub . 最近我加入了GitHub 。 I hosted some projects there. 我在那里举办了一些项目。 I need to include some images in my README File. 我需要在README文件......

技术盛宴
今天
17
0
R语言笔记:用R语言绘制条形图

学“统计学”的人对R语言应该不会太陌生,近十年来,随着大数据时代的到来,把统计学和数据分析,R语言都带火了。虽然我本人会用Python的matplotlib和pyecharts,百度的Echarts,微软的Excel...

tengyulong
今天
29
0
字符串中全半角的不同

全角空格 数字表示 12288 半角空格 数字表示 32 一般string类中去掉空格的一般操作都是半角空格 /** * 去除字符串中所包含的空格(包括:空格(全角,半角)、制表符、换页符等) * @param s *...

飞雪无痕
今天
15
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部