通常,自定义View有三个大的区分点,在日常开发中遇到自定义View的需求时,最好想一想该走哪条路,走得快一些轻松些总是好的。
1 简单的“自定义”
需求总是在不经意间来临,也许是原生控件丑了点,也许是哪些布局复用的多了些,Leader让改改,写个自定义View解决一下,减少一下重复开发工作,这时候该我们迎难而上了,必要的时候还是得造个轮子。
这时候,看一眼需求,原来是这个TextView
的样式太单调了,只有简单的背景、前景色之类的,产品和UI让改改加点炫酷的东西,文字得一上一下写(上下)。得,自定义View吧,感觉相对于原生的TextView
区别只有一个,绘制过程中调整一下位置即可,那就简单点,写个UpDownTextView
,继承TextView
,改改onDraw()
方法。
第一个需求解决,来看下一个需求,咱们项目中有个布局好像用的比较多,好几个地方用到了,关键它逻辑也一样,只是include布局文件还是要写不少重复代码,得抽离一下。自定义View吧,看看已经抽离的布局文件,这个布局是一个RelativeLayout
,里面包含了我们需要的控件之类的,写个BtRelativeLayout
继承RelativeLayout
,然后把布局文件的根布局RelativeLayout
改成BtRelativeLayout
,再把原有的逻辑都写在BtRelativeLayout
类里,搞定。
简单的“自定义”就是这样了,继承了现成的控件,再在此基础上完成附加的功能样式或者更改原有的功能样式,可以迅速达到想要的效果,缺点是会受到父类的限制、无法完成复杂的需求。
2 重写一个View
万恶的需求又来了,抄家伙上!一顿操作猛如虎,一看需求心里堵。来吧,这会真的要重头写一个View
了。
先仔细看看需求,一个声音波形控件,持续动画效果,单靠上面的简单实现是不可能的了,需要的效果如下:
这时候是时候新建一个类VoiceLineView
,继承自View
,我们来理一理大致思路:
- 可能需要我们重写
onMeasure()
方法,计算VoiceLineView
的宽高。
根据父布局传入的MeasureSpec
,计算出自身的宽高并保存。 - 可能需要我们重写
onDraw()
方法,对VoiceLineView
的绘制进行控制。
循环绘制,根据宽高、每个点的位置绘制需要的波形图。 - 可能需要我们提供一些自定义的属性,方便上层定制化
VoiceLineView
。
提供样式、颜色、最大值等属性设置。
如果有感兴趣的小伙伴可以动手写了,具体的实现我将会放在下一篇文章当中,文章链接等我写完下一篇文章后会更新。
3 重写一个ViewGroup
VoiceLineView
顺利上线使用,是时候放松一波了,掏出我新买的小梳子,打理一下为数不多的头发。很秃然,一只大手摸上了我的肩。。。一个抖激灵,一根珍贵的秀发开始了一场说走就走的旅行(T^T)。产品大叔又又又来了!!!
在无数次压下拔刀斩的念头之后,我们看看这次的需求,很好,改造图片九宫格的重任,根据图片的数量调整图片的布局(仿造Facebook ),让页面不要那么死板,是个鸡肋 不错的想法。
我们先来看看原有的图片九宫格,和微信的朋友圈图片九宫格一样,固定图片大小,最多是一个三行三列的表格,简单高效,多好的孩子,动什么刀子啊,ε=(´ο`*)))唉。
话说回来,要实现需求需要一个自定义的ViewGroup
,就叫DynamicImageGroup
吧,类似的,我们来理一理大致思路:
- 可能需要我们重写
onMeasure()
方法,计算DynamicImageGroup
的宽高。
根据父布局传入的MeasureSpec
,计算出自身的宽高并保存。 - 可能需要我们重写
onLayout()
方法,计算子View
的位置。
根据onMeasure()
方法中计算并保存的宽高、子View
的宽高,计算子View
的位置。 - 可能需要我们提供缓存机制,加快绘制速度减少卡顿。
如果有感兴趣的小伙伴可以动手写了,具体的实现我将会放在之后的文章当中,文章链接等我写完后会更新。
4 综述
三条路都有适用点,我们要合理选择,做最懒的程序猿:
- 站在巨人的肩膀上。对一些只是在某个View的基础上进行的改动,只需要直接继承该View,按需替换或者增删功能,最快速度完成需求。
- 必要的时候,轮子还是要造的。对于系统目前没有类似View的需求,如果不是包含某些View的话,实现类继承自View,重写onMeasure()方法和onDraw()方法,再提供自定义属性。
- 必要的时候,可能还得造个车。需要包含某些View的情况下,如没有类似的ViewGroup可以改改的话,就需要实现类继承自ViewGroup,重写onMeasure()方法和onLayout()方法,有需要的话可以加上缓存机制。