Подтвердить что ты не робот

RecyclerView: Async-загрузка изображений

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

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

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

4b9b3361

Ответ 1

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

Здесь есть два решения: хорошая практика и плохой взлом:

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

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

Ответ 2

Вы должны проверить универсальный загрузчик изображений . Он имеет кэш памяти, кэш диска и загружает ваши изображения асинхронно, поэтому не блокирует 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);

Ответ 3

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

Решение:

  • Сохраняйте уникальный идентификатор в держателе вида во время onBindViewHolder (это происходит синхронно, поэтому, если VH переработано, это будет перезаписано)
  • Затем загрузите изображение асинхронно (с помощью AsynchTask, RxJava и т.д.) и передайте этот уникальный идентификатор в асинхронном вызове для ссылки
  • Наконец, в методе загрузки изображений для последующей обработки (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

Ответ 4

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

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

}

не забудьте сохранить контейнер изображения в viewHolder:

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

}

Ответ 5

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

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

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

Ответ 6

То, что говорит MLProgrammer, совершенно правильно.

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

holder.setIsRecyclable(false);

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

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

Ответ 7

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