2015-03-29 3 views
68

У меня есть RecyclerView, который загружает некоторые данные из API, включает в себя URL-адрес изображения и некоторые данные, и я использую networkImageView для ленивого загружаемого изображения.RecyclerView мигает после notifyDatasetChanged()

@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, он blincking за очень короткое время в начале, который выглядит странно.

Я просто использовал 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); 

Любой сталкивался с такой проблемой? Что может быть причиной?

ответ

2

Предполагается, что mItems - это коллекция, которая поддерживает ваш Adapter, почему вы удаляете все и повторно добавляете? В основном вы говорите, что все изменилось, поэтому RecyclerView перепроверяет все представления, чем я предполагаю, что библиотека изображений не обрабатывает ее должным образом, где она по-прежнему сбрасывает представление, даже если это тот же URL-адрес изображения. Возможно, у них было какое-то испеченное решение для AdapterView, так что он отлично работает в GridView.

Вместо того, чтобы звонить notifyDataSetChanged, что приведет к повторной привязке всех видов, вызовет подробные уведомления о событиях (уведомлять добавленные/удаленные/перемещенные/обновленные), чтобы RecyclerView перепрограммировал только необходимые виды, и ничто не будет мерцать.

+2

Или, может быть, это просто «ошибка» в RecyclerView? Очевидно, если он работал отлично в течение последних 6 лет с AbsListView, и теперь это не с RecyclerView, значит, что-то не так с RecyclerView, правильно? :) Быстрое ознакомление с ней показывает, что при обновлении данных в ListView и GridView они отслеживают позицию просмотра +, поэтому при обновлении вы получите точно такой же зритель. Пока RecyclerView перетасовывает держатели для просмотра, что приводит к мерцанию. – vovkab

+0

Работа с ListView * не означает, что это правильно для RecyclerView. Эти компоненты имеют разные архитектуры. – yigit

+1

Согласитесь, но иногда очень сложно узнать, какие элементы изменены, например, если вы используете курсоры или просто обновляете все данные. Поэтому recyclerview должен правильно обрабатывать этот случай. – vovkab

1

Эй @ Возможно, это может быть поздний повтор. Я также столкнулся с этой проблемой и решил с помощью решения ниже, это может помочь вам проверить.

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

Я создал метод getLruBitmapCache() для возврата LruBitmapCache

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

Надежда его собираются, чтобы помочь вам.

+0

Спасибо за ваш ответ. Это то, что я точно сделал в своем VollyClient.java. Взгляните на: [VolleyClient.java] (https://bitbucket.org/ali-rezaei/twitzer/src/ba60b041fce1abfc271ae4a084381e205c972ee5/app/src/main/java/com/dynamo/android/client/volley/VolleyClient.java ? at = master) – Ali

+0

Просто проверьте один раз с помощью класса LruCache , я думаю, что он решит вашу проблему. Взгляните на [LruCache] (http://developer.android.com/reference/android/util/LruCache.html) –

+0

Вы проверили один раз код, который я поделился с вами в комментарии? что у вас в вашем классе, что я пропустил там? – Ali

0

У меня был подобный вопрос, и это работает для меня Вы можете вызвать этот метод, чтобы установить размер для кэша изображений

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

Согласно этой issue странице .... это recycleview по умолчанию изменить пункт анимация. .. Вы можете отключить его.попробуйте этот

recyclerView.getItemAnimator().setSupportsChangeAnimations(false); 

Изменение последней версии

Android developer blog Цитируется:

Обратите внимание, что этот новый API не является обратно совместимым. Если вы ранее реализовали ItemAnimator, вы можете вместо этого расширить SimpleItemAnimator, который предоставляет старый API, обернув новый API . Вы также заметите, что некоторые методы были полностью удалены из ItemAnimator. Например, если вы вызывали recyclerView.getItemAnimator(). SetSupportsChangeAnimations (false), Этот код больше не компилируется. Вы можете заменить его:

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

@sabeer все тот же выпуск. Это не решение проблемы. –

+2

@delive дайте мне знать, если вы нашли какое-либо решение для этого –

+0

Я использую picasso, его что-то, не кажется мерцающим. – delive

70

Попробуйте использовать стабильные идентификаторы в вашем RecyclerView.Adapter

setHasStableIds(true) и переопределить getItemId(int position).

Без стабильных идентификаторов, после notifyDataSetChanged(), ViewHolders обычно присваиваются не в одинаковые позиции. Это было причиной моргания в моем случае.

+2

работает отлично. freaking awesome –

+3

Это самый правильный ответ –

+0

префект решение –

6

попробовать это отключить анимации по умолчанию

ItemAnimator animator = recyclerView.getItemAnimator(); 

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

этот новый способ отключить анимацию, так как андроид поддержки 23

этот старый способ будет работать на старой версии библиотеки поддержки

recyclerView.getItemAnimator().setSupportsChangeAnimations(false) 
20

Это просто сработало:

recyclerView.getItemAnimator().setChangeDuration(0); 
+0

Работал как очарование! спасибо – Alireza

+0

это хорошая альтернатива 'code' ItemAnimator animator = recyclerView.getItemAnimator(); if (animator instanceof SimpleItemAnimator) { ((SimpleItemAnimator) аниматор) .setSupportsChangeAnimations (false); } 'code' – ziniestro

+0

останавливает все анимации ADD и REMOVE – MBH

0

для моего приложения, у меня были некоторые данные, но я не хотел, чтобы весь вид мигал.

Я решил это, только угасая старое видение вниз на 0,5 альфа и запустив newview alpha на 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

для меня recyclerView.setHasFixedSize(true); работал

1

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 альфа-линии выше.

Чтобы использовать новый аниматор просто позвоните setItemAnimator() на вашем RecyclerView:

mRecyclerView.setItemAnimator(new MyItemAnimator()); 
0

Используя соответствующие методы recyclerview обновления вида будет решить эту проблему

Во-первых, внести изменения в список

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

Затем уведомлять, используя

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

Надеюсь, это поможет!

5

У меня такая же проблема, загружая изображение с некоторых URL-адресов, и изображениеView мигает. решена с помощью

notifyItemRangeInserted()  

вместо

notifyDataSetChanged() 

которых избежать перезагрузки те неизменные старые ДАННЫЕ.

Смежные вопросы