View的工作原理
ViewRoot:
ViewRoot应用于ViewRootImpl类,它是连接WindowManager和DecorView的枢纽,View的三大流程都是通过ViewRoot来完成的
三大流程:测量measure、布局layout、draw绘制
从ViewRoot的performTraversals方法开始的,经过3大流程将一个view绘制出来
顶级view--DecorView:
本身就是一个FrameLayout,包含一个竖直方向的LinearLayout,而linearLayout又包含内容栏和标题栏,其中内容栏也是一个FrameLayout,它的id是content,通过他的方法getChildAt(0)就可以获得我们的view
Measure:
MeasureSpec,测量说明书,很大程度上决定了一个View的尺寸规格
在测量的过程中,系统会将View的LayoutParams根据父容器所施加的规则转换成对应的MeasureSpec,然后根据这个进行测量
MeasureSpec的2个参数,SpecMode和SpecSize,一组SpecMode和SpecSize就可以打包为一个MeasureSpec,相反解包也可以获得参数
SpecMode有三类:UNSPECIFIED不限制型、EXACTLY精准型(对应match_parent和数值型)、AT_MOST(对应wrap_content)
需要注意的是,MeasureSpec是由LayoutParams、margin、padding和父容器一起决定的,其中顶级View父容器没有,由窗口尺寸决定
LayoutParams也有3种模式:MATCH_PARENT、WRAP_CONTENT、固定值模式
View的MeasureSpec的创建规则如下表:
在测量阶段,用getMeasuredWidth/Height来获得测量宽高,基本和layout中使用getWidth/Height的实际宽高是一样的
*直接继承自View,需要重写onMeasure方法并且设置wrap_content的自身大小,否则wrap_content不能正常工作,它和match_parent效果一样,设置的方法如下:
如果在活动中需要获得自定view的属性值,由于它和活动的生命周期不同,所以可能会获取失败,可以有一些方法解决:
onWindowFocusChanged,焦点,这个方法在变成焦点和失去焦点时会被调用,此时view已经绘画完了,因此可以在这里获得准确的属性
使用view自身带有的post方法(尽量不要使用Handler),将一个Runnable传递进去,在消息队列里放置消息,然后在Runnable里执行获得属性的操作
使用ViewTreeObserver
使用view.measure手动对View进行测量
Layout:
作用是ViewGroup用来确定子元素的位置
在测量阶段,用getMeasuredWidth/Height来获得测量宽高,基本和layout中使用getWidth/Height的实际宽高是一样的
Layout阶段用来确定view的位置,也就是四个顶点和实际宽高
Draw:
过程:
绘制背景background.draw(canvas)
绘制自己onDraw
绘制children(dispatchDraw)(通过dispatchDraw来实现)
绘制装饰(onDrawScrollBars)
自定义View:
分类:
继承自View重写Draw:需要自己支持wrap_content和处理padding
继承自ViewGroup派生的特殊的Layout:需要合适地处理ViewGroup的测量、布局过程,同时需要处理子元素的测量和布局过程
继承自特定的View
继承自特定的ViewGroup
需要注意的是,如果View中有线程或者动画,需要及时停止
另外,我们在继承自View时,类名是XML中的标签名,类似Button这个名称一样,所以还要为View制定id
自定义属性:
第一步,在values下创建自定义属性XML
第二步,在View的构造方法中,对自定义属性的值进行解析,然后处理
第三步,在布局文件中使用自定义属性
app就是自定义属性的前缀
自定义View的思想:
1. 掌握基本功,比如View的弹性滑动、滑动冲突、绘制原理
2. 能够对其进行分类并选择合适的实现思路