调用removeViewInternal、removeView 屏幕还是显示被删除界面 的解决方法和源码分析

这是在使用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();

发布了242 篇原创文章 · 获赞 775 · 访问量 224万+

猜你喜欢

转载自blog.csdn.net/xx326664162/article/details/103616005