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

Posted 薛瑄

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了调用removeViewInternalremoveView 屏幕还是显示被删除界面 的解决方法和源码分析相关的知识,希望对你有一定的参考价值。

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

以上是关于调用removeViewInternalremoveView 屏幕还是显示被删除界面 的解决方法和源码分析的主要内容,如果未能解决你的问题,请参考以下文章

java三种调用方式(同步调用/回调/异步调用)

LINUX系统调用

引用调用 vs 复制调用调用

RPC 调用和 HTTP 调用的区别

js方法调用

深入理解Java虚拟机——方法调用(解析调用)