前提
这个问题是在做项目的时候发现的,当然并非我第一个发现,而是看到了一些网上的信息后,发现很多都没有说清楚到底是什么导致的此问题,而只是有一些解决方法,也就是知其所以然,而不知其所以然。觉得有必要留下一个TAG,以便后面忘记了,还可以找到解决方法。当然,如果知道问题是什么了,由什么引起的后,也应该会知道怎么解决。
场景
在使用RecyclerView
时,我们都会搭配SwipeRefreshLayout
来做下拉刷新,而问题点就在这个下拉刷新的地方,我们下拉刷新一般会先清空适配器中的数据,然后再去请求数据进行填充,问题就发生在这个当我们清空数据后,这时我们进行了快速滑动,此时会抛出异常,如下所示:
由图上可以看出这个问题是RecyclerView
内部抛出来的,也就是说这可能是RecyclerView
的内部bug导致的,不管那么多了,现在问题我们已经清楚了,就是由于下拉刷新,清空数据后,此时页面刚好在滑动,此时就会抛出异常。
解决方法
问题点清楚以后,我们来分析其解决方法,这里应该有两个方面来进行分析,第一个方面就是下拉刷新时是否可以将数据不要清空;另一个方面是下拉刷新时当数据没有加载完成时,不要进行滑动。思路有了之后,我们就从这两个方面来分别考虑其可实现的可能。
1.下拉刷新时不清空数据
我个人想到的是这种方法,在我对RecyclerView.Adapter
进行封装的RecyclerViewBaseAdapter
里就是采用此方法来处理数据的。
public void addData(List<D> items) {
boolean isChangeData = items.removeAll(mData);
if (items.size() == 0) {
//走到这里,说明加载的数据全部为重复数据,所以数据没有变化
if (mProgressBarView != null) {
mProgressBarView.setVisibility(View.GONE);
mNotMoreDataView.setVisibility(View.GONE);
}
}
mData.addAll(items);
notifyDataSetChanged();
这里就是将每次添加到适配器中的数据先使用removeAll()
去除重复项,然后再添加到数据集合中,当然,这里我们数据集合里往往会存放对象,而对象之间比较是否相等时,我们需要将此对象实现hashCode()
和equeal()
两个方法。
在这里处理下拉刷新时,还需要注意这样处理,就是当是下拉刷新时,需要将数据添加到数据集合的前面,而不是尾部。这里我们需要对下拉刷新做一个判断:
@Override
public void setData(List list) {
errorString = null;
list.removeAll(datas);
if (isDownRefresh) {
//是否是下拉刷新
datas.addAll(0, list);
} else {
datas.addAll(list);
}
notifyDataSetChanged();
}
当是下拉刷新时,我们将请求的数据添加到position为0的位置。这样就大概可以解决我们上面抛异常的问题了,当然这里如果下拉刷新时,新数据超过一页的时候,数据的显示,也就是第二页数据显示的位置,可能还需要在这个setData()
方法里根据list.reomve(datas)
返回的是否是boolean值还再进行插入数据集合的处理了。
2.当下拉刷新时,静止滑动
这个方法呢,体验就没那么好了,如果数据没加载完成时,不让滑动?数据请求比较快,还好一点,如果比较慢的话,会给用户造成app已死的假象,因为不能动了嘛。当然,如果没有什么好的解决方法时,也只能这么勉强先应付着吧。
mRecyclerView.setOnTouchListener(
new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (mIsRefreshing) {
return true;
} else {
return false;
}
}
}
);
当刷新时设将mIsRefreshing
设置为true,拦截滑动事件,而当刷新完毕后,再将mIsRefreshing
设置为false.
好了,使用上面两种方法都是可以解决抛异常的,但我个人还是倾向于使用第一种方法。