目录
③.示例自定义ViewGroup(实现类似横向滑动的ViewGroup)
5.getMeasuredWidth()与getWidth()函数区别
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方法设置测量大小。
注意:当父布局SpecMode是AT_MOST时此处的子布局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)是最好的方式。
注意:控件测量的结果只是为正式布局提供建议,至于用不用,要看onLayout
②.布局过程
父布局通过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)如有需要,保存图层;
(3)绘制view;(onDraw)
(4)绘制子view;(dispatchDraw)
(5)如有需要,绘制阴影效果;
(6)绘制装饰;(ondrawScrollBars)
注意:在view中有setWillNotDraw是表示是否绘制的,如果不用绘制可以设置为true,这样便于系统优化,如果需要绘制设置为false。
4.自定义view
①自定义view须知
(1).让view或者viewGroup支持wrap_content。
(2).让自定义view支持margin需要重写generateLayoutParams()函数和generateDefaultLayoutParams()函数,因为只有重写这两个函数才能获取控件的margin值
(3).让自定义view支持padding,需要在onDraw里面处理padding;让自定义viewGroup支持padding,需要在onMeasure和onLayout考虑到padding和子元素margin对其影响。
(4).不要在view中用handler,view本身的post系列方法可以替代handler作用。
(5).view中有线程或者动画需要及时停止,可以在onDetachedFromWindow方法中做停止操作。
(6).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).处理滑动嵌套冲突问题。
5.getMeasuredWidth()与getWidth()函数区别
大部分情况下上面两个函数获取得到的值相同,但是含义不一样
①.获取得到的时间点不一样:getMeasuredWidth()函数在measure()过程结束后就可以获取到宽度值,而getWidth()函数要在layout()过程结束后才能获取得到的宽度值。
②.获取的值设置方式不同:getMeasuredWidth()函数中的值是通过setMeasuredDimension()函数来进行设置的;而getWidth()函数中的值则是通过layout(left,top,right,bottom)函数来设置的。