Intereting Posts
Как отобразить GIF в приложении для Android-приложения с поддержкой Android? TabLayout удаляет ненужную прокрутку Консоль разработчика Google Play: бесплатное бета-тестирование платных приложений? Android – независимый инструмент для тестирования фрагментов фрагментов Мне нужно протестировать мое приложение на разных виртуальных устройствах Пиратство, пиратство, пиратство. Что я могу сделать? Есть ли бесплатная CMS для мобильных приложений? Как отобразить диалог прогресса перед началом активности в Android? Активность Android по блокировке блокировки по умолчанию Невозможно связать локальный XXXX для отладчика Android – Как отключить кнопку поиска, как реализовать onSearchRequested ()? Каково главное преимущество и недостаток «не держать действия» в android Выровнять отметку места в андроиде Значение ошибки Android Studio: переопределенные параметры аннотированных параметров @NonNull Как я могу использовать SensorManager.getOrientation для управления наклоном, например «My Paper Plane»?

Изображения повторяются в ListView

Я внедрил приложение для Android, которое должно загружать изображения с сервера и отображать их в ListView, но очень интересная вещь возникает во время загрузки изображений

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

http://www.youtube.com/watch?v=lxY-HAuJO0o&feature=youtu.be

Вот мой код адаптера ListView.

public class MoviesAdapter extends ArrayAdapter<ParkCinema> { private ArrayList<ParkCinema> movieDataItems; private Activity context; public MoviesAdapter(Activity context, int textViewResourceId, ArrayList<ParkCinema> movieDataItems) { super(context, textViewResourceId, movieDataItems); this.context = context; this.movieDataItems = movieDataItems; } @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { LayoutInflater vi = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); convertView = vi.inflate(R.layout.movie_data_row, null); } ParkCinema movie = movieDataItems.get(position); if (movie!=null){ ImageView imageView = (ImageView) convertView.findViewById(R.id.movie_thumb_icon); String url = movie.poster(); if (url!=null) { Bitmap bitmap = fetchBitmapFromCache(url); if (bitmap==null) { new BitmapDownloaderTask(imageView).execute(url); } else { imageView.setImageBitmap(bitmap); } } } return convertView; } private LinkedHashMap<String, Bitmap> bitmapCache = new LinkedHashMap<String, Bitmap>(); private void addBitmapToCache(String url, Bitmap bitmap) { if (bitmap != null) { synchronized (bitmapCache) { bitmapCache.put(url, bitmap); } } } private Bitmap fetchBitmapFromCache(String url) { synchronized (bitmapCache) { final Bitmap bitmap = bitmapCache.get(url); if (bitmap != null) { return bitmap; } } return null; } private class BitmapDownloaderTask extends AsyncTask<String, Void, Bitmap> { private String url; private final WeakReference<ImageView> imageViewReference; public BitmapDownloaderTask(ImageView imageView) { imageViewReference = new WeakReference<ImageView>(imageView); } @Override protected Bitmap doInBackground (String... source) { url = source[0]; Bitmap image; try{ image = BitmapFactory.decodeStream(new URL(url).openConnection().getInputStream()); return image; } catch(Exception e){Log.e("Error", e.getMessage()); e.printStackTrace();} return null; } @Override protected void onPostExecute(Bitmap bitmap) { addBitmapToCache(url, bitmap); imageViewReference.get().setImageBitmap(bitmap); } } } 

Редактировать 3:

 public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { LayoutInflater vi = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); convertView = vi.inflate(R.layout.movie_data_row, null); } ParkCinema movie = movieDataItems.get(position); ImageView imageView = (ImageView) convertView.findViewById(R.id.movie_thumb_icon); if (movie!=null){ String url = movie.poster(); if (url != null) { Bitmap bitmap = fetchBitmapFromCache(url); if (bitmap == null) { imageView.setImageResource(R.drawable.no_image); new BitmapDownloaderTask(imageView).execute(url); } else { imageView.setImageBitmap(bitmap); } } else { imageView.setImageResource(R.drawable.no_image); } } else { imageView.setImageResource(R.drawable.no_image); } return convertView; } 

Solutions Collecting From Web of "Изображения повторяются в ListView"

Ага! Думаю, я знаю эту проблему. Прямо сейчас, ваш метод getView устанавливает ваш ImageView следующим образом:

  1. Получает объект фильма в позиции
  2. Вытаскивает URL-адрес миниатюры фильма
  3. Используя этот URL-адрес, он пытается найти изображение в кеше
  4. Если он находит изображение, он устанавливает его
  5. Если он не может найти изображение, он запускает сетевой запрос async, чтобы получить его, и устанавливает его после его загрузки.

Ваш issus возникает, поскольку ListView повторно использует строки ' View s. Когда первый View прокручивается с экрана, а не раздувает новый, ListView передает вид внеэкранной строки View в виде convertView для повторного использования (это для эффективности).

Когда ваш getView получает convertView который используется повторно, его ImageView уже был установлен из строки, имевшей его раньше, поэтому вы видите старое изображение из представления « convertView . С помощью текущего процесса getView вы проверяете образ новой строки, и он не находит его в кеше, он запускает запрос на его загрузку. Пока вы загружаете, вы видите старое изображение, пока не получите новое изображение.

Чтобы исправить это, вам нужно убедиться, что вы сразу же установите каждое поле в представлении строки, чтобы убедиться, что у вас нет View s, показывающего устаревшие данные. Я предлагаю вам установить ImageView на ресурс по умолчанию, который вы устанавливаете в своем R.layout.movie_data_row пока вы ждете загрузки сети, чтобы получить изображение.

 @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { LayoutInflater vi = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); convertView = vi.inflate(R.layout.movie_data_row, null); } ParkCinema movie = movieDataItems.get(position); ImageView imageView = (ImageView) convertView.findViewById(R.id.movie_thumb_icon); if (movie != null) { String url = movie.poster(); if (url != null) { Bitmap bitmap = fetchBitmapFromCache(url); if (bitmap == null) { // Set the movie thumbnail to the default icon while we load // the real image imageView.setImageResource(R.drawable.movie_thumb_icon); new BitmapDownloaderTask(imageView).execute(url); } else { // Set the image to the bitmap we get from the cache imageView.setImageBitmap(bitmap); } } else { // Set the movie thumbnail to the default icon, since it doesn't // have a thumbnail URL imageView.setImageResource(R.drawable.movie_thumb_icon); } } else { // Set the movie thumbnail to the default icon, since there's no // movie data for this row imageView.setImageResource(R.drawable.movie_thumb_icon); } 

-Редактировать-

Обновлено, чтобы быть еще более надежным, используя ваш drawable . У вас также есть проблема с вашей BitmapDownloaderTask , она не обрабатывает ошибки / null. Попробуйте добавить это также.

 @Override protected void onPostExecute(Bitmap bitmap) { addBitmapToCache(url, bitmap); if (bitmap == null) { // Set the movie thumbnail to the default icon, since an error occurred while downloading imageViewReference.get().setImageResource(R.drawable.movie_thumb_icon); } else { imageViewReference.get().setImageBitmap(bitmap); } } 

У меня была эта проблема и реализован lruCache … я считаю, что вам нужно api 12 и выше или использовать библиотеку совместимости v4. LurCache – это быстрая память, но у него также есть бюджет, поэтому, если вы беспокоитесь об этом, вы можете использовать diskcache … его все описано здесь http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html

Теперь я предоставлю свою реализацию, которую я вызываю из любой точки мира:

// где сначала строка, а другая – изображение для загрузки

 DownloadImageTask.getInstance().loadBitmap(avatarURL, iv_avatar); 

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

  public class DownloadImageTask { private LruCache<String, Bitmap> mMemoryCache; /* create a singleton class to call this from multiple classes */ private static DownloadImageTask instance = null; public static DownloadImageTask getInstance() { if (instance == null) { instance = new DownloadImageTask(); } return instance; } //lock the constructor from public instances private DownloadImageTask() { // Get max available VM memory, exceeding this amount will throw an // OutOfMemory exception. Stored in kilobytes as LruCache takes an // int in its constructor. final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); // Use 1/8th of the available memory for this memory cache. final int cacheSize = maxMemory / 8; mMemoryCache = new LruCache<String, Bitmap>(cacheSize) { @Override protected int sizeOf(String key, Bitmap bitmap) { // The cache size will be measured in kilobytes rather than // number of items. return bitmap.getByteCount() / 1024; } }; } public void loadBitmap(String avatarURL, ImageView imageView) { final String imageKey = String.valueOf(avatarURL); final Bitmap bitmap = getBitmapFromMemCache(imageKey); if (bitmap != null) { imageView.setImageBitmap(bitmap); } else { imageView.setImageResource(R.drawable.ic_launcher); new DownloadImageTaskViaWeb(imageView).execute(avatarURL); } } private void addBitmapToMemoryCache(String key, Bitmap bitmap) { if (getBitmapFromMemCache(key) == null) { mMemoryCache.put(key, bitmap); } } private Bitmap getBitmapFromMemCache(String key) { return mMemoryCache.get(key); } /* a background process that opens a http stream and decodes a web image. */ class DownloadImageTaskViaWeb extends AsyncTask<String, Void, Bitmap> { ImageView bmImage; public DownloadImageTaskViaWeb(ImageView bmImage) { this.bmImage = bmImage; } protected Bitmap doInBackground(String... urls) { String urldisplay = urls[0]; Bitmap mIcon = null; try { InputStream in = new java.net.URL(urldisplay).openStream(); mIcon = BitmapFactory.decodeStream(in); } catch (Exception e) { Log.e("Error", e.getMessage()); e.printStackTrace(); } addBitmapToMemoryCache(String.valueOf(urldisplay), mIcon); return mIcon; } /* after decoding we update the view on the mainUI */ protected void onPostExecute(Bitmap result) { bmImage.setImageBitmap(result); } } 

}

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

  public class MoviesAdapter extends ArrayAdapter<ParkCinema> { private ArrayList<ParkCinema> movieDataItems; private Activity context; public MoviesAdapter(Activity context, int textViewResourceId, ArrayList<ParkCinema> movieDataItems) { super(context, textViewResourceId, movieDataItems); this.context = context; this.movieDataItems = movieDataItems; } @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { LayoutInflater vi = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); convertView = vi.inflate(R.layout.movie_data_row, null); holder = new ViewHolder(); holder.imageView = (BarImageView) convertView.findViewById(R.id.movie_thumb_icon); } else { holder = (ViewHolder) convertView.getTag(); } ParkCinema movie = movieDataItems.get(position); if (movie!=null){ String url = movie.poster(); if (url!=null) { Bitmap bitmap = fetchBitmapFromCache(url); if (bitmap==null) { new BitmapDownloaderTask(imageView).execute(url); } else { imageView.setImageBitmap(bitmap); } } } return convertView; } private LinkedHashMap<String, Bitmap> bitmapCache = new LinkedHashMap<String, Bitmap>(); private void addBitmapToCache(String url, Bitmap bitmap) { if (bitmap != null) { synchronized (bitmapCache) { bitmapCache.put(url, bitmap); } } } private Bitmap fetchBitmapFromCache(String url) { synchronized (bitmapCache) { final Bitmap bitmap = bitmapCache.get(url); if (bitmap != null) { return bitmap; } } return null; public static class ViewHolder { ImageView imageView; } } 

Я потратил часы, пытаясь понять это тоже … Благодаря решению Стивена Байла … Вот мое решение чего-то похожего, когда пользователь выбирает элемент из списка:

 adapter.setSelectedIndex(position); 

Затем в пользовательском адаптере:

 public void setSelectedIndex(int ind) { selectedIndex = ind; notifyDataSetChanged(); } 

А затем, наконец, в методе getView адаптера:

 if(selectedIndex!= -1 && position == selectedIndex) { holder.tab.setBackgroundColor(Color.BLACK); } else{ holder.tab.setBackgroundColor(Color.DKGRAY); } 

Поэтому в заключение убедитесь, что вы назначаете значения по умолчанию

В моем случае я использовал библиотеку Picasso вместо AsyncTask для загрузки изображения. Введите ссылку здесь

Также напишите, если условие else, то есть значение null для изображения, если URL недоступен

Вместо использования объекта convertview каждый раз создается новое представление.

 View localView = ((LayoutInflater)parentscreen.getSystemService("layout_inflater")).inflate(R.layout.activity_list_row, null); 

Надувая, как указано выше.