如果你在界面中使用了RecyclerView,并且添加了上拉加载和下拉刷新的功能的话,一定对这个异常不会陌生。因为这个异常就时常发生在刷新清除数据的时候,刚好上拉加载的也调用了notifyDataSetChange();
然后就会跑出如下异常:
java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 6(offset:6).state:9
at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java)
at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java)
at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java)
at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java)
at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java)
at android.support.v7.widget.LinearLayoutManager.scrollBy(LinearLayoutManager.java)
at android.support.v7.widget.LinearLayoutManager.scrollVerticallyBy(LinearLayoutManager.java)
at android.support.v7.widget.RecyclerView.scrollByInternal(RecyclerView.java)
at android.support.v7.widget.RecyclerView.onTouchEvent(RecyclerView.java)
at android.view.View.dispatchTouchEvent(View.java:7706)
所以,要怎么避免这个bug呢?这就需要把上拉加载和下拉刷新区分开来,让着两个不再同一时间发生。但是我们没有办法去阻止用户去做这样的操作,所以只能在代码上下功夫。我们举个例子
public class FragmentKaifuTodayAndTomorrow extends BaseFragment implements OnRefreshListener, ItemKaiFuKaiCeFragmentLayout.OnClickRemindListener, UserManager.IOnLogin, UserManager.IOnLoginOut { private SwipeRefreshLayout mSwipeRefreshLayout; private RecyclerView mRecyclerView; private AdapterKaiFuKaiCeTodayAndTomorrow mAdapter; private LinearLayoutManager mLinearLayoutManager; private ArrayList<KaiFuKaiCeBean> mDatas = new ArrayList<KaiFuKaiCeBean>(); private ProtocolHomeKaiFuKaiCe mProtocolHomeKaiFuKaiCe;//获取数据 private FootView mFootView; private boolean mIsLoadingMore;//是否在加载更多数据 //下拉加载更多数据时候的网络请求 private void getMoreData() { mIsLoadingMore = true;//开始请求的时候就置true标志正在加载 mProtocolHomeKaiFuKaiCe = new ProtocolHomeKaiFuKaiCe(getActivity(), mDatas.size(), Constants.SIZE, mType, new ProtocolBase.IProtocolListener() { @Override public void onSuccess(Object resultData) { if (!getActivity().isFinishing()) { mFootView.invisible(); int start = mDatas.size(); if (mProtocolHomeKaiFuKaiCe.mDatas.size() > 0) { mDatas.addAll(mProtocolHomeKaiFuKaiCe.mDatas); mAdapter.notifyItemRangeInserted(start, mProtocolHomeKaiFuKaiCe.mDatas.size()); } else { mFootView.hide(); ToastUtils.showLongToast(getActivity(), "没有更多数据"); } mProtocolHomeKaiFuKaiCe = null; mIsLoadingMore = false;//请求更多数据成功了,并且notifyItemRangeInserted之后就置false标识已
//经添加完数据 } } @Override public void onFailure(int state,boolean hasCache, String errMsg) { if (!getActivity().isFinishing()) { ToastUtils.showLongToast(getActivity(), errMsg); mIsLoadingMore = false;//请求数据失败了,也要更改加载标识 } } }); mProtocolHomeKaiFuKaiCe.postRequest(); } @Override public void onRefresh() { if(mIsLoadingMore){//加载更多数据的时候不执行刷新数据请求 mSwipeRefreshLayout.setRefreshing(false); return; } loadData(getContext());//刷新数据 } @Override protected void initViews(View convertView) { mSwipeRefreshLayout = (SwipeRefreshLayout) convertView.findViewById(R.id.activity_kaifukaice_layout_refresh); mSwipeRefreshLayout.setColorSchemeResources(android.R.color.holo_green_light, android.R.color.holo_blue_bright, android.R.color.holo_orange_light, android.R.color.holo_red_light); mRecyclerView = (RecyclerView) convertView.findViewById(R.id.activity_kaifukaice_layout_recyclerview); mLinearLayoutManager = new LinearLayoutManager(getContext()); mRecyclerView.setLayoutManager(mLinearLayoutManager); mFootView = new FootView(getContext(), mRecyclerView); mAdapter = new AdapterKaiFuKaiCeTodayAndTomorrow(getContext(), mDatas,mType); mAdapter.setFooterView(mFootView.getView()); mRecyclerView.setAdapter(mAdapter); mSwipeRefreshLayout.setOnRefreshListener(this); mAdapter.setOnClickRemindListener(this); mRecyclerView.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { return null != mSwipeRefreshLayout && mSwipeRefreshLayout.isRefreshing();//刷新的时候不加载更多数据,刷新的时候控制列表不可滑动 } }); } }
这样就实现了对上拉加载滚动和下拉刷新数据请求的控制,使他们不会在同一时间发生,从而避免的异常。