Android中的ViewTreeObserver分析(二)

本文系转载文章,阅读原文可读性会更好些,原文链接:https://mp.weixin.qq.com/s/TFCxAFU9VOijZ0HaNDhkiQ

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
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值