这是在使用Fragmentation时遇到一个问题,在这个库中很多人都遇到这个问题。追求极致体验的我,只好对源码进行修改了。源码的问题,我已经修改了,会在近期开源出来。
下面把这个问题的核心抽取出来,进行分析。
问题:
1、removeViewInternal 后,屏幕上还是显示那个被删除的布局界面
2、虽然屏幕上显示那个被删除的布局界面,但是可以点击事件可以传递到后面的布局上。
3、使用Layout Inspector,分析布局,显示的也是后面的布局,当前屏幕的布局没有任何数据
如下图:
对应上面的第2点,点击到了登录按钮,提示toast
对应上面的第3点
源码分析:
问题的根本原因就是在remove view 的时候,view的动画还没有执行完。
我们从ViewGroup 的函数removeViewInternal 开始分析
/**
* Removes a view during layout. This is useful if in your onLayout() method,
* you need to remove more views.
*
* <p><strong>Note:</strong> do not invoke this method from
* {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
* {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
*
* @param view the view to remove from the group
*/
public void removeViewInLayout(View view) {
removeViewInternal(view);
}
private boolean removeViewInternal(View view) {
//获取参数view 在viewGroup中mChildren(view 数组)的索引位置
final int index = indexOfChild(view);
if (index >= 0) {
//调用函数进行删除操作
removeViewInternal(index, view);
return true;
}
return false;
}
private void removeViewInternal(int index, View view) {
...省略若干代码.....
//判断当前的view 正在播放,或预定播放的动画
if (view.getAnimation() != null ||
(mTransitioningViews != null && mTransitioningViews.contains(view))) {
addDisappearingView(view);
} else if (view.mAttachInfo != null) {
view.dispatchDetachedFromWindow();
}
...省略若干代码.....
}
在removeViewInternal函数中,省略了与这个问题没有关系的代码。
view.getAnimation() 很简单,重点来分析一下mTransitioningViews 是什么意思
// The set of views that are currently being transitioned. This list is used to track views
// being removed that should not actually be removed from the parent yet because they are
// being animated.
private ArrayList<View> mTransitioningViews;
意思就是它是来存储有过渡动画的view的一个数组列表。因为它们已经设置了动画,因此实际上不应该从父视图中删除。
这里的过渡动画指的是布局容器动画(LayoutTransition 官网介绍),通俗的讲,就是在添加、隐藏子view 的时候,有动画效果。
如果有这些动画效果,就会执行addDisappearingView()
/**
* Add a view which is removed from mChildren but still needs animation
*
* @param v View to add
*/
private void addDisappearingView(View v) {
ArrayList<View> disappearingChildren = mDisappearingChildren;
if (disappearingChildren == null) {
disappearingChildren = mDisappearingChildren = new ArrayList<View>();
}
disappearingChildren.add(v);
}
从注释中可以看出,把这些有动画效果的被删除的view,添加到mDisappearingChildren 数组列表中
在下一次调用ViewGroup 的dispatchDraw时,会把mDisappearingChildren 中的view绘制出来
@Override
protected void dispatchDraw(Canvas canvas) {
...省略若干代码.....
// Draw any disappearing views that have animations
if (mDisappearingChildren != null) {
final ArrayList<View> disappearingChildren = mDisappearingChildren;
final int disappearingCount = disappearingChildren.size() - 1;
// Go backwards -- we may delete as animations finish
for (int i = disappearingCount; i >= 0; i--) {
final View child = disappearingChildren.get(i);
//在当前的viewGroup中绘制有动画但是被删除的view
more |= drawChild(canvas, child, drawingTime);
}
}
...省略若干代码.....
}
至此,就知道为什么有些视图remove后,还是会显示出来
解决方法:
知道了原因,解决办法就很简单了,在remove view 之前,确保动画执行完,或者自己手动viewclearAnimation();