关于android:Android中的ViewTreeObserver分析二

4次阅读

共计 11726 个字符,预计需要花费 30 分钟才能阅读完成。

PS:本文系转载文章,浏览原文可读性会更好,文章开端有原文链接

ps:源码是基于 android api 27 来剖析的

咱们在上一篇文章 Android 中的 ViewTreeObserver 剖析 (一) 列举了一些常见的 ViewTreeObserver 外部接口的作用以及剖析了 OnWindowAttachListener 和 OnWindowFocusChangeListener 接口触发机会的过程,这篇文章持续剖析 Android 中的 ViewTreeObserver 剖析 (一) 里列举进去的其余接口的触发工夫的过程。

1、OnGlobalFocusChangeListener 的第一次触发机会

OnGlobalFocusChangeListener 的第一次触发机会是在 View 的布局实现之后 View 的绘制之前进行的,View 的测量、布局和绘制的入口跟踪能够看对 Android 中 View 的 post 办法进行摸索这篇文章;咱们晓得 View 的测量、布局和绘制的入口会调用 ViewRootImpl 的 performTraversals 办法;

private void performTraversals() {

    ......
    if (didLayout) {
        //1、performLayout(lp, mWidth, mHeight);
        ......
    }
    ......
    if (mFirst && sAlwaysAssignFocus) {
        ......
        if (mView != null) {if (!mView.hasFocus()) {
                //2、mView.restoreDefaultFocus();
                ......
            } else {......}
        }
    }
    ......
    if (!cancelDraw && !newSurface) {
        ......
        //3、performDraw();} else {......}
    ......

}

正文 1 中的 performLayout 办法是执行 View 的布局流程;正文 3 中的 performDraw 办法是执行 View 的绘制流程;正文 2 中的 mView 是 DecorView 对象,它的 restoreDefaultFocus 办法是在它的父类的父类中实现,也就是 ViewGroup,咱们看一下 ViewGroup 的 restoreDefaultFocus 办法;

@Override
public boolean restoreDefaultFocus() {
    ......
    //4、return super.restoreDefaultFocus();}

从正文 4 看出,ViewGroup 的 restoreDefaultFocus 办法调用了父类的 restoreDefaultFocus 办法,咱们去看 View 的 restoreDefaultFocus 办法;

public boolean restoreDefaultFocus() {
    //5、return requestFocus(View.FOCUS_DOWN);
}

看正文 5,View 的 restoreDefaultFocus 办法调用了 View 的 requestFocus(int direction)办法;

public final boolean requestFocus(int direction) {
    //6、return requestFocus(direction, null);
}

看正文 6,View 的 requestFocus(int direction)办法调用了 requestFocus(int direction, Rect previouslyFocusedRect) 办法,它调用的不是 View 的,而是 ViewGroup 的,因为 ViewGroup 重写了 requestFocus(int direction, Rect previouslyFocusedRect) 办法;

@Override
public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
    ......
    switch (descendantFocusability) {
        //7、case FOCUS_BLOCK_DESCENDANTS:
            return super.requestFocus(direction, previouslyFocusedRect);
            
            //8、case FOCUS_BEFORE_DESCENDANTS: {final boolean took = super.requestFocus(direction, previouslyFocusedRect);
            return took ? took : onRequestFocusInDescendants(direction, previouslyFocusedRect);
        }
        
        //9、case FOCUS_AFTER_DESCENDANTS: {final boolean took = onRequestFocusInDescendants(direction, previouslyFocusedRect);
            return took ? took : super.requestFocus(direction, previouslyFocusedRect);
        }
        ......
    }
}

正文 7 示意拦挡焦点,不论以后 View 是否被聚焦,子 View 肯定获取不到焦点;正文 8 示意在子 View 之前判断是否应被聚焦,如果为 false 则会去判断其子 View;正文 9 示意在子 View 判断焦点之后判断;咱们以正文 7 下的代码为案例,看 super.requestFocus(direction, previouslyFocusedRect) 的具体实现,也就是 View 的 requestFocus(direction, previouslyFocusedRect)办法;

public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
    //10、return requestFocusNoSearch(direction, previouslyFocusedRect);
}

看正文 10,View 的 requestFocus(direction, previouslyFocusedRect)办法调用了 View 的 requestFocusNoSearch(int direction, Rect previouslyFocusedRect)办法;

private boolean requestFocusNoSearch(int direction, Rect previouslyFocusedRect) {
    ......
    //11、handleFocusGainInternal(direction, previouslyFocusedRect);
    return true;
}

看正文 11,View 的 requestFocusNoSearch(int direction, Rect previouslyFocusedRect)办法调用了 View 的 handleFocusGainInternal(@FocusRealDirection int direction, Rect previouslyFocusedRect) 办法;

void handleFocusGainInternal(@FocusRealDirection int direction, Rect previouslyFocusedRect) {

    ......
    if ((mPrivateFlags & PFLAG_FOCUSED) == 0) {
        ......
        if (mAttachInfo != null) {
            //12、mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus, this);
        }
        ......
    }

}

看正文 12,mAttachInfo.mTreeObserver 是 ViewTreeObserver 类型的对象,View 的 handleFocusGainInternal(@FocusRealDirection int direction, Rect previouslyFocusedRect) 办法调用了 ViewTreeObserver 的 dispatchOnGlobalFocusChange(View oldFocus, View newFocus) 办法;

final void dispatchOnGlobalFocusChange(View oldFocus, View newFocus) {

    // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
    // perform the dispatching. The iterator is a safe guard against listeners that
    // could mutate the list by calling the various add/remove methods. This prevents
    // the array from being modified while we iterate it.
    final CopyOnWriteArrayList<ViewTreeObserver.OnGlobalFocusChangeListener> listeners = mOnGlobalFocusListeners;
    if (listeners != null && listeners.size() > 0) {for (ViewTreeObserver.OnGlobalFocusChangeListener listener : listeners) {
            //13、listener.onGlobalFocusChanged(oldFocus, newFocus);
        }
    }

}

看正文 13,ViewTreeObserver 的 dispatchOnGlobalFocusChange(View oldFocus, View newFocus) 办法调用了 OnGlobalFocusChangeListener 接口的 onGlobalFocusChanged(View oldFocus, View newFocus) 办法;所以从以上代码的跟踪过程能够得出 OnGlobalFocusChangeListener 的第一次触发机会是在 View 的布局实现之后 View 的绘制之前进行的。

2、OnGlobalLayoutListener 第一次触发机会

OnGlobalLayoutListener 第一次触发机会也是在 View 的布局实现之后 View 的绘制之前进行的,View 的测量、布局和绘制的入口跟踪能够看对 Android 中 View 的 post 办法进行摸索这篇文章;咱们晓得 View 的测量、布局和绘制的入口会调用 ViewRootImpl 的 performTraversals 办法;

private void performTraversals() {

    ......
    if (didLayout) {
        //14、performLayout(lp, mWidth, mHeight);
        ......
    }

    if (triggerGlobalLayoutListener) {
        mAttachInfo.mRecomputeGlobalAttributes = false;
        //15、mAttachInfo.mTreeObserver.dispatchOnGlobalLayout();}
    ......
    if (!cancelDraw && !newSurface) {
        ......
        //16、performDraw();} else {......}
    mIsInTraversal = false;

}

正文 14 中的 performLayout 办法是执行 View 的布局流程;正文 16 中的 performDraw 办法是执行 View 的绘制流程;正文 15 中的 mAttachInfo.mTreeObserver 是 ViewTreeObserver 类型的对象,ViewRootImpl 的 performTraversals 办法调用了 ViewTreeObserver 的 dispatchOnGlobalLayout 办法;

public final void dispatchOnGlobalLayout() {

    // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
    // perform the dispatching. The iterator is a safe guard against listeners that
    // could mutate the list by calling the various add/remove methods. This prevents
    // the array from being modified while we iterate it.
    final CopyOnWriteArray<ViewTreeObserver.OnGlobalLayoutListener> listeners = mOnGlobalLayoutListeners;
    if (listeners != null && listeners.size() > 0) {CopyOnWriteArray.Access<ViewTreeObserver.OnGlobalLayoutListener> access = listeners.start();
        try {int count = access.size();
            for (int i = 0; i < count; i++) {
                //17、access.get(i).onGlobalLayout();}
        } finally {listeners.end();
        }
    }

}

看正文 17,access.get(i) 示意的是 OnGlobalLayoutListener 接口对象,所以正文 17 这里回调了 OnGlobalLayoutListener 接口的 onGlobalLayout 办法,所以 OnGlobalLayoutListener 第一次触发机会也是在 View 的布局实现之后 View 的绘制之前进行的。

3、OnPreDrawListener 第一次触发机会

OnPreDrawListener 第一次触发机会是在 View 的布局实现之后 View 的绘制之前进行的,咱们晓得 View 的测量、布局和绘制入口会调用 ViewRootImpl 的 performTraversals 办法;

private void performTraversals() {

    ......
    if (didLayout) {
        //18、performLayout(lp, mWidth, mHeight);
        ......
    }
    ......
    //19、boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible;
    if (!cancelDraw && !newSurface) {
        ......
        //20、performDraw();} else {if (isViewVisible) {
            // Try again
            //21、scheduleTraversals();} else if (mPendingTransitions != null && mPendingTransitions.size() > 0) {......}
    }

    mIsInTraversal = false;

}

正文 18 中的 performLayout 办法是执行 View 的布局流程;正文 20 中的 performDraw 办法是执行 View 的绘制流程;当正文 19 中的 cancelDraw 为 true 时,isViewVisible 变量也为 true 时,就会调用正文 21 中的 scheduleTraversals 办法,scheduleTraversals 办法还是会间接调用到 ViewRootImpl 的 performTraversals 办法;cancelDraw 和 isViewVisible 变量都为 true,可能拦挡正文 20 中的 performDraw 办法,从新发动测量、布局和绘制流程,也就是会间接调用 ViewRootImpl 的 performTraversals 办法;正文 19 中的 mAttachInfo.mTreeObserver 是 ViewTreeObserver 类型的对象,咱们且看正文 19 中的 ViewTreeObserver 中的 dispatchOnPreDraw 办法;

public final boolean dispatchOnPreDraw() {

    boolean cancelDraw = false;
    final CopyOnWriteArray<ViewTreeObserver.OnPreDrawListener> listeners = mOnPreDrawListeners;
    if (listeners != null && listeners.size() > 0) {CopyOnWriteArray.Access<ViewTreeObserver.OnPreDrawListener> access = listeners.start();
        try {int count = access.size();
            for (int i = 0; i < count; i++) {
                //22、cancelDraw |= !(access.get(i).onPreDraw());
            }
        } finally {listeners.end();
        }
    }
    return cancelDraw;

}

看到正文 22 没,access.get(i) 是 OnPreDrawListener 接口,ViewTreeObserver 中的 dispatchOnPreDraw 办法最终调用了 OnPreDrawListener 的 onPreDraw 办法;所以 OnPreDrawListener 第一次触发机会是在 View 的布局实现之后 View 的绘制之前进行的。

4、OnDrawListener 第一次触发机会

OnDrawListener 第一次触发机会是在 View 的绘制过程,咱们先从 ViewRootImpl 的 performTraversals 办法看起;

private void performTraversals() {

    ......
    if (!cancelDraw && !newSurface) {
        ......
        //23、performDraw();} else {......}
    mIsInTraversal = false;

}

看正文 23,performDraw 办法是进行 View 的绘制流程,performTraversals 办法调用了 ViewRootImpl 的 performDraw 办法;

private void performDraw() {

    ......
    try {
        //24、draw(fullRedrawNeeded);
    } finally {......}
    ......

}

看正文 24,performDraw 办法调用了 ViewRootImpl 的 draw(boolean fullRedrawNeeded) 办法;

private void draw(boolean fullRedrawNeeded) {

    ......
    //25、mAttachInfo.mTreeObserver.dispatchOnDraw();
    ......

}

看正文 25,mAttachInfo.mTreeObserver 是 ViewTreeObserver 类型的对象,ViewRootImpl 的 draw(boolean fullRedrawNeeded) 办法调用了 ViewTreeObserver 的 dispatchOnDraw 办法;

public final void dispatchOnDraw() {

    if (mOnDrawListeners != null) {
        mInDispatchOnDraw = true;
        final ArrayList<ViewTreeObserver.OnDrawListener> listeners = mOnDrawListeners;
        int numListeners = listeners.size();
        for (int i = 0; i < numListeners; ++i) {
            //26、listeners.get(i).onDraw();}
        mInDispatchOnDraw = false;
    }

}

看正文 26,listeners.get(i) 是 OnDrawListener 接口,ViewTreeObserver 的 dispatchOnDraw 办法调用了 OnDrawListener 的 onDraw 办法,OnDrawListener 的 onDraw 办法告诉 View 开始绘制,所以 OnDrawListener 第一次触发机会是在 View 的绘制过程。

5、OnTouchModeChangeListener 第一次触发机会

OnTouchModeChangeListener 第一次触发机会是在 View 的绘制完之后,OnTouchModeChangeListener 第一次触发机会是跟 OnWindowFocusChangeListener 的触发机会的流程大部分是一样的,OnWindowFocusChangeListener 的触发机会能够看 Android 中的 ViewTreeObserver 剖析 (一) 这篇文章,从 Android 中的 ViewTreeObserver 剖析 (一) 这篇文章能够看出 OnWindowFocusChangeListener 的触发机会的过程中会调用 ViewRootImpl 的外部类 ViewRootHandler 的 case MSG_WINDOW_FOCUS_CHANGED 的代码;

final class ViewRootHandler extends Handler {

    ......
    @Override
    public void handleMessage(Message msg) {switch (msg.what) {
            ......
            case MSG_WINDOW_FOCUS_CHANGED: {if (mAdded) {
                    ......
                    if (hasWindowFocus) {
                        boolean inTouchMode = msg.arg2 != 0;
                        //28、ensureTouchModeLocally(inTouchMode);
                        ......
                    }
                    ......
                    if (mView != null) {
                        ......
                        //29、mAttachInfo.mTreeObserver.dispatchOnWindowFocusChange(hasWindowFocus);
                        ......
                    }
                    ......
                }
            } break;
            ......
        }
    }

}

正文 29 的代码最终会调用 OnWindowFocusChangeListener 接口;看正文 28 处的代码,ViewRootHandler 的 case MSG_WINDOW_FOCUS_CHANGED 的代码又调用了 ViewRootImpl 的 ensureTouchModeLocally(boolean inTouchMode) 办法;

private boolean ensureTouchModeLocally(boolean inTouchMode) {

    ......
    //30、mAttachInfo.mTreeObserver.dispatchOnTouchModeChanged(inTouchMode);

    return (inTouchMode) ? enterTouchMode() : leaveTouchMode();

}

看正文 30 的代码,mAttachInfo.mTreeObserver 是 ViewTreeObserver 类型的对象,ViewRootImpl 的 ensureTouchModeLocally(boolean inTouchMode) 办法调用了 ViewTreeObserver 的 dispatchOnTouchModeChanged(boolean inTouchMode) 办法;

final void dispatchOnTouchModeChanged(boolean inTouchMode) {

    final CopyOnWriteArrayList<ViewTreeObserver.OnTouchModeChangeListener> listeners =
            mOnTouchModeChangeListeners;
    if (listeners != null && listeners.size() > 0) {for (ViewTreeObserver.OnTouchModeChangeListener listener : listeners) {
            //31、listener.onTouchModeChanged(inTouchMode);
        }
    }

}

看正文 31,listener 是 OnTouchModeChangeListener 接口,ViewTreeObserver 的 dispatchOnTouchModeChanged(boolean inTouchMode) 办法调用了 OnTouchModeChangeListener 的 onTouchModeChanged(boolean isInTouchMode) 办法;从以上代码跟踪来看,所以 OnTouchModeChangeListener 第一次触发机会是在 View 的绘制完之后。

6、OnScrollChangedListener 第一次触发机会

OnScrollChangedListener 第一次触发机会是 View 的绘制过程,它的触发过程有一部分跟 OnDrawListener 第一次触发机会(小标题 4 的内容)一样(ViewRootImpl.performTraversals()->ViewRootImpl.performDraw()->ViewRootImpl.draw(boolean fullRedrawNeeded)),都会被 ViewRootImpl 的 draw(boolean fullRedrawNeeded) 办法被调用;

private void draw(boolean fullRedrawNeeded) {

    ......
    if (mAttachInfo.mViewScrollChanged) {
        mAttachInfo.mViewScrollChanged = false;
        //32、mAttachInfo.mTreeObserver.dispatchOnScrollChanged();}
    ......
    //33、mAttachInfo.mTreeObserver.dispatchOnDraw();
    ......

}

正文 33 的代码最终会调用 OnDrawListener 接口的办法;看正文 32,mAttachInfo.mTreeObserver 是 ViewTreeObserver 类型的对象,ViewRootImpl 的 draw(boolean fullRedrawNeeded) 办法也会调用到 ViewTreeObserver 的 dispatchOnScrollChanged 办法;

final void dispatchOnScrollChanged() {

    // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
    // perform the dispatching. The iterator is a safe guard against listeners that
    // could mutate the list by calling the various add/remove methods. This prevents
    // the array from being modified while we iterate it.
    final CopyOnWriteArray<ViewTreeObserver.OnScrollChangedListener> listeners = mOnScrollChangedListeners;
    if (listeners != null && listeners.size() > 0) {CopyOnWriteArray.Access<ViewTreeObserver.OnScrollChangedListener> access = listeners.start();
        try {int count = access.size();
            for (int i = 0; i < count; i++) {
                //34、access.get(i).onScrollChanged();}
        } finally {listeners.end();
        }
    }

}

看正文 34,access.get(i) 是 OnScrollChangedListener 接口,ViewTreeObserver 的 dispatchOnScrollChanged 办法调用了 OnScrollChangedListener 的 onScrollChanged 办法,所以从下面代码跟踪得出,OnScrollChangedListener 第一次触发机会是 View 的绘制过程。

正文完
 0