在拿到布局后,有时需要给ViewTreeObserver
注册一些监听器。今天就碰到一个注册OnDrawListener
时发现的系统bug,记录一下。
问题描述
在设置布局之后,立即设置OnDrawListener
代码很简单:
textView.getViewTreeObserver().addOnDrawListener(new ViewTreeObserver.OnDrawListener() {
@Override
public void onDraw() {
}
});
api26没有问题,api26以下竟然不会回调!!
源码排查
首先看下View#getViewTreeObserver
:
public ViewTreeObserver getViewTreeObserver() {
if (mAttachInfo != null) {
return mAttachInfo.mTreeObserver;
}
if (mFloatingTreeObserver == null) {
mFloatingTreeObserver = new ViewTreeObserver(mContext);
}
return mFloatingTreeObserver;
}
刚得到View时,还没有mAttachInfo
,所以此时返回mFloatingTreeObserver
。随后在dispatchAttachedToWindow
中:
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
mAttachInfo = info;
...
if (mFloatingTreeObserver != null) {
info.mTreeObserver.merge(mFloatingTreeObserver);
mFloatingTreeObserver = null;
}
...
}
注意这个merge方法,问题就出在这里。
看下api25的源码吧:
void merge(ViewTreeObserver observer) {
if (observer.mOnWindowAttachListeners != null) {
if (mOnWindowAttachListeners != null) {
mOnWindowAttachListeners.addAll(observer.mOnWindowAttachListeners);
} else {
mOnWindowAttachListeners = observer.mOnWindowAttachListeners;
}
}
if (observer.mOnWindowFocusListeners != null) {
if (mOnWindowFocusListeners != null) {
mOnWindowFocusListeners.addAll(observer.mOnWindowFocusListeners);
} else {
mOnWindowFocusListeners = observer.mOnWindowFocusListeners;
}
}
if (observer.mOnGlobalFocusListeners != null) {
if (mOnGlobalFocusListeners != null) {
mOnGlobalFocusListeners.addAll(observer.mOnGlobalFocusListeners);
} else {
mOnGlobalFocusListeners = observer.mOnGlobalFocusListeners;
}
}
if (observer.mOnGlobalLayoutListeners != null) {
if (mOnGlobalLayoutListeners != null) {
mOnGlobalLayoutListeners.addAll(observer.mOnGlobalLayoutListeners);
} else {
mOnGlobalLayoutListeners = observer.mOnGlobalLayoutListeners;
}
}
if (observer.mOnPreDrawListeners != null) {
if (mOnPreDrawListeners != null) {
mOnPreDrawListeners.addAll(observer.mOnPreDrawListeners);
} else {
mOnPreDrawListeners = observer.mOnPreDrawListeners;
}
}
if (observer.mOnTouchModeChangeListeners != null) {
if (mOnTouchModeChangeListeners != null) {
mOnTouchModeChangeListeners.addAll(observer.mOnTouchModeChangeListeners);
} else {
mOnTouchModeChangeListeners = observer.mOnTouchModeChangeListeners;
}
}
if (observer.mOnComputeInternalInsetsListeners != null) {
if (mOnComputeInternalInsetsListeners != null) {
mOnComputeInternalInsetsListeners.addAll(observer.mOnComputeInternalInsetsListeners);
} else {
mOnComputeInternalInsetsListeners = observer.mOnComputeInternalInsetsListeners;
}
}
if (observer.mOnScrollChangedListeners != null) {
if (mOnScrollChangedListeners != null) {
mOnScrollChangedListeners.addAll(observer.mOnScrollChangedListeners);
} else {
mOnScrollChangedListeners = observer.mOnScrollChangedListeners;
}
}
if (observer.mOnWindowShownListeners != null) {
if (mOnWindowShownListeners != null) {
mOnWindowShownListeners.addAll(observer.mOnWindowShownListeners);
} else {
mOnWindowShownListeners = observer.mOnWindowShownListeners;
}
}
observer.kill();
}
其他的监听器都merge了,唯独漏了OnDrawListener
。不得不说这是一个悲伤的故事。。
修补措施
要解决这个问题,可以通过view.post
去注册,因为这时,mAttachInfo
已经有值了。
网友评论