感觉是时候输出一点东西了…
一. 写在开头
作为一名客户端开发者,不仅要能写出健壮的逻辑代码,而且必须得能写出优美的界面,不然为啥不去做后台呢哈哈当然这是玩笑话,但是作为一名客户端开发者对于自定义 View 还是必须要熟练掌握的,不然很多需求你都完成不了。今天主要给大家输出一下我对自定义 View 的一些理解。
上方是我开源的已经上线的 Android 小游戏里面大量使用自定义View如果需要大家可以看下哈~
二. 如何自定义 View
1. 继承 View
java
public class GameView extends View {…}
![构造函数.png](http://upload-images.jianshu.io/upload_images/2789199-0a830dc64cb14cac.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
继承 View 后重写它的构造函数,对于这四个构造函数,1是使用在Java代码中,2是使用在xml 文件中,3,4都是样式相关的,对于我们普通的自定义 View,继承第二个构造函数就行。
######2. 重写onDraw 方法
onDraw 方法是自定义 View 的主要方法,在这个方法体内通过 Canvas 相关函数绘制你想要的界面元素或者动画即可,需要注意的是,这个函数每次的调用都是完全重新绘制而且只能在主线程绘制。
如何调用 onDraw 方法呢?首先当界面发生变化的时候,比如滚动,缩放等,onDraw 会被主动回调,另外如果你想主动调用 onDraw 方法,可以使用 View 中 invaildate 方法或 postInvaildate 方法。
######3. 其他的一些函数
对于其他的比如 Measure,Touch相关的方法大家可以自己看下我的开源项目,很简单也很容易懂,就不再赘述,如果有需要的话可以下次分享~
#####三. 我对 onDraw 的理解
######1. 原理
![原理.png](http://upload-images.jianshu.io/upload_images/2789199-36b27572720c33d1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/600)
上图的大致意思就是,我在主线程每次对 View 进行更新,实质上是更新了 View 内部对自身的缓存图像,不管你更新的频率如何,Android 界面显示的时候始终以 16ms 一次的速度读取你的缓存并绘制到屏幕上。
看到这里是不是感觉 View 的绘制原理很简单,实质上就和动画播放的原理一样,都是一帧一帧的播放,每16ms 取一次的就相当于取一帧图片,然后这些图片连续起来就形成了动画,当然这个播放的频率我们控制不了,但是16ms 一帧人的肉眼已经感受不出卡顿了(我们的手机其实就相当于一个以16ms 每帧不停播放内容的显示器)。
######2. 为什么我的自定义 View 的动画会卡顿(掉帧)
就像上面的原理图(如果你的自定义 View 不存在动画的话你就可以不考虑卡顿问题了,毕竟不动的东西哪来的卡顿...),我们想一下动画为什么会卡顿,是不是因为里面有一帧两帧的图片没有取到(这也是为什么叫掉帧的原因),但是我们的界面更新的时候不会存在取不到缓存的情况,那原因只有我们的 View 缓存没有及时更新,就像是以前老式手摇电影,每摇几秒停一秒再继续重复,是不是给我们的感觉就是卡顿了?为什么没有更新呢?
![原理图.png](http://upload-images.jianshu.io/upload_images/2789199-afb54d50aff949d5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/600)
在一个情景下,比如我需要自定义 View 上的一个圆跟随手指移动,如上图,我前两次对 View 调用 invalidate 更新缓存的时候,都赶上了界面每一帧对缓存的读取,所以这时候没有卡顿,小圆始终在手指下方,但是第三次更新因为其他耗时任务占着主线程导致更新延迟了38ms 才得以实施,中间就错过了3次界面的读帧,并且第4次更新很快来了,界面的下次更新直接读取了第4次更新的缓存,也就是第3次更新的缓存不仅有了延迟而且没有绘制,就造成了卡顿,这时小圆会出现从手指外的一个位置突然跳到手指底下,也就是掉帧了。这是卡顿的一种原因,当然卡顿的原理都差不多,都是有帧没有绘制上去,其他的原因大家可以自己考虑一下。
######3. 如何才能避免类似的卡顿呢?
1. 因为我们所有的界面绘制都是在主线程,所以尽量避免在主线程做耗时任务是必须的。
2. 不管是在 View 的onDraw 方法还是 SurfaceView的 unlockCanvasAndPost方法前的绘制工作的执行时间都要尽量的短,因为每次刷新缓存之前都需要绘制,如果这个绘制过程超过16ms,对缓存的更新也会有延迟,从而导致掉帧。
>小技巧:
>1. 优化算法。
>2. 尽量避免在绘制方法内部创建复杂对象,因为这些方法都是频繁调用的,所以这些复杂对象就会被频繁创建销毁导致内存动荡,从而导致系统 GC 频繁发生,系统 GC 的时候有可能会暂停所有线程操作,这也是会导致卡顿的一个原因(我曾经在优化一个卡顿自定义 View 的时候把复杂对象提到外面初始化,界面卡顿明显得到优化)。
>3. 如果做有动画的界面的话,个人推荐使用 View,因为亲测 SurfaceView 对界面的绘制更新要比 View 的更新慢很多...内部原理还没来得及查,后面查阅之后再写上来。
#####四. 写在结尾
一些看似复杂难懂的东西,当你把它抽象成生活中相关的简单的东西的时候,你会发现它变得异常简单,哈哈~以后再把之前忙的时候积累的东西逐渐输出与大家交流学习~来来来老铁们关注走一波~
欢迎大家支持我最近一直在维护的独立开发的应用:
思维导图:http://zhushou.360.cn/detail/index/soft_id/3346022
欢迎大家关注我的新浪微博:
![新浪微博](http://upload-images.jianshu.io/upload_images/2789199-b3d6394fc533f34e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)