RecyclerViewデータのリフレッシュについて

3675 ワード

RecyclerViewを使用してデータの削除または追加を行うと、次のような異常が発生することがあります.
java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionViewHolder{431a7450 position=1 id=-1, oldPos=-1, pLpos:-1 scrap [attachedScrap] tmpDetached no parent}  
    at android.support.v7.widget.RecyclerView$Recycler.validateViewHolderForOffsetPosition(RecyclerView.java:4251)  
    at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4382)  
    at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4363)  
    at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:1961)  
    at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1370)  
    at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1333)  
    at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:562)  
    at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:2900)  
    at android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:3071)  

では、この異常はどのように発生し、どのように解決されるのでしょうか.
RecyclerViewでは、4つの方法でデータをリフレッシュします.1.notifyDataSetChanged();これはListViewの使用方法で、RecyclerViewでも同様に適用されますが、これは公式に推奨されていません.RecyclerViewはListViewに対してローカルリフレッシュインタフェースを提供し、ローカルリフレッシュはアニメーション効果があるためです.2.notifyItemRangeRemoved(); 3.notifyItemRangeInserted(); 4.notifyItemRangeChanged();
データをリフレッシュする後の3つの方法は推奨されますが、うまく使えないとさっきの文章からの異常が発生します.この異常発生の原因を分析する.
以前の個人開発で発生した問題を見てみましょう.
/**
     *      
     */
    public void insertData(List insertedData) {
        if (insertedData == null) {
            Log.e(TAG, "insertData(list) list is null");
            return;
        }
        for (T data : insertedData){
            if (data!=null){
                mDatas.add(data);
            }
        }
        notifyItemRangeInserted(mDatas.size() - insertedData.size(), insertedData.size());
    }

以上のコードでは、insertDataに空のデータがある場合に異常が発生し、空のデータがある場合は、後のnotifyItemRangeInsertedの開始位置と増加したitem数がmDatasの中のものと一致せず、mDatasはadapter内部データとなり、insertDataは外部データとなり、リアルタイムでデータの一致性を保つべきである.
       :
/**
     *      
     */
    public void insertData(List insertedData) {
        if (insertedData == null) {
            Log.e(TAG, "insertData(list) list is null");
            return;
        }
        int index = 0;
        for (T data : insertedData) {
            if (data != null) {
                mDatas.add(data);
                index++;
            }
        }
        notifyItemRangeInserted(mDatas.size() - index, index);
    }

もう一つの問題は、RecyclerViewデータのリフレッシュ操作を「原子」操作に分解し、「原子」操作を3つにし、削除、追加、修正することです.例を挙げて説明します.
public void notifyData(List poiItemList) {
    if (poiItemList != null ) {
        mPoiItems.clear();
        mPoiItems.addAll(poiItemList);
        notifyItemRangeChanged(0, poiItemList.size());
    }
}

上記の操作では、まずデータを削除しましたが、RecyclerViewをリフレッシュしていません.その後、データを追加しました.この場合、RecyclerViewをリフレッシュします.この場合、問題が発生します.このプロセスを2つのステップに分割します.
public void notifyData(List poiItemList) {
    if (poiItemList != null) {
        int previousSize = mPoiItems.size();
        mPoiItems.clear();
        notifyItemRangeRemoved(0, previousSize);
        mPoiItems.addAll(poiItemList);
        notifyItemRangeInserted(0, poiItemList.size());
    }
}

そこで、RecyclerViewのローカルリフレッシュ機能を使用すると、adapterの内部データセットが変更されるたびに、データの一貫性を保つために、データリフレッシュをアクティブに呼び出します.
参考記事:http://www.jianshu.com/p/2eca433869e9