2014-10-23 3 views
19

Im, используя RecyclerView, чтобы отобразить список, содержащий imageView. Чтобы сделать пользовательский интерфейс более плавным, я загружаю 58dp миниатюры, сохраненные на SD-карте, в эти изображения с помощью asyncTask.RecyclerView: Async image-loading

Проблема заключается в том, что как только childView приходит на визуальный дисплей, старое изображение из других данных повторно используется, а затем заменяется после завершения AsyncTask. Я могу остановить перетасовку, установив растровое изображение imageView в onPreExecute.

Есть ли способ действительно повторно использовать старые изображения или мне действительно нужно загружать изображения с SD-карты каждый раз, когда появляется новый View? Это делает вид довольно уродливым, потому что либо появляются неправильные изображения, либо изображение белого цвета.

ответ

30

Из-за повторного использования просмотра вы будете получать просмотры с контентом, который уже был на них, это была проблема и на ListViews, если вы использовали шаблон ViewHolder, который вам нужно.

Есть два решения здесь, хорошая практика и плохая хак:

  • В хорошей практике вы установите ImageView для отображения ничего на начале bindViewHolder(VH holder, int position) использования setDrawable(null) или аналогичные.

  • В плохом хака вы не рецикл/повторное представление, не обеспечение соблюдение ViewHolder модели, и вы бы раздуть его каждый раз, но это разрешено только в ListView и других старых компонентах.

+0

Значит, старые растровые изображения, прикрепленные к представлениям, отбрасываются, когда они покидают экран? –

+2

Вы можете поместить их в LruCache, когда вы их прочтете, или перед их удалением, и повторите их позже. –

+1

http://developer.android.com/reference/android/util/LruCache.html –

-1

Что MLProgrammer говорит, что это правильно.

Решение, к счастью, это легко: остановить рециркуляция

holder.setIsRecyclable(false); 

Этот выбор имеет свои последствия, так как делать то, что я предлагаю выше угнетает один из RecyclerView основных целей: переработка.

  • Ваш RecyclerView будет медленнее прокручивать (новый держатель должен создаваться каждый раз, снова раздуваясь от ресурсов);
  • Ваше использование памяти будет больше.
+2

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

+1

@ MLProgrammer-CiM вы правы –

7

Вы должны проверить universal image loader. Он имеет кэш памяти, кэш диска и загружает изображения асинхронно, поэтому не блокирует ui. Вы можете установить изображение по умолчанию и/или не выполнить выборку изображения и т. Д. Он может пробовать снижать изображение, чтобы уменьшить объем памяти растрового изображения. Я действительно рекомендую вам использовать его для изображений.

Не отключайте подлежит повторному использованию для вашего случая, потому что это бессмысленно. Изображения должны быть переработаны, потому что их растровые рисунки генерируют очень большую перегрузку памяти, если они не были правильно отобраны.

Пример использования в RecyclerViewAdapter:

@Override 
public void onBindViewHolder(CustomViewHolder viewHolder, int position) { 
    String imageUri = "";//local or remote image uri address 
    //viewHolder.imgView: reference to your imageview 
    //before you call the displayImage you have to 
    //initialize imageloader in anywhere in your code for once. 
    //(Generally done in the Application class extender.) 
    ImageLoader.getInstance().displayImage(imageUri, viewHolder.imgView); 
} 

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

Glide.with(context) 
    .load(imageUri) 
    .placeholder(R.drawable.myplaceholder) 
    .into(imageView); 
+0

почему Glide почему бы не использовать Picasso? любое преимущество? – mhdjazmati

+0

Пикассо лучше +1 – delive

1

вы должны отменить старый запрос в "onBindViewHolder" метод:

try{ 
     ((SpecialOfferViewHolder)viewHolder).imageContainer.cancelRequest(); 
}catch(Exception e) { 

} 

запомнить, чтобы сохранить контейнер изображения в viewHolder:

public void onResponse(ImageContainer response, boolean arg1) { 
       ((SpecialOfferViewHolder)viewHolder).imageContainer=response; 

}

-1

Вы должны использовать Picasso. Мощная библиотека загрузки и кеширования изображений для Android. Простая в использовании и мощная библиотека. Используя эту библиотеку, вы можете получать изображения асинхронно или синхронно из ресурсов, ресурсов, файлов, поставщиков контента.

3

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

Решение заключается в следующем:

  1. магазин некоторые уникальный идентификатор в View Holder во onBindViewHolder (это происходит синхронно, так что если VH перерабатывается это будет перезаписан)
  2. Затем загрузите изображение асинхронно (с AsynchTask , RxJava и т. Д.) И передать этот уникальный идентификатор в асинхронном вызове для ссылки
  3. Наконец, в методе загрузки изображений для последующей обработки (onPostExecute для AsyncTasks) убедитесь, что идентификатор, переданный в async-запросе, совпадает с идентификатором текущий идентификатор присутствует в держателе вида.

Пример загрузка иконка из приложений в фоновом режиме с RxJava:

public void loadIcon(final ImageView appIconView, final ApplicationInfo appInfo, final String uniqueAppID) { 
    Single.fromCallable(() -> { 
      return appIconView.getContext().getPackageManager().getApplicationIcon(appInfo); 
     }) 
      .subscribeOn(Schedulers.computation()) 
      .observeOn(AndroidSchedulers.mainThread()) 
      .subscribe(drawable -> { 
       if (uniqueAppID.equals(mUniqueAppID)) { // Show always the correct app icon 
        appIconView.setImageDrawable(drawable); 
       } 
      } 
     ); 
} 

Здесь mUniqueAppID это поле владельца вида изменен onBindViewHolder

0

Я хотел бы добавить к хорошей практике:

В хорошей практике вы установите свой ImageView для отображения ничего в начале bindViewHolder (держатель VH, позиция int) с помощью setDrawable (null) o r похоже.

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