webentwicklung-frage-antwort-db.com.de

RecyclerView blinkt nach notifyDatasetChanged ()

Ich habe einen RecyclerView, der einige Daten von der API lädt, eine Bild-URL und einige Daten enthält, und ich verwende networkImageView, um das Laden von Bildern zu verzögern.

@Override
public void onResponse(List<Item> response) {
   mItems.clear();
   for (Item item : response) {
      mItems.add(item);
   }
   mAdapter.notifyDataSetChanged();
   mSwipeRefreshLayout.setRefreshing(false);
}

Hier ist die Implementierung für den Adapter:

public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, final int position) {
        if (isHeader(position)) {
            return;
        }
        // - get element from your dataset at this position
        // - replace the contents of the view with that element
        MyViewHolder holder = (MyViewHolder) viewHolder;
        final Item item = mItems.get(position - 1); // Subtract 1 for header
        holder.title.setText(item.getTitle());
        holder.image.setImageUrl(item.getImg_url(), VolleyClient.getInstance(mCtx).getImageLoader());
        holder.image.setErrorImageResId(Android.R.drawable.ic_dialog_alert);
        holder.Origin.setText(item.getOrigin());
    }

Das Problem ist, dass die Aktualisierung in der recyclerView anfangs nur für kurze Zeit blinkt, was seltsam aussieht.

Ich habe stattdessen GridView/ListView verwendet und es hat wie erwartet funktioniert. Es gab kein Blinzeln.

konfiguration für RecycleView in onViewCreated of my Fragment:

mRecyclerView = (RecyclerView) view.findViewById(R.id.recyclerView);
        // use this setting to improve performance if you know that changes
        // in content do not change the layout size of the RecyclerView
        mRecyclerView.setHasFixedSize(true);

        mGridLayoutManager = (GridLayoutManager) mRecyclerView.getLayoutManager();
        mGridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
            @Override
            public int getSpanSize(int position) {
                return mAdapter.isHeader(position) ? mGridLayoutManager.getSpanCount() : 1;
            }
        });

        mRecyclerView.setAdapter(mAdapter);

Jemand mit einem solchen Problem konfrontiert? woran könnte das liegen

98
Ali

Versuchen Sie, stabile IDs in Ihrem RecyclerView.Adapter zu verwenden

setHasStableIds(true) und überschreiben getItemId(int position).

Ohne stabile IDs werden ViewHolders nach notifyDataSetChanged() normalerweise nicht denselben Positionen zugewiesen. Das war der Grund, warum ich blinzelte.

Sie finden eine gute Erklärung hier.

105

Entsprechend dieser Ausgabe Seite ... ist es die Standardanimation für das Ändern von Elementen in der Papierkorbansicht ... Sie können sie deaktivieren. Probieren Sie dies aus

recyclerView.getItemAnimator().setSupportsChangeAnimations(false);

Änderung in der neuesten Version

Zitiert aus Android-Entwickler-Blog :

Beachten Sie, dass diese neue API nicht abwärtskompatibel ist. Wenn Sie zuvor einen ItemAnimator implementiert haben, können Sie stattdessen SimpleItemAnimator erweitern, der die alte API bereitstellt, indem Sie die neue API umschließen. Sie werden auch feststellen, dass einige Methoden vollständig aus ItemAnimator entfernt wurden. Wenn Sie beispielsweise recyclerView.getItemAnimator (). SetSupportsChangeAnimations (false) aufrufen, wird dieser Code nicht mehr kompiliert. Sie können es ersetzen durch:

ItemAnimator animator = recyclerView.getItemAnimator();
if (animator instanceof SimpleItemAnimator) {
  ((SimpleItemAnimator) animator).setSupportsChangeAnimations(false);
}
93
Sabeer Mohammed

Das hat einfach funktioniert:

recyclerView.getItemAnimator().setChangeDuration(0);
37
Hamzeh Soboh

Ich habe das gleiche Problem beim Laden von Bildern von einigen URLs und dann blinkt imageView. Gelöst mit

notifyItemRangeInserted()    

anstatt

notifyDataSetChanged()

dadurch wird vermieden, dass die unveränderten alten Daten erneut geladen werden.

9
Wesely

versuchen Sie dies, um die Standardanimation zu deaktivieren

ItemAnimator animator = recyclerView.getItemAnimator();

if (animator instanceof SimpleItemAnimator) {
  ((SimpleItemAnimator) animator).setSupportsChangeAnimations(false);
}

dies ist die neue Möglichkeit, die Animation zu deaktivieren, da Android Unterstützung 23

dieser alte Weg funktioniert für ältere Versionen der Support-Bibliothek

recyclerView.getItemAnimator().setSupportsChangeAnimations(false)
9
Mohamed Farouk

Angenommen, mItems ist die Sammlung, die Ihr Adapter sichert. Warum entfernen Sie alles und fügen es erneut hinzu? Sie sagen im Grunde, dass sich alles geändert hat, also bindet RecyclerView alle Ansichten neu, als ich annehme, dass die Bildbibliothek es nicht richtig handhabt, wo es die Ansicht immer noch zurücksetzt, obwohl es die gleiche Bild-URL ist. Möglicherweise haben sie eine Lösung für AdapterView eingebaut, damit diese in GridView einwandfrei funktioniert.

Anstatt notifyDataSetChanged aufzurufen, wodurch alle Ansichten erneut gebunden werden, rufen Sie granulare Benachrichtigungsereignisse auf (Benachrichtigung hinzugefügt/entfernt/verschoben/aktualisiert), damit RecyclerView nur die erforderlichen Ansichten erneut bindet und nichts flackert.

2
yigit

Recyclerview verwendet DefaultItemAnimator als Standardanimator. Wie Sie dem folgenden Code entnehmen können, ändern sie das Alpha des Ansichtsinhabers bei einer Artikeländerung:

@Override
public boolean animateChange(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder, int fromX, int fromY, int toX, int toY) {
    ...
    final float prevAlpha = ViewCompat.getAlpha(oldHolder.itemView);
    ...
    ViewCompat.setAlpha(oldHolder.itemView, prevAlpha);
    if (newHolder != null) {
        ....
        ViewCompat.setAlpha(newHolder.itemView, 0);
    }
    ...
    return true;
}

Ich wollte den Rest der Animationen behalten, aber das "Flackern" entfernen, also habe ich DefaultItemAnimator geklont und die drei obigen Alpha-Linien entfernt.

Um den neuen Animator zu verwenden, rufen Sie einfach setItemAnimator () auf Ihrem RecyclerView auf:

mRecyclerView.setItemAnimator(new MyItemAnimator());
2
Peter File

Versuchen Sie, die stableId in der Recycler-Ansicht zu verwenden. Der folgende Artikel erklärt es kurz

https://medium.com/@hanru.yeh/recyclerviews-views-are-blink-wenn-notifydatasetchanged-c7b76d5149a2

1

Hey @Ali, es könnte spät sein, noch einmal zu spielen. Ich stellte auch dieses Problem gegenüber und löste mit der folgenden Lösung, es kann Ihnen helfen, bitte zu überprüfen.

LruBitmapCache.Java Klasse wird erstellt, um die Größe des Bild-Cache zu ermitteln

import Android.graphics.Bitmap;
import Android.support.v4.util.LruCache;
import com.Android.volley.toolbox.ImageLoader.ImageCache;

public class LruBitmapCache extends LruCache<String, Bitmap> implements
        ImageCache {
    public static int getDefaultLruCacheSize() {
        final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
        final int cacheSize = maxMemory / 8;

        return cacheSize;
    }

    public LruBitmapCache() {
        this(getDefaultLruCacheSize());
    }

    public LruBitmapCache(int sizeInKiloBytes) {
        super(sizeInKiloBytes);
    }

    @Override
    protected int sizeOf(String key, Bitmap value) {
        return value.getRowBytes() * value.getHeight() / 1024;
    }

    @Override
    public Bitmap getBitmap(String url) {
        return get(url);
    }

    @Override
    public void putBitmap(String url, Bitmap bitmap) {
        put(url, bitmap);
    }
}

VolleyClient.Java Singleton-Klasse [Erweitert Anwendung] hinzugefügt unter Code

fügen Sie im VolleyClient-Konstruktor der Singleton-Klasse das folgende Snippet hinzu, um den ImageLoader zu initialisieren

private VolleyClient(Context context)
    {
     mCtx = context;
     mRequestQueue = getRequestQueue();
     mImageLoader = new ImageLoader(mRequestQueue,getLruBitmapCache());
}

Ich habe die Methode getLruBitmapCache () erstellt, um LruBitmapCache zurückzugeben

public LruBitmapCache getLruBitmapCache() {
        if (mLruBitmapCache == null)
            mLruBitmapCache = new LruBitmapCache();
        return this.mLruBitmapCache;
}

Hoffe es wird dir helfen.

1
Android learner

In Kotlin können Sie die Klassenerweiterung für RecyclerView verwenden:

fun RecyclerView.disableItemAnimator() {
    (itemAnimator as? SimpleItemAnimator)?.supportsChangeAnimations = false
}

// sample of using in Activity:
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                          savedInstanceState: Bundle?): View? {
    myRecyclerView.disableItemAnimator()
    // ...
}
1
Gregory

Durch die Verwendung geeigneter Recycling-Übersichtsmethoden zum Aktualisieren von Ansichten wird dieses Problem behoben

Nehmen Sie zunächst Änderungen in der Liste vor

mList.add(item);
or mList.addAll(itemList);
or mList.remove(index);

Dann benachrichtigen Sie mit

notifyItemInserted(addedItemIndex);
or
notifyItemRemoved(removedItemIndex);
or
notifyItemRangeChanged(fromIndex, newUpdatedItemCount);

Hoffe das wird helfen !!

0
Sreedhu Madhu

für mich hat recyclerView.setHasFixedSize(true); funktioniert

0
Pramod

bei meiner Anwendung hatten sich einige Daten geändert, aber ich wollte nicht, dass die gesamte Ansicht blinkt.

Ich habe es gelöst, indem ich nur die alte Ansicht um 0,5 Alpha verkleinert und die neue Ansicht um 0,5 Alpha gestartet habe. Dadurch wurde ein weicherer Überblendungsübergang erstellt, ohne dass die Ansicht vollständig verschwand.

Leider konnte ich wegen privater Implementierungen den DefaultItemAnimator nicht in Unterklassen unterteilen, um diese Änderung vorzunehmen, sodass ich den Code klonen und die folgenden Änderungen vornehmen musste

in animateChange:

ViewCompat.setAlpha(newHolder.itemView, 0);  //change 0 to 0.5f

in animateChangeImpl:

oldViewAnim.alpha(0).setListener(new VpaListenerAdapter() { //change 0 to 0.5f
0
Deefer

Ich hatte ein ähnliches Problem und das hat bei mir funktioniert. Sie können diese Methode aufrufen, um die Größe für den Bild-Cache festzulegen

private int getCacheSize(Context context) {

    final DisplayMetrics displayMetrics = context.getResources().
            getDisplayMetrics();
    final int screenWidth = displayMetrics.widthPixels;
    final int screenHeight = displayMetrics.heightPixels;
    // 4 bytes per pixel
    final int screenBytes = screenWidth * screenHeight * 4;

    return screenBytes * 3;
}
0