RecyclerView blinking after notifyDatasetChanged()

2019-01-21 12:04发布

I have a RecyclerView which loads some data from API, includes an image url and some data, and I use networkImageView to lazy load image.

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

Here is implementation for 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());
    }

Problem is when we have refresh in the recyclerView, it is blincking for a very short while in the beginning which looks strange.

I just used GridView/ListView instead and it worked as I expected. There were no blincking.

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

Anyone faced with such a problem? what could be the reason?

12条回答
狗以群分
2楼-- · 2019-01-21 12:44

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!!

查看更多
男人必须洒脱
3楼-- · 2019-01-21 12:45

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());
查看更多
聊天终结者
4楼-- · 2019-01-21 12:45

for me recyclerView.setHasFixedSize(true); worked

查看更多
beautiful°
5楼-- · 2019-01-21 12:48

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;
}
查看更多
狗以群分
6楼-- · 2019-01-21 12:49

Try using stable IDs in your RecyclerView.Adapter

setHasStableIds(true) and override getItemId(int position).

Without stable IDs, after notifyDataSetChanged(), ViewHolders usually assigned to not to same positions. That was the reason of blinking in my case.

You can find a good explanation here.

查看更多
放荡不羁爱自由
7楼-- · 2019-01-21 12:50

This simply worked:

recyclerView.getItemAnimator().setChangeDuration(0);
查看更多
登录 后发表回答