View之invalidate,requestLayout,postInvalidate
其他
2019-03-23 10:31:20
阅读次数: 0
目录介绍
- 01.invalidate,requestLayout,postInvalidate区别
- 02.invalidate深入分析
- 03.postInvalidate深入分析
- 04.requestLayout深入分析
- 05.ViewRootImpl作用分析
- 06.这几个方法总结
好消息
01.requestLayout、invalidate与postInvalidate作用与区别
- invalidate() postInvalidate()
- 共同点:都是调用onDraw()方法,然后去达到重绘view的目的
- 区别:invalidate()用于主线程,postInvalidate()用于子线程【通过handler发送消息,在handleMessage中((View) msg.obj).invalidate(),】
- requestLayout()
- 也可以达到重绘view的目的,但是与前两者不同,它会先调用onLayout()重新排版,再调用ondraw()方法。
- 当view确定自身已经不再适合现有的区域时,该view本身调用这个方法要求parent view(父类的视图)重新调用他的onMeasure、onLayout来重新设置自己位置。特别是当view的layoutparameter发生改变,并且它的值还没能应用到view上时,这时候适合调用这个方法requestLayout()。 requestLayout调用onMeasure和onLayout,不一定调用onDraw
02.invalidate深入分析
- 看看invalidate源码,如下所示
- invalidateCache为true表示全部重绘。View的invalidate方法最后调用的是invalidateInternal方法,invalidateInternal方法中的重点逻辑在源码上添加注释呢。
public void invalidate() { invalidate(true); } public void invalidate(boolean invalidateCache) { invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true); } void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache, boolean fullInvalidate) { if (mGhostView != null) { mGhostView.invalidate(true); return; } if (skipInvalidate()) { return; } if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS) || (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID) || (mPrivateFlags & PFLAG_INVALIDATED) != PFLAG_INVALIDATED || (fullInvalidate && isOpaque() != mLastIsOpaque)) { if (fullInvalidate) { mLastIsOpaque = isOpaque(); mPrivateFlags &= ~PFLAG_DRAWN; } mPrivateFlags |= PFLAG_DIRTY; if (invalidateCache) { mPrivateFlags |= PFLAG_INVALIDATED; mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID; }
- 看看ViewGroup中的invalidateChild方法
- 在ViewGroup的invalidateChild方法中有一个循环,循环里面会一直调用父布局的invalidateChildInParent方法,而View和ViewGroup的最终父布局都是ViewRootImpl。
@UiThread
public abstract class ViewGroup extends View implements ViewParent, ViewManager { @Override public final void invalidateChild(View child, final Rect dirty) { ViewParent parent = this; final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) {
- View中的invalidateChild方法和ViewGroup中的invalidateChildInParent方法最后殊途同归,都会调用到ViewRootImpl中的方法
- 可以看到在ViewRootImpl中最后都会调用scheduleTraversals方法进行绘制。按照对于View的绘制过程的理解,View的绘制流程是从ViewRootImpl的performTraversals方法开始的
public final class ViewRootImpl implements ViewParent,View.AttachInfo.Callbacks, ThreadedRenderer.HardwareDrawCallbacks {
- 下面我们来看看ViewRootImpl中的scheduleTraversals方法
- 看到scheduleTraversals方法中调用了mChoreographer.postCallback方法
- Choreoprapher类的作用是编排输入事件、动画事件和绘制事件的执行,它的postCallback方法的作用就是将要执行的事件放入内部的一个队列中,最后会执行传入的Runnable,这里也就是mTraversalRunnable。
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true
- 来看看TraversalRunnable这个类做了什么?
- 可以看到最终调用了performTraversals()方法进行绘制
final class TraversalRunnable implements Runnable { @Override public void run() { doTraversal(); } } final TraversalRunnable mTraversalRunnable = new TraversalRunnable(); void doTraversal() { if (mTraversalScheduled) { mTraversalScheduled = false; mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); if (mProfile) { Debug.startMethodTracing("ViewAncestor"); } performTraversals(); if (mProfile) { Debug.stopMethodTracing(); mProfile = false; } } }
- 大概总结一下
- invalidate方法最终调用的是ViewRootImpl中的performTraversals来重新绘制View
- 在自定义View时,当需要刷新View时,如果是在UI线程中,那就直接调用invalidate方法,如果是在非UI线程中,那就通过postInvalidate方法来刷新View
03.postInvalidate深入分析
- 先来看看View中的postInvalidate方法
@UiThread
public class View implements Drawable.Callback, KeyEvent.Callback,AccessibilityEventSource { ... public void postInvalidate() { postInvalidateDelayed(0); } public void postInvalidate(int left, int top, int right, int bottom) { postInvalidateDelayed(0, left, top, right, bottom); } public void postInvalidateDelayed(long delayMilliseconds) { final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { attachInfo.mViewRootImpl.dispatchInvalidateDelayed(this, delayMilliseconds); } } ... }
- 可以看到,postInvalidate方法最后调用了ViewRootImpl的dispatchInvalidateDelayed方法
- ViewRootImpl中的dispatchInvalidateDelayed方法就是像ViewRootHandler发送了一个MSG_INVALIDATE消息,ViewRootHandler是ViewRootImpl中的一个内部Handler类
- 大概总结一下
- postInvalidate方法调用了ViewRootImpl中的dispatchInvalidateDelayed方法向ViewRootImpl中的ViewRootHandler发送一个消息,最后调用的还是View的invalidate方法。
- 因为ViewRootImpl是在UI线程的,所以postInvalidate方法的作用就是将非UI线程的刷新操作切换到UI线程,以便在UI线程中调用invalidate方法刷新View。所以如果我们自定义的View本身就是在UI线程,没有用到多线程的话,直接用invalidate方法来刷新View就可以了。而我们平时自定义View基本上都没有开起其他线程,所以这就是我们很少接触postInvalidate方法的原因。
- 一句话总结postInvalidate方法的作用就是:实现了消息机制,可以使我们在非UI线程也能调用刷新View的方法。
04.requestLayout深入分析
- 源码如下所示
- 在View的requestLayout方法中,首先会设置View的标记位,PFLAG_FORCE_LAYOUT表示当前View要进行重新布局,PFLAG_INVALIDATED表示要进行重新绘制。
- requestLayout方法中会一层层向上调用父布局的requestLayout方法,设置PFLAG_FORCE_LAYOUT标记,最终调用的是ViewRootImpl中的requestLayout方法。
- 然后看看ViewRootImpl中的requestLayout方法
- 可以看到ViewRootImpl中的requestLayout方法中会调用scheduleTraversals方法,scheduleTraversals方法最后会调用performTraversals方法开始执行View的三大流程,会分别调用View的measure、layout、draw方法。
@Override
public void requestLayout() { if (!mHandlingLayoutInLayoutRequest) { checkThread(); mLayoutRequested = true; scheduleTraversals(); } }
- 然后再看看measure测量方法
- 由于requestLayout方法设置了PFLAG_FORCE_LAYOUT标记位,所以measure方法就会调用onMeasure方法对View进行重新测量。在measure方法中最后设置了PFLAG_LAYOUT_REQUIRED标记位,这样在layout方法中就会执行onLayout方法进行布局流程。
public final void measure(int widthMeasureSpec, int heightMeasureSpec) { final boolean forceLayout = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT; if (forceLayout || needsLayout) {
- 再然后看看layout方法
- 由于measure方法中设置了PFLAG_LAYOUT_REQUIRED标记位,所以在layout方法中onLayout方法会被调用执行布局流程。最后清除PFLAG_FORCE_LAYOUT和PFLAG_LAYOUT_REQUIRED标记位。
public void layout(int l, int t, int r, int b) {
05.ViewRootImpl作用分析
- 链接WindowManager和DecorView的纽带,另外View的绘制也是通过ViewRootImpl来完成的。
- 它的主要作用我的总结为如下:
- A:链接WindowManager和DecorView的纽带,更广一点可以说是Window和View之间的纽带。
- B:完成View的绘制过程,包括measure、layout、draw过程。
- C:向DecorView分发收到的用户发起的event事件,如按键,触屏等事件。
06.这几个方法总结
- requestLayout方法会标记PFLAG_FORCE_LAYOUT,然后一层层往上调用父布局的requestLayout方法并标记PFLAG_FORCE_LAYOUT,最后调用ViewRootImpl中的requestLayout方法开始View的三大流程,然后被标记的View就会进行测量、布局和绘制流程,调用的方法为onMeasure、onLayout和onDraw。
- invalidate方法我们分析过,它的过程和requestLayout方法方法很像,但是invalidate方法没有标记PFLAG_FORCE_LAYOUT,所以不会执行测量和布局流程,而只是对需要重绘的View进行重绘,也就是只会调用onDraw方法,不会调用onMeasure和onLayout方法。
其他介绍
01.关于博客汇总链接
02.关于我的博客
转载自www.cnblogs.com/yc211/p/10582656.html