【Android源码学习】View的layout和draw流程

关于View的layout和draw流程,各路大神们的博客都讲解的很清晰,View绘制流程,本文仅写出自己的理解和总结,方便加强印象。
一、layout
measure是通过父布局推荐的大小确定各个子View最终的大小,同样,layout通过父布局推荐的位置确定各个子View最终的位置。它的入口是ViewRootImpl的performLayout,ViewGroup.layout(DecorView不能重写layout方法,因为ViewGroup的layout是个final的方法,它相对于View的layout方法添加了布局动画判断逻辑),View.layout,DecorView.onLayout,FrameLayout.onLayout,FrameLayout.layoutChildren,各个子View和子布局.layout。可以看出View.layout的逻辑比较简单,它先看判断需要重新measure,再判断位置是否确实发生变化,如果变化了则调用View.onLayout并且触发相应的监听器。而View.onLayout是个空方法(ViewGroup重写并限定为抽象方法),主要用来确定子View在父布局内部的位置,它的实现依赖于布局的类型,这里就不详述了。
二、draw
同样,draw的起点是ViewRootImpl.performDraw、DecorView.draw(相比View.draw多了菜单背景的绘制)、View.draw(如果是ViewGroup则会调用dispatchDraw,如果是View会调用onDraw)、ViewGroup.dispatchDraw、ViewGroup.drawChild、View.draw(Canvas canvas, ViewGroup parent, long drawingTime)、View.draw(Canvas)。
这里与measure和layout不同的是,ViewGroup不是在onDraw里遍历绘制子View,而是用dispatchDraw来“分发绘制事件”,onDraw仅用来绘制本身内容。原因可能是“绘制事件的分发”逻辑都相同吧,通常都不用重写dispatchDraw方法。
draw的递归调用弄清了,再看draw的主要逻辑。它依次绘制背景、保留渐变层级信息、绘制内容、绘制子View内容、绘制渐变效果、绘制装饰内容(滚动条等)。
三、View和ViewGroup的区别
它们在View绘制过程中的区别是:
View的measure方法是final的,onMeasure默认设置父布局推荐的大小,layout方法设置位置并触发监听器,onLayout为空方法,draw方法整合背景、内容、子View内容等逻辑,真正绘制内容是在onDraw里,它依赖于具体View所以onDraw方法是abstract的,同时dispatchDraw是空方法因为非ViewGroup的View没有子View。
而ViewGroup没有measure方法,它是在抽象方法onMeasure中遍历测量各个子View,它的layout方法是final的,与View相比主要多了布局动画的逻辑,它的onLayout方法是abstract的,通常无需重写draw和onDraw以及dispatchDraw方法。
简单来说各有一个final方法,View是measure,ViewGroup是layout,各有一个abstract方法,View是onDraw,ViewGroup是onLayout。
四、View的requestLayout
View的invalidate和requestLayout
它自底向上调用父View的requestLayout,最终会触发一次ViewRootImpl类的schedualTraversals。会调用measure和layout过程,在layout过程有可能间接调用invalidate而引起某些View重绘。
五、Invalidate和postInvalidate
与requestLayout,它们也是自底向上调用父View的方法,最终触发一次ViewRootImpl的schedualTraversals,但是有可能引起界面的重绘。
触发invalidate的情况通常有以下几种:

  • 不做其他任何操作,直接调用invalidate方法.请求重新draw,只会绘制调用者本身。
  • 触发setSelection方法。请求重新draw,只会绘制调用者本身。
  • 触发setVisibility方法。 当View可视状态在INVISIBLE和VISIBLE间转换时会间接调用invalidate方法,重而重绘该View。当View的可视状态在INVISIBLE\VISIBLE 和GONE状态间转换时会间接调用requestLayout和invalidate方法,由于View树大小发生了变化,所以会请求measure过程以及draw过程,所以发生了变化的View都会重绘。
  • 触发setEnabled方法,可能重绘当前View。
  • 触发requestFocus方法。请求View树的draw过程,只绘制“需要重绘”的View。
    总的来说,当布局的大小和位置都没有发生变化时,调用invalidate只会重绘当前View,如果大小和位置任一发生了变化,则会重新进行measure和layout,并重绘发生了变化的所有View。
    postInvalidate实质与invalidate一样。

你可能感兴趣的:(Android源码)