web-dev-qa-db-ja.com

notifyDatasetChanged()後のRecyclerViewの点滅

APIから一部のデータをロードし、画像のURLと一部のデータを含むRecyclerViewがあり、networkImageViewを使用して画像を遅延ロードします。

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

アダプタの実装は次のとおりです。

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());
    }

問題は、recyclerViewを更新するときです。最初は非常に短い間不思議に見えます。

代わりにGridView/ListViewを使用しただけで、期待どおりに機能しました。まばたきはありませんでした。

onViewCreated of my FragmentのRecycleViewの構成:

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);

誰もそのような問題に直面しましたか?理由は何でしょうか?

98
Ali

RecyclerView.Adapterで安定したIDを使用してみてください

setHasStableIds(true)およびgetItemId(int position)をオーバーライドします。

安定したIDがない場合、notifyDataSetChanged()の後、ViewHoldersは通常同じ位置に割り当てられません。それが私の場合の瞬きの理由でした。

ここで良い説明 を見つけることができます

105

これによると issue page ....それはデフォルトのrecycleview項目変更アニメーションです...あなたはそれをオフにすることができます..これを試してください

recyclerView.getItemAnimator().setSupportsChangeAnimations(false);

最新バージョンの変更

Androidデベロッパーブログ から引用:

この新しいAPIは後方互換性がないことに注意してください。以前にItemAnimatorを実装している場合は、代わりにSimpleItemAnimatorを拡張できます。これは、新しいAPIをラップすることで古いAPIを提供します。また、一部のメソッドがItemAnimatorから完全に削除されていることにも気付くでしょう。たとえば、recyclerView.getItemAnimator()。setSupportsChangeAnimations(false)を呼び出していた場合、このコードはコンパイルされなくなります。次のものに置き換えることができます。

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

これは単に働いた:

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

いくつかのURLから画像を読み込むと同じ問題が発生し、imageViewが点滅します。を使用して解決

notifyItemRangeInserted()    

の代わりに

notifyDataSetChanged()

これらの変更されていない古いデータをリロードすることを回避します。

9
Wesely

これを試してデフォルトのアニメーションを無効にします

ItemAnimator animator = recyclerView.getItemAnimator();

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

これは、Androidがサポートされているため、アニメーションを無効にする新しい方法です23

この古い方法は、サポートライブラリの古いバージョンで機能します

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

mItemsAdapterをサポートするコレクションであると仮定すると、なぜすべてを削除して再追加するのですか?基本的にはすべてが変更されたと伝えているので、RecyclerViewはすべてのビューを再バインドします。これは、同じイメージURLであっても、イメージライブラリがビューをリセットする場所で適切に処理しないと想定しているためです。 GridViewで問題なく動作するように、AdapterViewのソリューションが焼き付けられている場合があります。

すべてのビューを再バインドするnotifyDataSetChangedを呼び出す代わりに、詳細な通知イベント(追加/削除/移動/更新を通知)を呼び出して、RecyclerViewが必要なビューのみを再バインドし、何も点滅しないようにします。

2
yigit

Recyclerviewは、デフォルトのアニメーターとしてDefaultItemAnimatorを使用します。以下のコードからわかるように、アイテムを変更するとビューホルダーのアルファが変更されます。

@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;
}

残りのアニメーションを保持したいが、「ちらつき」を削除するため、DefaultItemAnimatorのクローンを作成し、上の3つのアルファ線を削除しました。

新しいアニメーターを使用するには、RecyclerViewでsetItemAnimator()を呼び出すだけです。

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

リサイクラビューでstableIdを使用してみてください。次の記事で簡単に説明しています

https://medium.com/@hanru.yeh/recyclerviews-views-are-blinking-when-notifydatasetchanged-c7b76d5149a2

1

ちょっと@ALi遅いリプレイかもしれません。私もこの問題に直面し、以下の解決策で解決しました。確認してください。

LruBitmapCache.Javaクラスは、画像キャッシュサイズを取得するために作成されます

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シングルトンクラス[アプリケーションの拡張]コードの下に追加

volleyClientシングルトンクラスコンストラクターで、以下のスニペットを追加してImageLoaderを初期化します

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

LruBitmapCacheを返すgetLruBitmapCache()メソッドを作成しました

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

それがあなたを助けることを願っています。

1
Android learner

Kotlinでは、RecyclerViewに「クラス拡張」を使用できます。

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

適切なrecyclerviewメソッドを使用してビューを更新すると、この問題が解決します

まず、リストを変更します

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

次に通知を使用して

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

これが役立つことを願っています!!

0
Sreedhu Madhu

私のためにrecyclerView.setHasFixedSize(true);働いた

0
Pramod

私のアプリケーションでは、いくつかのデータが変更されましたが、ビュー全体を点滅させたくありませんでした。

Oldviewを0.5アルファにフェードし、newviewアルファを0.5から開始するだけで解決しました。これにより、ビューが完全に消えることなく、ソフトなフェードトランジションが作成されました。

残念ながら、プライベート実装のため、この変更を行うためにDefaultItemAnimatorをサブクラス化できなかったため、コードを複製して次の変更を行う必要がありました。

animateChangeで:

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

animateChangeImpl内:

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

私は同様の問題があり、これは私のために働いたあなたは画像キャッシュのサイズを設定するためにこのメソッドを呼び出すことができます

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