Inconsistentie gedetecteerd in RecyclerView, inhoud van RecyclerView wijzigen tijdens het scrollen

Ik gebruik RecyclerViewom de naam van de items weer te geven. Mijn rij bevat één TextView. Itemnamen worden opgeslagen in List<String> mItemList.

Om de inhoud van RecyclerViewte wijzigen, vervang ik Strings in mItemListen roep ik notifyDataSetChanged() op RecyclerViewAdapteraan.

Maar als ik de inhoud van de mItemListprobeer te wijzigen terwijl RecyclerView aan het scrollen is, krijg ik soms
java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 157(offset:157).state:588

Dit gebeurt als de grootte van mItemListkleiner is dan voorheen. Dus wat is de juiste manier om de inhoud van de RecyclerViewte wijzigen? Is dit een bug in RecyclerView?

Hier is de volledige stacktracering van Exception:

java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 157(offset:157).state:588
        at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:3300)
        at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:3258)
        at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:1803)
        at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1302)
        at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1265)
        at android.support.v7.widget.LinearLayoutManager.scrollBy(LinearLayoutManager.java:1093)
        at android.support.v7.widget.LinearLayoutManager.scrollVerticallyBy(LinearLayoutManager.java:956)
        at android.support.v7.widget.RecyclerView$ViewFlinger.run(RecyclerView.java:2715)
        at android.view.Choreographer$CallbackRecord.run(Choreographer.java:725)
        at android.view.Choreographer.doCallbacks(Choreographer.java:555)
        at android.view.Choreographer.doFrame(Choreographer.java:524)
        at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:711)
        at android.os.Handler.handleCallback(Handler.java:615)
        at android.os.Handler.dispatchMessage(Handler.java:92)
        at android.os.Looper.loop(Looper.java:137)
        at android.app.ActivityThread.main(ActivityThread.java:4921)
        at java.lang.reflect.Method.invokeNative(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:511)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1027)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:794)
        at dalvik.system.NativeStart.main(Native Method)

AdapterView-code:

private static class FileListAdapter extends RecyclerView.Adapter<FileHolder> {
    private final Context mContext;
    private final SparseBooleanArray mSelectedArray;
    private final List<String> mList;
    FileListAdapter(Context context, List<String> list, SparseBooleanArray selectedArray) {
        mList = list;
        mContext = context;
        mSelectedArray = selectedArray;
    }
    @Override
    public FileHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
        View view = LayoutInflater.from(viewGroup.getContext()).inflate(
                R.layout.file_list_item, viewGroup, false);
        TextView tv = (TextView) view
                .findViewById(R.id.file_name_text);
        Typeface font = Typeface.createFromAsset(viewGroup.getContext().getAssets(),
                viewGroup.getContext().getString(R.string.roboto_regular));
        tv.setTypeface(font);
        return new FileHolder(view, tv);
    }
    @Override
    public void onBindViewHolder(FileHolder fileHolder, final int i) {
        String name = mList.get(i);
        // highlight view if selected
        setSelected(fileHolder.itemView, mSelectedArray.get(i));
        // Set text
        fileHolder.mTextView.setText(name);
    }
    @Override
    public int getItemCount() {
        return mList.size();
    }
}
private static class FileHolder extends RecyclerView.ViewHolder {
    public final TextView mTextView;
    public FileHolder(View itemView, TextView tv) {
        super(itemView);
        mTextView = tv;
    }
}

Antwoord 1, autoriteit 100%

Bewerken:de bug is nu opgelost. Als u nog steeds dezelfde uitzondering krijgt, zorg er dan voor dat u uw Adapter-gegevensbron alleen bijwerkt vanuit de hoofdthread en de juiste adapter-notificatiemethode aanroept erna.

Oud antwoord:het lijkt een bug te zijn in RecyclerView, het is gemeld hieren hier. Hopelijk wordt het in de volgende release opgelost.


Antwoord 2, autoriteit 40%

Geen probleem voor mij. Gebruik NotifyDataSetChanged();

public class MyFragment extends Fragment{
    private MyAdapter adapter;
    // Your code
    public void addArticle(){
        ArrayList<Article> list = new ArrayList<Article>();
        //Add one article in this list
        adapter.addArticleFirst(list); // or adapter.addArticleLast(list);
    }
}
public class ArticleAdapterRecycler extends RecyclerView.Adapter<ArticleAdapterRecycler.ViewHolder> {
    private ArrayList<Article> Articles = new ArrayList<Article>();
    private Context context;
    // Some functions from RecyclerView.Adapter<ArticleAdapterRecycler.ViewHolder>    
    // Add at the top of the list.
    public void addArticleFirst(ArrayList<Article> list) {
        Articles.addAll(0, list);
        notifyDataSetChanged();
    }
    // Add at the end of the list.
    public void addArticleLast(ArrayList<Article> list) {
        Articles.addAll(Articles.size(), list);
        notifyDataSetChanged();
    }
}

Antwoord 3, autoriteit 24%

Verbied gewoon het scrollen van RecyclerView wanneer gegevens veranderen.

Vind ik leuk als mijn code:

mRecyclerView.setOnTouchListener(
        new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if (mIsRefreshing) {
                    return true;
                } else {
                    return false;
                }
            }
        }
);

Meer over: http://drakeet.me /recyclerview-bug-indexoutofboundsexception-inconsistency-detected-invalid-item-position-solution


Antwoord 4, autoriteit 19%

Hoewel een aantal nuttige hyperlinks over dit probleem worden gegeven in het geaccepteerde antwoord, is het niet waar dat dit gedrag van RecyclerViewtijdens het scrollen een bug is.

Als u deze uitzondering ziet, vergeet u hoogstwaarschijnlijk de adapter te informeren nadat de inhoud van RecyclerViewis “gewijzigd”. Mensen noemen notifyDataSetChanged()pas nadat een item aan de dataset is toegevoegd. De inconsistentie treedt echter niet alleen op nadat u de adapter opnieuw hebt gevuld, maar ook wanneer u een item verwijdert of de gegevensset wist. U moet de weergave vernieuwen door de adapter op de hoogte te stellen van deze wijziging:

public void refillAdapter(Item item) {
    adapter.add(item);
    notifyDataSetChanged();
}
public void cleanUpAdapter() {
    adapter.clear();
    notifyDataSetChanged(); /* Important */
}

In mijn geval heb ik geprobeerd de adapter op te schonen in onStop()en hem opnieuw te vullen in onStart(). Ik ben vergeten notifyDataSetChanged()aan te roepen nadat de adapter is opgeschoond met clear(). Telkens wanneer ik de status veranderde van onStop()in onStart()en snel door de RecyclerViewscrolde terwijl de dataset opnieuw werd geladen, zag ik deze uitzondering. Als ik tot het einde van het herladen wachtte zonder te scrollen, zou er geen uitzondering zijn, aangezien de adapter deze keer soepel kan worden hersteld.

Kortom, de RecyclerViewis niet consistent wanneer deze wordt gewijzigd. Als u door de weergave probeert te bladeren terwijl de wijzigingen in de gegevensset worden verwerkt, ziet u java.lang.IndexOutOfBoundsException: Inconsistency detected. Om dit probleem op te lossen, dient u de adapter onmiddellijk op de hoogte te stellen nadat de dataset is gewijzigd.


Antwoord 5, autoriteit 19%

Het probleem is zeker niet het scrollen van recyclerview, maar het is gerelateerd aan notifyDataSetChanged(). Ik had een recycler-weergave waarin ik constant gegevens aan het veranderen was, d.w.z. gegevens toevoegen en verwijderen. Ik riep notifyDataSetChanged()elke keer dat ik items aan mijn lijst toevoegde, Maar was de adapter niet aan het vernieuwenwanneer het item werd verwijderd of de lijst werd gewist.

Dus om de :

. te repareren

java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 2(offset:2).state:12 at android.support.v7.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:5456)

Ik belde adapter.notifyDataSetChanged() na list.clear(), waar het ook nodig was.

if (!myList.isEmpty()) {
        myList.clear();
        myListAdapter.notifyDataSetChanged();
    }

Sindsdien ben ik de uitzondering nooit meer tegengekomen.
Hoop dat het voor anderen ook zo werkt. 🙂


Antwoord 6, autoriteit 17%

Dit probleem komt naar recyclerview als je

. gebruikt

adapter.setHasStableIds(true);

als u dit instelt, verwijdert u dit en werkt u uw dataset in de adapter bij;

als u nog steeds met het probleem wordt geconfronteerd, maakt u alle weergaven ongeldig zodra u nieuwe gegevens heeft ontvangen en werkt u vervolgens uw gegevensset bij.


Antwoord 7, autoriteit 10%

Ik had hetzelfde probleem, java.lang.IndexOutOfBoundsException: inconsistentie gedetecteerd.

Maak Aangepaste LinearLayoutManager.

HPLinearLayoutManager.java

public class HPLinearLayoutManager extends LinearLayoutManager {
    public HPLinearLayoutManager(Context context) {
        super(context);
    }
    public HPLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
        super(context, orientation, reverseLayout);
    }
    public HPLinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }
    /**
     * Magic here
     */
    @Override
    public boolean supportsPredictiveItemAnimations() {
        return false;
    }
}

maak instancevan HPLinearLayoutManager.

HPLinearLayoutManager hpLinearLayoutManager = new HPLinearLayoutManager(mContext);
recyclerView.setLayoutManager(hpLinearLayoutManager);

Ik hoop dat dit je zou helpen.


Antwoord 8, autoriteit 7%

Ik wijzig gegevens voor de RecyclerViewop de achtergrond Thread. Ik kreeg dezelfde Exceptionals de OP. Ik heb dit toegevoegd na het wijzigen van gegevens:

myRecyclerView.post(new Runnable() {
@Override
public void run() {
myRecyclerAdapter.notifyDataSetChanged();
}
});

Hopelijk helpt het


9, Autoriteit 5%

Ik kom erachter dat voor mij deze uitzondering komt wanneer twee dingen tegelijkertijd gebeuren, namelijk

1) Scrollen van recyclerview

2) dataset wordt gewijzigd

Dus ik heb dit probleem opgelost door de scroll uit te schakelen totdat de notificationdatasetchanged wordt aangeroepen.

leaderAdapter.notifyDataSetChanged();
pDialog.hide();

Om het scrollen uit te schakelen, heb ik een voortgangsdialoogvenster gebruikt waarvan de setCancelable false is.

pDialog = new ProgressDialog(getActivity());
pDialog.setMessage("Please wait...");
pDialog.setCancelable(false);

De truc hier is om scrollen alleen in te schakelen wanneer de dataset is bijgewerkt.


Antwoord 10, autoriteit 5%

Vermijd notifyDatasetHasChanged() en doe het volgende:

public void setItems(ArrayList<Article> newArticles) {
    //get the current items
    int currentSize = articles.size();
    //remove the current items
    articles.clear();
    //add all the new items
    articles.addAll(newArticles);
    //tell the recycler view that all the old items are gone
    notifyItemRangeRemoved(0, currentSize);
    //tell the recycler view how many new items we added
    notifyItemRangeInserted(0, articles.size());
}

Antwoord 11, autoriteit 2%

Ik had hetzelfde probleem toen ik een nieuwe adapterinstantie instelde op basis van enkele selectiecriteria.

Ik heb mijn probleem opgelost door RecyclerView.swapAdapter(adapter, true)te gebruiken
Wanneer we een nieuwe adapter plaatsen.


Antwoord 12, autoriteit 2%

Ik heb hetzelfde probleem met dit probleem, ik ben erg moe om het te zoeken en op te lossen. Maar ik heb een oplossing gevonden en uitzonderingen zijn niet meer weggegooid.

public class MyLinearLayoutManager extends LinearLayoutManager 
{
    public MyLinearLayoutManager(Context context) {
        super(context);
    }
    public MyLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
        super(context, orientation, reverseLayout);
    }
    public MyLinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }
    @Override
    public boolean supportsPredictiveItemAnimations() {
        return false;
    }
    @Override
    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
        //override this method and implement code as below
        try {
            super.onLayoutChildren(recycler, state);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Ik hoop dat dit antwoord je probleem oplost.


Antwoord 13, autoriteit 2%

Ik heb dit probleem gerepliceerd.
Dit gebeurde toen we items in de achtergrondthread van mlist verwijderden, maar notifyDataSetChanged() niet aanroepen. Als we nu scrollen, komt deze uitzondering eraan.

java.lang.IndexOutOfBoundsException: inconsistentie gedetecteerd. Ongeldige itempositie 86(offset:86).state:100

Aanvankelijk had ik 100 items en verwijderde ik enkele items uit de achtergrondthread.

Het lijkt erop dat Recyclerview getItemCount() zelf aanroept om de status te valideren.


Antwoord 14

Ik zie niets mis met de code die je hebt gepost. Het enige wat ik raar vind, is deze regel

setSelected(fileHolder.itemView, mSelectedArray.get(i));

in uw onBindViewHolder-methode in de adapter.. werkt u deze array ook bij wanneer u de grootte van uw lijst met items in de array wijzigt?


Antwoord 15

Ik had een soortgelijk probleem toen ik probeerde het eerste item toe te voegen aan recyclerView met de methode notificationItemInserted, dus ik wijzigde de addItem-functie op mijn adapter zoals hieronder en het is opgelost.

Vreemd probleem, hopelijk wordt het snel opgelost!

public void addItem(int position, TableItem item) {
    boolean firstEntry = false;
    if (items.size() == 0) {
        firstEntry = true;
    }
    items.add(position, item);
    if (firstEntry) {
        notifyDataSetChanged();
    } else {
        notifyItemInserted(position);
    }
}

Antwoord 16

er is één zin in de geluidscode:
/**
* Gebruikt wanneer LayoutState is geconstrueerd in een scrollende staat. Het zou moeten
* stel de hoeveelheid scrollen in die we kunnen maken zonder een nieuwe te maken
* visie. Instellingen die nodig zijn voor een efficiënte recycling van weergaven.
*/
int mScrollingOffset; 


Antwoord 17

In mijn geval is het opgelost door te veranderen
mRecyclerView.smoothScrollToPosition(0)naar

mRecyclerView.scrollToPosition(0)

Antwoord 18

Ik had ook hetzelfde probleem en ik heb het opgelost door de methode notifyItemRangeChanged() niet te gebruiken. Het wordt mooi uitgelegd op

https://code.google.com/p/ android/issues/detail?id=77846#c10


Antwoord 19

probeer een booleaanse vlag te gebruiken, initialiseer deze als onwaar en maak deze binnen de OnRefresh-methode waar, wis uw dataList als de vlag waar is net voordat u de nieuwe gegevens eraan toevoegt en maak deze daarna onwaar.

uw code zou er zo uit kunnen zien

private boolean pullToRefreshFlag = false ;
 private ArrayList<your object> dataList ;
 private Adapter adapter ;
 public class myClass extend Fragment implements SwipeRefreshLayout.OnRefreshListener{
 private void requestUpdateList() {
     if (pullToRefresh) {
        dataList.clear
        pullToRefreshFlag = false;
     }
     dataList.addAll(your data);
     adapter.notifyDataSetChanged;
 @Override
 OnRefresh() {
 PullToRefreshFlag = true
 reqUpdateList() ; 
 }
}

Antwoord 20

In mijn geval lag het probleem bij mij.

Mijn setup is een Recyclerview, Adapter & Cursor/laadmechanisme.

Op een gegeven moment in mijn app wordt de lader vernietigd.

supportLoaderManager.destroyLoader(LOADER_ID_EVENTS)

Ik verwachtte dat de Recyclerview een lege lijst zou weergeven, aangezien ik zojuist hun gegevensbron heb verwijderd. Wat het vinden van fouten ingewikkelder maakte, was dat de lijst zichtbaar was en dat de bekende uitzondering alleen optrad bij een fling/scroll/animatie.

Dat kostte me een paar uur. 🙂


Antwoord 21

doe dit wanneer u weergave wilt toevoegen (zoals notifyDataof addViewof iets dergelijks)

if(isAdded()){ 
    // 
    //  add view like this.
    //
    //  celebrityActionAdapter.notifyItemRangeInserted(pageSize, 10);
    //
    //
}

Antwoord 22

Ik kwam onlangs dit probleem tegen en ontdekte dat mijn probleem was dat ik de gegevensbron van de adapter aan het wijzigen was op één lus/aanroepstack en vervolgens notifyDataSetChangedaanriep op een volgende lus/aanroepstack. Tussen het wijzigen van de gegevensbron en het optreden van notifyDataSetChangedprobeerde de RecyclerViewweergaven in te vullen vanwege het scrollen en merkte dat de adapter zich in een vreemde staat bevond en gooide deze uitzondering terecht.

Yigit Boyar legt keer op keer uitwaarom deze crash zou optreden in jouw app:

  • U moet zich in dezelfde call-stack bevinden wanneer u uw adapterbron wijzigt en notifyDataSetChanged()
  • U moet in de hoofdthread zijn wanneer u de gegevensbron van uw adapter wijzigt

Als je niet zeker weet hoe je dit moet debuggen, voeg dan de volgende Kotlin-code toe waar je je adapterbron wijzigt en waar je notifyDataSetChanged

aanroept

Log.d("TEST", "isMainThread: ${Looper.myLooper() == Looper.getMainLooper()}")
Log.d("TEST", Log.getStackTraceString(Exception("Debugging RV and Adapter")))

Other episodes