2010-12-11 15 views
2

私は利用可能な場合は、さまざまな画像で私のlistviewを埋めるために、以下のコードを実装しましたが問題は、画面上の他の要素に触れたり、どんな助けがあれば幸いです。Androidのリストビューの画像の移動

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はビューをリサイクルします。リサイクルされたビューを使用する場合は、ビットマップを既存または新しいimageViewに設定します。つまり、ビットマップがダウンロードされると、表示されているアイテムに対応しない可能性のあるイメージビューに設定されます。

私のアプリケーションでは、タグをイメージビューに設定します。ダウンロードが完了すると、ビューが見つからない場合はlistview.findViewByTag(タグ)を実行します。ビットマップを設定しないだけです。それが見つかったら私はそれを設定した。

ビットマップがメモリに存在する場合、getViewでは、ビットマップをイメージビューに設定します。ビットマップがメモリにない場合は、バックグラウンドジョブを開始し、そのURLのビューにタグを設定します。

このジョブは、存在する場合、またはインターネットからディスクからビットマップをロードします。

ビットマップが存在すると、requestCode、タグ、およびビットマップを使用して、自分のアクティビティでコールバックを呼び出します。

あなたの場合、タグとビットマップで十分です。

リストビューでfindViewByTagを実行する必要があります。ビューを見つけたら、ビットマップを設定します。ビューが見つからない場合は忘れてしまいます。アイテムが表示されたらgetViewが呼び出され、ビットマップがメモリまたはディスクに存在する必要があります。

リストビューのビューは、アイテムを最初に表示したときのビューであるとは期待できません。私はまた、バックグラウンドスレッドを開始する場合、リサイクルされたビューのビットマップが表示されないように、イメージdrawableをnullに設定する必要があるとも言いたいと思います。

+0

感謝を。私はもはや新しい画像が表示されるようになりましたが、新しい問題は、リストビューをスクロールしたり、画面上の別のUI要素をクリックして画像が消えてから2〜2秒後にポップアップした場合です。助言がありますか? – Paul

+0

getView関数をログに記録する必要があります。リストビューの高さまたは幅がwrap_contentの場合、次元を無限に再計算しようとする可能性があります。 getviewが呼び出され、イメージやビットマップを再読み込みすることがあります。私はいくつかのビットマップを記憶に残す。私はまだ解放されることができるイメージを私に知らせる何かを実装していない。そして、メモリ使用量に注意してください。必要に応じてビットマップでリサイクルを呼び出します。ネイティブメモリを解放します。 –

+0

リストビューのサイズは固定されている必要があります。 fill parentのように。親またはピクセルに一致します。 –

1

上記のように、基本的な問題は、リストビューがリスト行をレンダリングするために使用されるViewオブジェクトをリサイクルすることです。このため、download()メソッドに渡したImageViewリファレンスは、リスト内の別の「位置」に再利用され、観察している動作につながります。

私が解決した方法は、DownloadタスクでImageViewではなくList Adapterをパラメータとして取り込み、ダウンロードが完了したらアダプタでnotifyDataSetChanged()を呼び出させることでした。ダウンロードタスクはキャッシュにイメージ描画可能オブジェクトを追加し、アダプタのgetView()実装は単にImageViewを設定するためにキャッシュから描画オブジェクトを取得できます。

私はアプローチを説明し、ここでのコードを掲載しました:応答のためのhttp://mobrite.com/lazy-loading-images-in-a-listview/

関連する問題