0. 前言
最近项目不是特别紧张,有一点时间可以用来看点东西,于是找了下自定义View相关的文章来看看,毕竟也确实挺久没碰到了。和一基友交流了下,他最近也在看这方面,并且推荐了启舰的系列文章,大致都看了下,感觉讲的很细致,而且也挺全面,这里也推荐下,希望给大家在自定义View这块有所帮助。(传送门在文末会给出)
看完文章之后,得来一发才行呀,所谓“纸上得来终觉浅,绝知此事要躬行”嘛。想到了以前项目里有个显示密码等级的控件,那干脆就再来实现一下。
1. 需求
在注册界面,用户设置密码时,为了更好的交互体验,需要根据当前用户输入的密码的复杂程度,通过不同颜色的色块来实时的表示出密码有多强。好吧,说起来挺啰嗦,咱们直接看效果就知道了:
从效果图我们可以看出:
- 控件总共有4个色块,分别为红、黄、蓝、绿,对应密码强度为风险、弱、中、强,而且色块后会有相应的强度描述,看起来好像是竖直方向居中的呢;
- 用户输入的过程中,根据输入密码的复杂度,控件会实时更新成对应的状态;
- 色块区域和文字之间貌似有一定间隙;
以上大致就是我们想要实现的东西了,接下来我们分析下具体实现。
2. 实现过程
2.1 自定义属性及解析
首先,一般自定义View都会涉及到自定义属性以及对应的解析取值操作,那么我们就来走一遍这个过程。我们定义一个属性,表示色块与文字之间的距离(当然其实定义文字尺寸貌似更好,这个就不用太过纠结了哈,都一样)。
如你所见,属性定义就是这么简单。通过declare-styleable标签来定义了一个名为PasswordLevelView的属性集,通过attr标签来定义了一个名为text_padding_level的属性,值类型为dimension。
接下来我们要在xml布局里把刚定义好的属性用起来:
好了,下面就该解析了:
也没啥好说的,主要就是两个重要方法:obtainStyledAttributes与TypedArray.getDimensionPixelSize。如果定义的是其他数据类型的属性的话,通过相应的TypedArray.getXXX方法去拿就好了。
2.2 类实现
我们这里的需求比较简单,核心思路就是在合适的时机,在正确的位置,把色块和文字给画出来,这个操作在onDraw方法中通过Canvas就能实现,所以我们继承View就行。来看代码:
代码不多,一点一点来过吧。
首先是定义了一个枚举Level,封装了对应的4个强度等级的信息。
接下来是3个构造方法,通过this调用,来执行参数最多的那个构造方法,这个是一般套路了,这里不再展开讲。
calculateTextWidth方法主要作用是计算强度文字的宽高尺寸,因为我们后面draw的时候需要各种计算尺寸。这里需要特别强调的是,在为文字测量宽高之前,需要先setTextSize,否则尺寸会不准确。这个也很好理解嘛,文字的大小不一样,当然所占用的宽高是不一样的。
接下来是onMeasure方法,也是一般的套路,通过MeasureSpec.getMode和MeasureSpec.getSize方法,来解析并调整控件的宽高,这里我们处理了高度值可能出现的异常情况。
然后是onDraw方法,在这里面我们draw了色块和文字。画色块的逻辑并不复杂,计算色块的宽高时,注意要把整体控件的padding值给考虑进去就行了。我们重点来看画文字的操作。
先来看一张图,不是本人原创哈:
在屏幕上展示的文字,不管尺寸颜色,都有上图中这几个重要的值:baseline, ascent, descent,他们分别表示的是哪一段长度,图上表示的很清除了,不再赘述。这些字段可以通过Paint.FontMetrics类来获取,而Paint.FontMetrics可以通过这个方法来取到:
同样也要注意,在此之前需要先设置文字尺寸。
不知看到这里,大家有没有想过这些值的正负。我在调试的过程中截了个图:
尼玛啊,真是日了狗。。。API文档里说好的表示的是distance的呢?怎么会有正负值?事实上,我在画文字的过程中就掉这个坑里了,最后通过debug发现了这个坑,所以大家要特别注意,不要再掉里面啦。
好了,这几个字段说了这么多,到底有啥用呢?其实是为了drawText的时候计算baseline的坐标服务的。使用Canvas.drawText方法可以画文字,其中有两个参数就是文字的baseline坐标。所以为了把文字draw到色块横向对应的中心线上,我们需要计算出文字的baseline坐标:
OK,比较难的baseline坐标值就搞定了。到此,核心的东西就都介绍完了。
3. 总结
本文实现了一个简单的密码等级展示效果,目的是回顾一下自定义View的过程。在我看来,自定义View的关键是要理解并掌握Canvas,Paint,Matrix这几个核心的绘制相关类,还有就是各种动画。掌握好这些,要实现一些复杂炫酷的效果就不是困难的事情啦。
最后,我是demo下载地址