看到这个标题是不是很难受,哈哈,个人工作的项目中关于播放器动画的自定义View的代码多的一大堆,仿佛写这代码的开发跟我从事的不是同一个工作,里面基本上一行注释都没有,Duang Duang的写了十几个类,几万行代码,心里佩服,活了一大把年纪的,也是高人,但是写的代码看一眼都嫌难受。没办法,项目中要用到,更何况现在的我还是以UI为主的程序员,自定义View都不懂说出去都有点丢人,还是写点自己关于自定义View的理解
首先了解下具体的流程,就像了解Android组件中的Activity的生命周期一样,少说贴图
上图是一个View的流程,从上至下,具体含义如下
View的onMeasure() --> 看源码知道这是一个final类型的方法,子类不能重写此方法,所以一般我们加入自定义一个View继承TextView的基类View的时候,要重写onMeasure的方法,并设置warp_content的自身大小,不然在xml布局文件中使用warp_content这个属性的时候就相当于使用match_content,这个东西让初学的我搞了半天,着重记一下。
View的onLayout() --> 先执行layout方法,在执行onLayout的方法,layout的作用是ViewGroup用来确定子元素的位置,当ViewGroup的位置被确定后,它在onLayout中会遍历所有的子元素并调用其layout方法。简单的来说就是,layout方法确定View本身的位置,而onLayout方法则会确定所有子元素的位置。
View的draw() --> 这个简单理解,就是将View绘制到屏幕上面,这个有好多方法,这个了解下就好,简单形象的了解下就是相当于我要给我家装修,一般步骤是 1.确定房间基调(背景),2.描绘图案(绘制线条),3.具体图案(绘制装饰),以这个顺序就是1.backgroud.draw, 2.onDraw, 3.onDrawForeground。就是这个顺序。其中还有一个方法就是disptachDraw,这个是子类绘制的,看到这个就想到了好多其他关于disptach的方法,没错,这个也会有冲突,想让自己写的自定义View也可以优化,这点就需要考虑进去。
以上是单个View 的绘制,但是我们一般都用不到,因为Google已经提供的一套的已经很好了,完全没有必要需要我们重新进行写,这是一种费力不讨好的活计,我们要自己思量着来,别一下子为自己工作的项目中引入了一大推内存问题。下来就是着重了哦,自定义ViewGroup的绘制,如图
画的不好,莫要吐槽
这个就是我们一个界面绘制的流程了,什么是ViewRoot,ViewRoot对应于ViewRootImpl类,它是连接WindowManager和DecorView的纽带,View的三大流程均是通过ViewRoot来完成的。在ActivityThread中,当Activity对象被创建完之后,会讲DecorView添加到Window中,同时会创建对应的ViewRootImpl,并将ViewRootImpl和DecorView建立关联,并保存到WindowManagerGlobal对象中。View的绘制流程是从ViewRoot的performTraversals方法开始的,它经过measure、layout和draw三个过程才能最终将一个View绘制出来
由于每种布局的measure的方式不一样,我这种半吊子也不好拿出来一一分析,大致上的规律也就那马几个,我就总结下
1. 根据各自的测量规则遍历Children元素,调用getChildMeasureSpec方法得到Child的measureSpec2. 调用Child的measure方法
3. 调用setMeasuredDimension确定最终的大小
onLayout的方法上述也提到,子View确定自己的位置,Group确定所有子View的位置,这边的话方法了解就大差不差了,就可以控制自身的位置显示了,反正里面方法的参数都有注释,直接看参数名字就可以明白,哦这个参数是要设置View的上边距,下边距之类的
onDraw这一块比较重要了哦,这边能做的操作很多,像一般动画都放在这里面,放点注释在里面就好了
- onDraw()函数一般由系统布局管理器来调用,在View第一次加载的时候会调用一次,或者在系统认为需要重绘的时候也会被调用。当然,你也可以在程序中手动触发该View的重绘,通过调用View的invalidate()函数或者postInvalidate()函数即可,前者用于UI线程,后者用于非UI线程。
- onDraw()的参数Canvas我们可以理解成系统提供给我们的一块内存区域,所有的绘制都是在这块内存中进行的,绘制完成后系统会显示到屏幕中去。该Canvas对象提供了各种绘制点、线、矩形、圆、位图的方法,基本可以满足各种绘制要求。
- drawRect函数需要提供四个坐标,其中,前两个参数代表是被绘制矩形的起始点坐标,后两个参数则是相对于起点的斜对角坐标,注意,原点坐标(0,0)在屏幕的左上角。
- onDraw()每次被调用的时候,原来画布中的内容会被清空。