2010-12-11 2 views
2

Я воспользовался приведенным ниже кодом, чтобы заполнить мой список различными изображениями, если они доступны, и это работает, но проблема в том, что касаются других элементов на экране или вы перемещаете быстрые изображения. Любая помощь будет очень благодарна ...Android listview images shift around

import java.lang.ref.WeakReference; 
import android.graphics.Bitmap; 
import android.graphics.BitmapFactory; 
import android.graphics.Color; 
import android.graphics.drawable.ColorDrawable; 
import android.graphics.drawable.Drawable; 
import android.os.AsyncTask; 
import android.view.View; 
import android.widget.ImageView; 
import android.widget.TableLayout; 

public class ImageDownloader { 

    public void download(String url, ImageView imageView, TableLayout imageTable) { 
     if (cancelPotentialDownload(url, imageView)) { 
     BitmapDownloaderTask task = new BitmapDownloaderTask(imageView, imageTable); 
     DownloadedDrawable downloadedDrawable = new DownloadedDrawable(task); 
     imageView.setImageDrawable(downloadedDrawable); 
     task.execute(url); 
     } 
    } 

    class BitmapDownloaderTask extends AsyncTask<String, Void, Bitmap> { 
     String url; 
     private final WeakReference<ImageView> imageViewReference; 
     private final WeakReference<TableLayout> imageTableReference; 

     public BitmapDownloaderTask(ImageView imageView, TableLayout imageTable) { 
      imageViewReference = new WeakReference<ImageView>(imageView); 
      imageTableReference = new WeakReference<TableLayout>(imageTable); 
     } 

      @Override 
      protected Bitmap doInBackground(String... params) { 
       BitmapFactory.Options o = new BitmapFactory.Options(); 
        o.inJustDecodeBounds = true; 
        BitmapFactory.decodeFile(params[0], o); 
        final int REQUIRED_SIZE=70; 

        //Find the correct scale value. It should be the power of 2. 
        int width_tmp=o.outWidth, height_tmp=o.outHeight; 
        int scale=4; 
        while(true){ 
         if(width_tmp/2<REQUIRED_SIZE || height_tmp/2<REQUIRED_SIZE) 
          break; 
         width_tmp/=2; 
         height_tmp/=2; 
         scale++; 
        } 
        //Decode with inSampleSize 
        BitmapFactory.Options o2 = new BitmapFactory.Options(); 
        o2.inSampleSize=scale;  
        return BitmapFactory.decodeFile(params[0], o2); 
      } 

      @Override 
      protected void onPostExecute(Bitmap result) { 
       if (isCancelled()) { 
        result = null; 
       } 

       if (imageViewReference != null) { 
        ImageView imageView = imageViewReference.get(); 
        TableLayout imageTable = imageTableReference.get(); 
        BitmapDownloaderTask bitmapDownloaderTask = ImageDownloader.getBitmapDownloaderTask(imageView); 
        // Change bitmap only if this process is still associated with it 
        if (this == bitmapDownloaderTask) { 
          imageView.setImageBitmap(result); 
          imageView.setVisibility(View.VISIBLE); 
          imageTable.setVisibility(View.VISIBLE); 
        }    
       } 
      } 
    } 

    static class DownloadedDrawable extends ColorDrawable { 
     private final WeakReference<BitmapDownloaderTask> bitmapDownloaderTaskReference; 

     public DownloadedDrawable(BitmapDownloaderTask bitmapDownloaderTask) { 
      super(Color.BLACK); 
      bitmapDownloaderTaskReference = 
       new WeakReference<BitmapDownloaderTask>(bitmapDownloaderTask); 
     } 

     public BitmapDownloaderTask getBitmapDownloaderTask() { 
      return bitmapDownloaderTaskReference.get(); 
     } 
    } 

    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 { 
       // The same URL is already being downloaded. 
       return false; 
      } 
     } 
     return true; 
    } 

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

ответ

1

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

В моем приложении я устанавливаю тег для своего изображения. Когда загрузка завершена, я делаю listview.findViewByTag (тег), если представление не найдено. Я просто не устанавливаю растровое изображение. если он найден, я его установил.

В getView, если битмап существует в памяти, я устанавливаю растровое изображение в свой вид изображения. Если битмап не в памяти, я запускаю фоновое задание, и я устанавливаю тег для своего представления для этого URL-адреса.

Эта работа будет загружать растровые изображения с диска, если они есть, или из Интернета.

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

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

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

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

+0

Спасибо за ответ. Хорошо, я получил изображения больше не сдвигаться, но новая проблема заключается в том, что если я прокручу вниз список или нажмите другой элемент пользовательского интерфейса на экране, изображения исчезнут, а затем всплывают второй или второй позже. Какие-либо предложения? – Paul

+0

Вы должны зарегистрировать функцию getView. Если в вашем представлении списка есть высота или ширина wrap_content, он может попытаться пересчитать размерность бесконечно. Он вызовет getview, и вы можете повторно загрузить изображения или растровые изображения. Я сохраняю некоторые растровые изображения в памяти. Я еще не реализовал что-то, что дало мне знать образ, который может быть освобожден первым. И обратите внимание на использование памяти. При необходимости переустановите вызов на растровые изображения. Это освободит родную память. –

+0

Listview должен иметь фиксированные размеры. Как заполнить родителя. Сопоставьте родительский или пиксельный. –

1

Как упоминалось в Loic, основная проблема заключается в том, что ListView перерабатывает объекты View, используемые для отображения строк списка. Из-за этого ссылка ImageView, которую вы передаете методу download(), может повторно использоваться для другой «позиции» в списке, что приводит к поведению, которое вы наблюдаете.

Способ, которым я решил это, состоял в том, чтобы задача «Загрузить» вместо адаптера «Список изображений» вместо «ImageView», а также чтобы он вызывал notifyDataSetChanged() на адаптере, когда загрузка завершена. Задача «Загрузка» добавляет изображение, обратное к кешу, и реализация getView() вашего адаптера может просто получить Drawable из кеша, чтобы установить ImageView.

Я описал подход и отправил код здесь: http://mobrite.com/lazy-loading-images-in-a-listview/