文档章节

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

htq
 htq
发布于 2016/07/29 12:28
字数 2224
阅读 39
收藏 0
点赞 0
评论 0


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

粉丝 19
博文 67
码字总数 1007
作品 3
武汉
自定义控件及效果

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

掘金官方 ⋅ 01/09 ⋅ 0

Android酷炫动画效果

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

掘金官方 ⋅ 2017/12/19 ⋅ 0

仿MIUI音量变化环形进度条实现

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

Jack_1900 ⋅ 2014/07/25 ⋅ 0

Android-UI控件

手摸手教你写 Slack 的 Loading 动画 四步实现: 画布旋转及线条变化动画(Canvas Rotate Line Change) 画布旋转动画(Canvas Rotate) 画布旋转圆圈变化动画(Canvas Rotate Circle Change...

掘金官方 ⋅ 01/12 ⋅ 0

欢迎进入Hensen_的博客目录(全站式导航)

Android基础 Java基础 Java基础——Java内存模型和垃圾回收机制 语法基础 语法基础——C语法基础 语法基础——C++语法基础 语法基础——Objective-C语法基础 语法基础——PHP语法基础 面试复...

qq_30379689 ⋅ 2016/09/23 ⋅ 0

Android UI框架(中)

唯美清新的switch按钮 优雅的switch状态按钮 通用的引导页和轮播器 仿微信图片选择器 快捷设置沉浸式状态栏 复杂表格布局 最全的开源集合 阻尼效果的界面 炫酷的纸飞机下拉刷新控件 仿ios水滴...

Moosphon ⋅ 2017/12/31 ⋅ 0

自定义view控件效果实现及实践

项目需求讨论 - Android 自定义 Dialog 实现步骤及封装 根据实际项目需求出发。因为项目中的对话框要配合整个项目的 UI 风格,所以进行自定义 Dialog 的实现步骤,及最后写封装类。来快速实现...

掘金官方 ⋅ 2017/12/11 ⋅ 0

安卓View

【Android 自定义 View 实战】之自定义项目通用的标题栏 CustomTitleBar 【Android 自定义 View 实战】之自定义项目通用的标题栏 CustomTitleBar 动手打造一个小巧且高效的线性图表组件——S...

掘金官方 ⋅ 01/05 ⋅ 0

Android系统源码分析团体项目BeesAndroid正式上线啦

嗨,BeesAndroid开源技术小组正式成立啦,Bees,即蜜蜂,取义分享、合作与奉献的意思,这也是BeesAndroid小组的宗旨,我们第一个团体项目BeesAndroid也于2018年3月6日同步上线,该项目的前 ...

郭孝星 ⋅ 03/08 ⋅ 0

RecycleView相关

Android ItemTouchHelper 实践 实现 RecyclerView 拖动排序和滑动删除,我想到的是 ViewDragHelper ,或者是第三方库,当我看了 ToDoList 的时候,发现原来官方已经支持 RecyclerView 拖动排...

掘金官方 ⋅ 2017/12/25 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

vbs 取文件大小 字节

dim namedim fs, s'name = Inputbox("姓名")'msgbox(name)set fs = wscript.createobject("scripting.filesystemobject") 'fs为FSO实例if (fs.folderexists("c:\temp"))......

vga ⋅ 12分钟前 ⋅ 1

高并发之Nginx的限流

首先Nginx的版本号有要求,最低为1.11.5 如果低于这个版本,在Nginx的配置中 upstream web_app { server 到达Ip1:端口 max_conns=10; server 到达Ip2:端口 max_conns=10; } server { listen ...

算法之名 ⋅ 今天 ⋅ 0

Spring | IOC AOP 注解 简单使用

写在前面的话 很久没更新笔记了,有人会抱怨:小冯啊,你是不是在偷懒啊,没有学习了。老哥,真的冤枉:我觉得我自己很菜,还在努力学习呢,正在学习Vue.js做管理系统呢。即便这样,我还是不...

Wenyi_Feng ⋅ 今天 ⋅ 0

博客迁移到 https://www.jianshu.com/u/aa501451a235

博客迁移到 https://www.jianshu.com/u/aa501451a235 本博客不再更新

为为02 ⋅ 今天 ⋅ 0

win10怎么彻底关闭自动更新

win10自带的更新每天都很多,每一次下载都要占用大量网络,而且安装要等得时间也蛮久的。 工具/原料 Win10 方法/步骤 单击左下角开始菜单点击设置图标进入设置界面 在设置窗口中输入“服务”...

阿K1225 ⋅ 今天 ⋅ 0

Elasticsearch 6.3.0 SQL功能使用案例分享

The best elasticsearch highlevel java rest api-----bboss Elasticsearch 6.3.0 官方新推出的SQL检索插件非常不错,本文一个实际案例来介绍其使用方法。 1.代码中的sql检索 @Testpu...

bboss ⋅ 今天 ⋅ 0

informix数据库在linux中的安装以及用java/c/c++访问

一、安装前准备 安装JDK(略) 到IBM官网上下载informix软件:iif.12.10.FC9DE.linux-x86_64.tar放在某个大家都可以访问的目录比如:/mypkg,并解压到该目录下。 我也放到了百度云和天翼云上...

wangxuwei ⋅ 今天 ⋅ 0

PHP语言系统ZBLOG或许无法重现月光博客的闪耀历史[图]

最近在写博客,希望通过自己努力打造一个优秀的教育类主题博客,名动江湖,但是问题来了,现在写博客还有前途吗?面对强大的自媒体站点围剿,还有信心和可能型吗? 至于程序部分,我选择了P...

原创小博客 ⋅ 今天 ⋅ 0

IntelliJ IDEA 2018.1新特性

工欲善其事必先利其器,如果有一款IDE可以让你更高效地专注于开发以及源码阅读,为什么不试一试? 本文转载自:netty技术内幕 3月27日,jetbrains正式发布期待已久的IntelliJ IDEA 2018.1,再...

Romane ⋅ 今天 ⋅ 0

浅谈设计模式之工厂模式

工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 在工厂模式中,我们在创建对象时不会对客户端暴露创建逻...

佛系程序猿灬 ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部