Intereting Posts

Android: видимые изображения ListView мерцают при добавлении данных в ArrayAdapter

У меня есть пользовательский адаптер, который расширяет ArrayAdapter, он реализует шаблоны владельца представления, чтобы отображать данные (текст + изображение) из веб-службы.

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

Я также использую кеш-память в виде бит + ram.

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

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

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

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

Есть ли способ обойти это нежелательное поведение?

Это класс, выполняющий загрузку и управление асинхронными задачами

public class ImageDownloader { private ImageCache mCache; private int reqWidth; private int reqHeight; public void download(String url, ImageView imageView, ImageCache imageCache, int reqHeight, int reqWidth) { mCache = imageCache; this.reqHeight = reqHeight; this.reqWidth = reqWidth; if (cancelPotentialDownload(url, imageView)) { BitmapDownloaderTask task = new BitmapDownloaderTask(imageView); DownloadedDrawable downloadedDrawable = new DownloadedDrawable(task); imageView.setImageDrawable(downloadedDrawable); task.execute(url); } } private class BitmapDownloaderTask extends AsyncTask<String, Void, Bitmap> { private String url; private WeakReference<ImageView> imageViewReference; public BitmapDownloaderTask(ImageView imageView) { imageViewReference = new WeakReference<ImageView>(imageView); } @Override protected Bitmap doInBackground(String... strings) { Bitmap bitmap = null; try { bitmap = mCache.getBitmapFromURL(strings[0], reqWidth, reqHeight); } catch (IOException e) { } return bitmap; } @Override protected void onPostExecute(Bitmap bitmap) { if (isCancelled()) bitmap = null; if (imageViewReference != null) { ImageView imageView = imageViewReference.get(); BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView); if (imageView != null) { imageView.setImageBitmap(bitmap); } } } } private static class DownloadedDrawable extends ColorDrawable { private WeakReference<BitmapDownloaderTask> bitmapDownloaderTaskReference; public DownloadedDrawable(BitmapDownloaderTask bitmapDownloaderTask) { bitmapDownloaderTaskReference = new WeakReference<BitmapDownloaderTask>(bitmapDownloaderTask); } public BitmapDownloaderTask getBitmapDownloaderTask() { return bitmapDownloaderTaskReference.get(); } } private static BitmapDownloaderTask getBitmapDownloaderTask(ImageView imageView) { if (imageView != null) { Drawable drawable = imageView.getDrawable(); if (drawable instanceof DownloadedDrawable) { DownloadedDrawable downloadedDrawable = (DownloadedDrawable)drawable; return downloadedDrawable.getBitmapDownloaderTask(); } } return null; } private static boolean cancelPotentialDownload(String url, ImageView imageView) { BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView); if (bitmapDownloaderTask != null) { String bitmapUrl = bitmapDownloaderTask.url; if ((bitmapUrl == null) || (!bitmapUrl.equals(url))) { bitmapDownloaderTask.cancel(true); } else { return false; } } return true; } 

}

Это адаптер:

Private class PlaceAdapter расширяет ArrayAdapter {final int viewResourceId;

  public PlaceAdapter(Context context, int textViewResourceId, List<PlaceModel> objects) { super(context, textViewResourceId, objects); viewResourceId = textViewResourceId; } @Override public View getView(final int position, View convertView, ViewGroup parent) { ViewHolder holder; if (convertView == null) { LayoutInflater inflater = getLayoutInflater(); convertView = inflater.inflate(viewResourceId, null); holder = new ViewHolder(convertView); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } PlaceModel place = getItem(position); holder.name.setText(place.getName()); holder.address.setText(place.getVicinity()); holder.position = position; if (place.getIcon() != null) { String url = mImageViewUrls.get(holder.image); if (url == null || (url != null && !url.equals(place.getIcon()))) { mDownloader.download(place.getIcon(), holder.image, mCache, 100, 100); mImageViewUrls.put(holder.image, place.getIcon()); } } else { holder.image.setImageBitmap(null); mImageViewUrls.remove(holder.image); } return convertView; } } private class ViewHolder { public final ImageView image; public final TextView name; public final TextView address; public int position; public ViewHolder(View row) { image = (ImageView) row.findViewById(R.id.placeRow_imageView); name = (TextView) row.findViewById(R.id.placeRow_placeName); address = (TextView) row.findViewById(R.id.placeRow_placeAddress); } } 

mImageViewUrls – это WeakHashMap<ImageView, String> который сопоставляется между ImageView и url, поэтому избыточные вызовы асинхронных задач могут быть уменьшены путем проверки того, что ImageView уже показывает требуемое изображение. Без этой реализации мерцание происходит во всех видимых изображениях при изменении данных. При этом это происходит только с некоторыми изображениями.

EDIT: я попытался устранить возможные причины этой проблемы, сначала я попытался полностью обойти реализацию кэша и загрузить каждый растровый рисунок из сети, а затем попробовал обернуть мой адаптер с помощью Endless-адаптера CommonsWare, и с обоих я получил тот же результат. Это оставляет только класс ImageDownloader и мой адаптер как возможные причины. Я полностью потерял это.

Solutions Collecting From Web of "Android: видимые изображения ListView мерцают при добавлении данных в ArrayAdapter"

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

В вашем случае добавьте файл holder.image.setImageBitmap (null); После владельца = (ViewHolder) convertView.getTag ();

Итак, ваш метод getView () будет выглядеть так:

@Override public View getView (конечная позиция int, View convertView, родительская группа ViewGroup) {

 ... if (convertView == null) { LayoutInflater inflater = getLayoutInflater(); convertView = inflater.inflate(viewResourceId, null); holder = new ViewHolder(convertView); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); holder.image.setImageBitmap(null) } ... return convertView; 

}

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

Метод getView () адаптера List вызывается несколько раз. Каждый раз, когда вызывается getView (), вы пытаетесь загрузить и установить изображение в строке. К моменту загрузки изображения из сети строка в списке, который запросил для этого изображения, мог перемещаться из видимой части экрана, но адаптер пытается повторно использовать эту строку, и это приводит к установке ранее запрошенного изображения в новой строке в этой позиции (Ранее запрошенная позиция строки).

Попробуйте этот подход в адаптере, установите требуемый URL-адрес в ImageView с помощью setTag:

 if(item.get_referenceImage().length()!=0){ //with view, hold the url. This is used to verify that right image is loaded on download completion holder.refImageView.setTag(item.get_referenceImage()); imageManager.loadImage(new MetaItem(item.get_referenceImage(), holder.refImageView)); }else{ //no image, set default Log.d(TAG, "No images found for the view setting default image"); holder.refImageView.setTag(null); //this is req, otherwise image cache will retain previous image holder.refImageView.setImageResource(R.drawable.blank_96_1382x); } 

В кеше, перед загрузкой растрового изображения проверьте, загружено ли правильное изображение:

  String url = (String) m_imageViewRef.get().getTag(); if(url != null && url.compareTo(m_metaItem.m_url) == 0 ){ m_metaItem.m_imageViewRef.get().setImageBitmap(result); } 

PS: MetaItem – это просто POJO, чтобы иметь ссылку на URL изображения и изображения

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

В ваших адаптерах getView () выполните:

 // schedule rendering: final String path = ... (set path here); if (holder.lastImageUrl == null || !holder.lastImageUrl.equals(path) || holder.headerImageView.getDrawable() == null) { // refresh image imageLoader.displayImage(imageUri, imageAware); } else { // do nothing, image did not change and does not need to be updated } 

При успешном выполнении (добавьте ImageLoadingListener) вы установите параметр holder.lastImageUrl = path, при неудаче и отмените настройку holder.lastImageUrl на null, чтобы он перезагрузился в следующий раз.

Возможно, вы вызываете adapter.notifyDatasetChanged() чего listview перезагружает свои списки. Попробуйте не вызывать adapter.notifyDatasetChanged() ваш список должен автоматически обновляться при прокрутке. Но при прокрутке это может вызвать исключение ArrayIndexOutOfBounds .