android一个view被remove后还能被看到的神奇bug
这是一个神奇的bug(巨大的坑)
最近使用工友封装的一个list控件,发现在下拉刷新失败时会发生两个view重叠的现象,大概是这样:
一开始以为很简单,就是两个view同时显示出来了。瞬间感觉这位工友封装的控件居然犯了这么低级的错误。然后信心满满滴去寻找让两个view一起set visible的代码。
然而,追踪发现,那个recyclerView明明已经从父布局中remove掉了的。
通过uiautomatorviewer打开这个页面的view结构(如上图右侧),可以明显看到,这个layout里面只包含了中间那个网络异常的提示view,并没有这个recyclerView。
第一次遇到view已经不存在了,但却依然显示在哪里。瞬间石化。这还怎么查?看来android开发这活真的是没法干了?。
直接从分析结论说
当然肯定经过哥艰苦的debug过程,这个问题产生的根本原因如下。
首先这个recyclerView引用了github上一个比较好用的下拉刷新控件PullToRefreshView (https://github.com/Yalantis/Phoenix)来实现下拉刷新的效果。
<PullToRefreshView
android:id="@+id/list_container">
<RecyclerView
android:id="@+id/recycler_list"/>
</PullToRefreshView>
这个问题就出在这个控件的下拉刷新这个实现方式,是用的比较老旧的补间动画。而我们这个项目因为要做一个刷新时如果出现失败就隐藏list,并展示一个失败提示view如上图。然后我的工友使用的办法是在外面在套一层layout:
<RelativeLayout
......>
<PullToRefreshView
android:id="@+id/list_container">
<RecyclerView
android:id="@+id/recycler_list"
/>
</PullToRefreshView>
<LinearLayout
android:id="@+id/load_error_view">
<......>
</LinearLayout>
</RelativeLayout>
正常情况,显示list。加载失败时从父layout里remove掉这个PullToRefreshView,add进来那个load_error_view。
就这样,因为PullToRefreshView在加载失败时会从下拉状态向上弹回去,这个动画效果是用Animation补间动画来完成的。
虽然向上弹回之前,就已经调用parentLayout.remove(pullToRefreshView)把这个view移除了。但可能这个动画过程还是要在canvas上绘制,就盖在了load_error_view上面了。
这个是PullToRefreshView弹回动画的代码:
private void animateOffsetToStartPosition() {
mFrom = mCurrentOffsetTop;
mFromDragPercent = mCurrentDragPercent;
long animationDuration = Math.abs((long) (MAX_OFFSET_ANIMATION_DURATION * mFromDragPercent));
mAnimateToStartPosition.reset();
mAnimateToStartPosition.setDuration(animationDuration);
mAnimateToStartPosition.setInterpolator(mDecelerateInterpolator);
mAnimateToStartPosition.setAnimationListener(mToStartListener);
mRefreshView.clearAnimation();
mRefreshView.startAnimation(mAnimateToStartPosition);
}
解决办法
找到问题的根源后,有以下解决思路:
1.不直接解决。如果已经有内容,重新加载失败只简单toast提示。事实上,这样的行为本身就是bug,必须改掉。
这样问题解决大半,但后来发现还是有其他奇奇怪怪的现象。比如多刷新几次,list里面的view颜色会变深之类的。所以还得从根源解决。
2.就算一开始没有内容,加载失败的基本显示行为,也不用add、remove这种方式,直接用setVisibility。
3.在某些场景确实要隐藏list,显示其他提示的时候,先把list的数据清空,让recycler本来就变得没有数据显示。