android开发艺术探索 第四章 view的工作原理

本章主要讲ViewRoot、DecorView、MeasureSpec、View的工作流程、自定义view

1.ViewRoot和DecorView

①ViewRoot的PerformTraversals方法开始最终调用view的measure、layout、draw方法。

②decorview最终分为有titlebar和content两个布局,我们平时设置的setcontentview布局就是content这一块的,titlebar是标题栏actionBar的那部分。

2.MeasureSpec

①.MeasureSpec代表32位int值,高2位代表SpecMode,低30位代表SpecSize。

②.SpecMode:测量模式(分为:UNSPECIFIED(无限大小)、EXACTLY(精准大小)、AT_MOST(可用大小))

③.SpecSize:某种测量模式下的规格大小

④.decorView的MeasureSpec由本身的LayoutParams决定的,对应关系:

LayoutParams.MATCH_PAREMT:精准模式EXACTLY

LayoutParams.WRAP_CONTENT:最大模式AT_MOST

固定大小(比如:100dp):精准模式EXACTLY

⑤.普通view的MeasureSpec由父容器的MeasureSpec和本身的LayoutParams决定的,对应关系如下:

parentSpecSize

 

ChildLayoutParams      

EXACITY

AT_MOST

UNSPECIFIED

dp/px

EXACITY

childSize

EXACITY

childSize

EXACITY

childSize

match_parent

EXACITY

parentSize

AT_MOST

parentSize

UNSPECIFIED

0

wrap_content

AT_MOST

parentSize

AT_MOST

parentSize

UNSPECIFIED

0

3.view测量布局绘制过程

①.测量过程(measure过程)

view的onMeasure方法中直接调用serMeasuredDimension方法设置测量大小。

注意:此处的wrap_content和match_parent的值一样,可以重写onMeasure方法给wrap_content设置默认值和match_parent区分

viewGroup的onMeasure方法未实现,需要根据布局重写此方法:

步骤:(1)测量子view  (2)测量自身

LinearLayout中来说:

(1)通过measureChildWithMargins方法测量子view的大小。

(2)通过setMeasuredDimension方法结合子view的状态测量本身view的值。(此处当是wrap_content时,高度是子view高度之和;采用match_parent或者具体数值则和测量view的一致)。

注意:View的measure过程和生命周期不是同步执行的,所以可能在oncreate,onStart,onResume中无法获取宽高。

解决方式:(1)通过onWindowFocusChanged方法,此方法表示view加载完毕。

                  (2)通过view的post方法,此方法在view初始化完成的尾部执行。

                  (3)通过ViewTreeObserver回调接口功能。

                  (4)手动调用view的measure方法进行测量。

对比发现:(1)(3)会被多次调用,(4)写起来比较麻烦,对match_parent还测量不出,所以(2)是最好的方式。

②.布局过程

父布局通过layout方法最终调用setFrame方法确认自己的位置。

父布局调用onLayout最终通过for循环调用setChildFrame方法确认子view的位置。

③.绘制过程(根据官网有详细步骤,如下)

*      1. Draw the background
*      2. If necessary, save the canvas' layers to prepare for fading
*      3. Draw view's content
*      4. Draw children
*      5. If necessary, draw the fading edges and restore layers
*      6. Draw decorations (scrollbars for instance)

第二步(保存图层)和第五步(绘制阴影效果)可以跳过,所以步骤如下:

(1)绘制背景;(drawBackground)

(2)绘制view;(onDraw)

(3)绘制子view;(dispatchDraw)

(4)绘制装饰;(ondrawScrollBars)

注意:在view中有setWillNotDraw是表示是否绘制的,如果不用绘制可以设置为true,这样便于系统优化,如果需要绘制设置为false。

4.自定义view:

①自定义view须知:

(1).让view或者viewGroup支持wrap_content。

(2).让自定义view支持padding,需要在onDraw里面处理padding;让自定义viewGroup支持padding,需要在onMeasure和onLayout考虑到padding和子元素margin对其影响。

(3).不要在view中用handler,view本身的post系列方法可以替代handler作用。

(4).view中有线程或者动画需要及时停止,可以在onDetachedFromWindow方法中做停止操作。

(5).view中有镶嵌滑动需要处理好滑动冲突。

②.示例自定义view(画一个圆形的view)

(1).复写onDraw(canvas)方法实现画圆

(2).在onMeasure(widthMeasureSpec,heightMeasureSpec)方法中处理wrap_content情况。

(3).在onDraw(canvas)中处理padding情况

③.示例自定义ViewGroup(实现类似横向滑动的ViewGroup)

(1).复写onMeasure(widthMeasureSpec,heightMeasureSpec),在这里面测量view及子view,需要考虑到padding及子view的margin的影响。

(2).复写onLayout确定子元素的位置,需要考虑到padding及子view的margin的影响。

(3).处理滑动嵌套冲突问题。

猜你喜欢

转载自blog.csdn.net/gongjdde/article/details/89278976
今日推荐