IT박스

notifyDatasetChanged () 후 RecyclerView 깜박임

itboxs 2020. 8. 20. 08:03
반응형

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

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

문제는 recyclerView에서 새로 고침을 할 때 처음에는 이상하게 보이는 매우 짧은 시간 동안 깜박입니다.

대신 GridView / ListView를 사용했고 예상대로 작동했습니다. 흠집이 없었습니다.

RecycleView 구성 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);

그런 문제에 직면 한 사람이 있습니까? 이유가 무엇일까요?


RecyclerView.Adapter에서 안정적인 ID를 사용해보십시오.

setHasStableIds(true)및 재정의 getItemId(int position).

안정적인 ID가 없으면 notifyDataSetChanged()일반적으로 ViewHolders가 동일한 위치에 할당되지 않습니다. 그것이 제 경우에 깜박이는 이유였습니다.

여기 에서 좋은 설명을 찾을 수 있습니다 .


문제 페이지 에 따르면 .... 기본 recycleview 항목 변경 애니메이션입니다 ... 해제 할 수 있습니다 .. 시도해보세요.

recyclerView.getItemAnimator().setSupportsChangeAnimations(false);

최신 버전 변경

Android 개발자 블로그 에서 인용 :

이 새로운 API는 이전 버전과 호환되지 않습니다. 이전에 ItemAnimator를 구현 한 경우 대신 새 API를 래핑하여 이전 API를 제공하는 SimpleItemAnimator를 확장 할 수 있습니다. 또한 일부 메서드가 ItemAnimator에서 완전히 제거되었음을 알 수 있습니다. 예를 들어, recyclerView.getItemAnimator (). setSupportsChangeAnimations (false)를 호출했다면이 코드는 더 이상 컴파일되지 않습니다. 다음으로 바꿀 수 있습니다.

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

이것은 단순히 작동했습니다.

recyclerView.getItemAnimator().setChangeDuration(0);

기본 애니메이션을 비활성화하려면 이것을 시도하십시오.

ItemAnimator animator = recyclerView.getItemAnimator();

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

이것은 안드로이드 지원 23 이후 애니메이션을 비활성화하는 새로운 방법입니다.

this old way will work for older version of the support library

recyclerView.getItemAnimator().setSupportsChangeAnimations(false)

I have the same issue loading image from some urls and then imageView blinks. Solved by using

notifyItemRangeInserted()    

instead of

notifyDataSetChanged()

which avoids to reload those unchanged old datas.


Assuming mItems is the collection that backs your Adapter, why are you removing everything and re-adding? You are basically telling it that everything has changed, so RecyclerView rebinds all views than I assume the Image library does not handle it properly where it still resets the View even though it is the same image url. Maybe they had some baked in solution for AdapterView so that it works fine in GridView.

Instead of calling notifyDataSetChanged which will cause re-binding all views, call granular notify events (notify added/removed/moved/updated) so that RecyclerView will rebind only necessary views and nothing will flicker.


Recyclerview uses DefaultItemAnimator as it's default animator. As you can see from the code below, they change the alpha of the view holder upon item change:

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

I wanted to retain the rest of the animations but remove the "flicker" so I cloned DefaultItemAnimator and removed the 3 alpha lines above.

To use the new animator just call setItemAnimator() on your RecyclerView:

mRecyclerView.setItemAnimator(new MyItemAnimator());

In Kotlin you can use 'class extension' for 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()
    // ...
}

Hey @Ali it might be late replay. I also faced this issue and solved with below solution, it may help you please check.

LruBitmapCache.java class is created to get image cache size

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 class [extends Application] added below code

in VolleyClient singleton class constructor add below snippet to initialize the ImageLoader

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

I created getLruBitmapCache() method to return LruBitmapCache

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

Hope its going to help you.


Try to use the stableId in recycler view. The following article briefly explains it

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


I had similar issue and this worked for me You can call this method to set size for image cache

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

for my application, I had some data changing but I didn't want the entire view to blink.

I solved it by only fading the oldview down 0.5 alpha and starting the newview alpha at 0.5. This created a softer fading transition without making the view disappear completely.

Unfortunately because of private implementations, I couldn't subclass the DefaultItemAnimator in order to make this change so I had to clone the code and make the following changes

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

for me recyclerView.setHasFixedSize(true); worked


Using appropriate recyclerview methods to update views will solve this issue

First, make changes in the list

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

Then notify using

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

Hope this will help!!

참고URL : https://stackoverflow.com/questions/29331075/recyclerview-blinking-after-notifydatasetchanged

반응형