View的绘制流程

参考文章链接 http://a.codekk.com/detail/Android/lightSky/%E5%85%AC%E5%85%B1%E6%8A%80%E6%9C%AF%E7%82%B9%E4%B9%8B%20View%20%E7%BB%98%E5%88%B6%E6%B5%81%E7%A8%8B

1.View树的绘图流程

View树的绘制是自上而下进行遍历,由父视图到子视图,每一个ViewGroup负责绘制它所有的子View,而最底层的View会负责测绘本身。

Measure过程有measure(int,int)方法发起,从上到下有序的测量View,在measure过程的最后,每个视图存储了自己的测量大小和测量规格。Layout过程由layout(int,int, int,int)方法发起,也是自上而下进行遍历。在该过程中,每个父视图会根据measure过程得到的尺寸来摆放自己的子视图。

Measure过程会为一个View及所有子节点的mMeasuredWidth和mMeasuredHeight变量赋值,该值可以通过getMeasuredWidth()和getMeasuredHeight()方法获得。

Measure过程传递尺寸的两个类ViewGroup.LayoutParam(View自身的布局参数)和MeasureSpecs(父视图对子视图的测量规格)。

1.1MeasureSpecs

测量规格,包含测量要求和尺寸的信息,有三种模式:

(1)UNSPECIFIED

父视图不对子视图有任何约束,子视图可以达到它所期望的任意尺寸。比如ListView、ScrollView

(2)EXACTLY

父视图为子视图指定一个确切的尺寸,而且无论子视图期望多大,它都必须为指定的尺寸,对应的属性为match_parent或具体的值,父控件可以通过MeasureSpecs.getSize(measureSpec)直接得到子控件的尺寸。

(3)AT_MOST

父视图为子视图指定一个最大尺寸。子视图必须确保它自己所有的子视图可以适应在改尺寸范围内,对应的属性为wrap_content,这种模式下,父控件无法确定子View的尺寸,只能由子控件根据需求去计算自己的尺寸,这种模式就是我们自定义视图需要实现测量逻辑的情况。

1.2measure核心方法

(1)measure(intwidthMeasureSpecs, int heightMeasureSpecs)

该方法位于View.java类中,为final类型,不可被复写,单measure调用链最终会调用View/ViewGroup的onMeasure()方法,因此自定义视图只复写onMeasure()方法即可。

(2)onMeasure(intwidthMeasureSpecs, int heightMeasureSpecs)

该方法就是我们自定义视图中实现测量逻辑的方法,该方法的参数是父视图对子视图的width 和height 的测量规格。在我们自定义视图中,要做的就是根据该widthMeasureSpec 和 heightMeasureSpec 计算视图的 width 和height,不同的模式处理方式不同。

(3)setMeasureDimension()

测量阶段终极方法,在 onMeasure(intwidthMeasureSpec, int heightMeasureSpec) 方法中调用,将计算得到的尺寸,传递给该方法,测量阶段即结束。该方法也是必须要调用的方法,否则会报异常。在我们在自定义视图的时候,不需要关心系统复杂的Measure 过程的,只需调用setMeasuredDimension()设置根据MeasureSpec 计算得到的尺寸即可,可以参考 ViewPagerIndicator 的 onMeasure 方法。

measureChildren---measureChild---getChildMeasureSpec---measure---onMeasure---setMeasureDimension

(1)measureChildren(intwidthMeasureSpec, int heightMeasureSpec)

请求所有子 View去measure 自己,要考虑的部分有对子 View的测绘要求MeasureSpec 以及其自身的padding。这里跳过所有为 GONE状态的子 View,最繁重的工作是在getChildMeasureSpec 方法中处理的

(2)getChildMeasureSpec(intspec, int padding, int childDimension)

该方法是measureChildren 中最繁重的部分,为每一个ChildView 计算出自己的MeasureSpec。目标是将 父View对子View 的MeasureSpec 和 子View的LayoutParams结合起来去得到一个最合适的结果。

(3)measure(intwidthMeasureSpec, int heightMeasureSpec)

根据最后的measureSpec获取子View最终的大小,该方法会调用真正测量逻辑方法onMeasure(intwidthMeasureSpec, int heightMeasureSpec)

1.3layout相关概念及核心方法

首先要明确的是,子视图的具体位置都是相对于父视图而言的。在layout 过程中,子视图会调用getMeasuredWidth()和getMeasuredHeight()方法获取到measure 过程得到的mMeasuredWidth 和mMeasuredHeight,作为自己的width 和height。然后调用每一个子视图的layout(l,t, r, b)函数,来确定每个子视图在父视图中的位置。

onLayout方法

View 的onLayout 方法为空实现,而ViewGroup 的onLayout 为abstract 的,因此,如果自定义的 View要继承ViewGroup 时,必须实现onLayout 函数。

1.4绘制流程相关概念及核心方法

与draw过程有关的方法

(1)View.draw(Canvascanvas)

由于ViewGroup 并没有复写此方法,因此,所有的视图最终都是调用 View的 draw方法进行绘制的。在自定义的视图中,也不应该复写该方法,而是复写 onDraw(Canvas) 方法进行绘制,如果自定义的视图确实要复写该方法,那么请先调用 super.draw(canvas)完成系统的绘制,然后再进行自定义的绘制。

(2)View.onDraw(Canvascanvas)

View 的onDraw(Canvas)默认是空实现,自定义绘制过程需要复写的方法,绘制自身的内容。

(3)dispatchDraw(Canvascanvas)

发起对子视图的绘制。View 中默认是空实现,ViewGroup复写了dispatchDraw()来对其子视图进行绘制。该方法我们不用去管,自定义的ViewGroup 不应该对dispatchDraw()进行复写。

绘制流程

绘制View的背景---绘制View的自身内容---绘制子View---绘制fadeedge 和layer---绘制scrollBar

(4)drawChild(Canvascanvas, View child, long drawingTime)

(5)invalidate()

请求重绘 View树,即 draw过程,假如视图发生大小没有变化就不会调用layout()过程,并且只绘制那些调用了invalidate()方法的 View。

(6)requestLayout()

当布局变化的时候,比如方向变化,尺寸的变化,会调用该方法,在自定义的视图中,如果某些情况下希望重新测量尺寸大小,应该手动去调用该方法,它会触发measure()和layout()过程,但不会进行 draw。

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值