可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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?
回答1:
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.
回答2:
According to this issue page ....it is the default recycleview item change animation... You can turn it off.. try this
recyclerView.getItemAnimator().setSupportsChangeAnimations(false);
Change in latest version
Quoted from Android developer blog:
Note that this new API is not backward compatible. If you previously
implemented an ItemAnimator, you can instead extend
SimpleItemAnimator, which provides the old API by wrapping the new
API. You’ll also notice that some methods have been entirely removed
from ItemAnimator. For example, if you were calling
recyclerView.getItemAnimator().setSupportsChangeAnimations(false),
this code won’t compile anymore. You can replace it with:
ItemAnimator animator = recyclerView.getItemAnimator();
if (animator instanceof SimpleItemAnimator) {
((SimpleItemAnimator) animator).setSupportsChangeAnimations(false);
}
回答3:
This simply worked:
recyclerView.getItemAnimator().setChangeDuration(0);
回答4:
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.
回答5:
try this to disable the default animation
ItemAnimator animator = recyclerView.getItemAnimator();
if (animator instanceof SimpleItemAnimator) {
((SimpleItemAnimator) animator).setSupportsChangeAnimations(false);
}
this the new way to disable the animation since android support 23
this old way will work for older version of the support library
recyclerView.getItemAnimator().setSupportsChangeAnimations(false)
回答6:
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.
回答7:
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.
回答8:
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());
回答9:
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;
}
回答10:
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
回答11:
for me recyclerView.setHasFixedSize(true);
worked
回答12:
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!!