2012-03-21 10 views
4

私はImageLoaderクラスを通してURLから画像をロードするアダプタを備えたlistViewを持っています。 問題は、リスト項目が画面からスクロールされるまで、画面上のイメージが表示/ロードされないことです。Imageloaderはlistitemが画面から消えた後にリスト内の画像をロードするだけです

基本的に、listViewが表示されますが、スクロールして再びスクロールアップするまで画像はロードされません。これは、リスト内のすべての項目に適用され、最初に画像がロードされていないときにスクロールして戻すまで表示されます。

私はImageLoaderクラスを自分で作成していません。Imは最初の表示に画像が読み込まれない理由を正確に理解するのに苦労しています。キューとは別のものをたくさん試しましたが、何も助けてくれないようです。

ImageLoaderクラス:

public class ImageLoader { 

    //the simplest in-memory cache implementation. This should be replaced with something like SoftReference or BitmapOptions.inPurgeable(since 1.6) 
    private HashMap<String, Bitmap> cache=new HashMap<String, Bitmap>(); 
    private File cacheDir; 

    public ImageLoader(Context context){ 
     //Make the background thead low priority. This way it will not affect the UI performance 
     photoLoaderThread.setPriority(Thread.NORM_PRIORITY-1); 

     //Find the dir to save cached images 
     if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)) 
      cacheDir=new File(android.os.Environment.getExternalStorageDirectory(),"LazyList"); 
     else 
      cacheDir=context.getCacheDir(); 
     if(!cacheDir.exists()) 
      cacheDir.mkdirs(); 
    } 

    final int stub_id=R.drawable.nopic; 

    public void DisplayImage(String url, Activity activity, ImageView imageView, ProgressBar progressBar) 
    { 
     if(cache.containsKey(url)){ 
      imageView.setImageBitmap(cache.get(url)); 
      if (progressBar != null){ 
       progressBar.setVisibility(View.GONE); 
       imageView.setVisibility(View.VISIBLE); 
      } 
     }else{ 
      queuePhoto(url, activity, imageView, progressBar); 

      if(progressBar != null){ 
       imageView.setVisibility(View.GONE); //ADDED 
       progressBar.setVisibility(View.VISIBLE); 
      } else { 
       imageView.setImageResource(stub_id); 
      } 
     }  
    } 

    private void queuePhoto(String url, Activity activity, ImageView imageView, ProgressBar progressBar) 
    { 

     //This ImageView may be used for other images before. So there may be some old tasks in the queue. We need to discard them. 
     photosQueue.Clean(imageView); 
     PhotoToLoad p = new PhotoToLoad(url, imageView, progressBar); 
     synchronized(photosQueue.photosToLoad){ 
      photosQueue.photosToLoad.push(p); 
      photosQueue.photosToLoad.notifyAll(); 
     } 

     //start thread if it's not started yet 
     if(photoLoaderThread.getState()==Thread.State.NEW) 
      photoLoaderThread.start(); 
    } 

    private Bitmap getBitmap(String url) 
    { 
     //I identify images by hashcode. Not a perfect solution, good for the demo. 
     String filename=String.valueOf(url.hashCode()); 
     File f=new File(cacheDir, filename); 

     //from SD cache 
     Bitmap b = decodeFile(f); 
     if(b!=null) 
      return b; 

     //from web 
     try { 
      Bitmap bitmap=null; 
      InputStream is=new URL(url).openStream(); 
      OutputStream os = new FileOutputStream(f); 
      Utils.CopyStream(is, os); 
      os.close(); 
      bitmap = decodeFile(f); 
      return bitmap; 
     } catch (Exception ex){ 
      ex.printStackTrace(); 
      return null; 
     } 
    } 

    //decodes image and scales it to reduce memory consumption 
    private Bitmap decodeFile(File f){ 
     try { 
      //decode image size 
      BitmapFactory.Options o = new BitmapFactory.Options(); 
      o.inJustDecodeBounds = true; 
      BitmapFactory.decodeStream(new FileInputStream(f),null,o); 

      //Find the correct scale value. It should be the power of 2. 
      int requiredSize = 100; 

      int width_tmp=o.outWidth, height_tmp=o.outHeight; 
      int scale=1; 
      while(true){ 
       if(width_tmp/2<requiredSize || height_tmp/2<requiredSize) 
        break; 
       width_tmp/=2; 
       height_tmp/=2; 
       scale*=2; 
      } 

      //decode with inSampleSize 
      BitmapFactory.Options o2 = new BitmapFactory.Options(); 
      o2.inSampleSize=scale; 
      return BitmapFactory.decodeStream(new FileInputStream(f), null, o2); 
     } catch (FileNotFoundException e) {} 
     return null; 
    } 

    //Task for the queue 
    private class PhotoToLoad 
    { 
     public String url; 
     public ImageView imageView; 
     public ProgressBar progressBar; 
     public PhotoToLoad(String u, ImageView i, ProgressBar p){ 
      url=u; 
      imageView=i; 
      progressBar = p; 
     } 
    } 

    PhotosQueue photosQueue=new PhotosQueue(); 

    public void stopThread(){ 
     photoLoaderThread.interrupt(); 
    } 

    //stores list of photos to download 
    class PhotosQueue 
    { 
     private Stack<PhotoToLoad> photosToLoad=new Stack<PhotoToLoad>(); 

     //removes all instances of this ImageView 
     public void Clean(ImageView image) 
     { 
      for(int j=0 ;j<photosToLoad.size();){ 
       if(photosToLoad.get(j).imageView==image) 
        photosToLoad.remove(j); 
       else 
        ++j; 
      } 
     } 
    } 

    //XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 
    // PHOTOSLOADER: Thread 
    //XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 

    class PhotosLoader extends Thread { 
     public void run() { 
      try { 
       while(true) 
       { 
        //thread waits until there are any images to load in the queue 
        if(photosQueue.photosToLoad.size() == 0) 
         synchronized(photosQueue.photosToLoad){ 
          photosQueue.photosToLoad.wait(); 
         } 
        if(photosQueue.photosToLoad.size() != 0) 
        { 
         PhotoToLoad photoToLoad; 
         synchronized(photosQueue.photosToLoad){ 
                  photoToLoad = photosQueue.photosToLoad.pop();              photosQueue.photosToLoad.remove(photoToLoad); 
         } 
         Bitmap bmp = getBitmap(photoToLoad.url); 
         cache.put(photoToLoad.url, bmp); 
         Object tag = photoToLoad.imageView.getTag(); 
         if(tag != null && ((String)tag).equals(photoToLoad.url)){ 
          BitmapDisplayer bd = new BitmapDisplayer(bmp, photoToLoad.imageView, photoToLoad.progressBar); 
          Activity a = (Activity)photoToLoad.imageView.getContext(); 
          a.runOnUiThread(bd); 
         } 
        } 
        if(Thread.interrupted()) 
         break; 
       } 
      } catch (InterruptedException e) { 
       //allow thread to exit 
      } 
     } 
    } 

    PhotosLoader photoLoaderThread = new PhotosLoader(); 


    //XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 
    // BITMAP DISPLAYER 
    //XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 

    //Used to display bitmap in the UI thread 
    class BitmapDisplayer implements Runnable{ 
     Bitmap  bitmap; 
     ImageView imageView; 
     ProgressBar progressBar; 
     public BitmapDisplayer(Bitmap b, ImageView i, ProgressBar p){ 
      bitmap = b; 
      imageView = i; 
      progressBar = p; 
     } 
     public void run(){ 
      if(bitmap != null){ 
       imageView.setImageBitmap(bitmap); 
       if (progressBar != null){ 
        imageView.setVisibility(View.VISIBLE); 
        progressBar.setVisibility(View.GONE); 
       } 
      }else{ 
       if (progressBar != null){ 
        progressBar.setVisibility(View.VISIBLE); 
        imageView.setVisibility(View.GONE); 
       } 
      } 
     } 
    } 

    public void clearCache() { 
     //clear memory cache 
     cache.clear(); 

     //clear SD cache 
     File[] files = cacheDir.listFiles(); 
     for(File f:files) 
      f.delete(); 
    } 
} 

アダプタ:

public class Adapter_Agenda extends BaseAdapter { 

    // DEBUG 
    private final String TAG = this.getClass().getSimpleName(); 

    private LayoutInflater inflater = null; 
    public ViewHolder holder; 
    private ArrayList<Agenda> agendas; 
    UnixTimeToStringConverter unixConverter; 
ImageLoader imageLoader; 

    View vi; 

    public Adapter_Agenda(Context context, ArrayList<Agenda> agendas) { 

     this.agendas = agendas; 
     unixConverter = new UnixTimeToStringConverter(); 
     imageLoader = new ImageLoader(context); 
     inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
    } 

    public int getCount() { 
     return agendas.size(); 
    } 

    @Override 
    public Agenda getItem(int position) { 
     return agendas.get(position); 
    } 

    public long getItemId(int position) { 
     return position; 
    } 

    public View getView(int position, View convertView, ViewGroup parent) { 

     vi = convertView; 

     if(convertView == null){ 

      vi = inflater.inflate(R.layout.item_list_agenda, null); 
      holder = new ViewHolder(); 

      holder.header  = (TextView)vi.findViewById(R.id.item_list_header); 
        holder.image   = (ImageView)vi.findViewById(R.id.item_list_image); 
      holder.time   = (TextView)vi.findViewById(R.id.item_list_time); 
      holder.description = (TextView)vi.findViewById(R.id.item_list_description); 

      vi.setTag(holder); 
      holder = (ViewHolder)vi.getTag(); 
     } else { 
      holder = (ViewHolder)vi.getTag(); 
     } 

     holder.header.setText(agendas.get(position).getHeader()); 
     imageLoader.DisplayImage(agendas.get(position).getImageURL(), activity, holder.image, null); 
    holder.time.setText(unixConverter.getUnixDateFormated(agendas.get(position).getStartTime())); 

     return vi; 
    } 

    //XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 
    // VIEWHOLDER 
    //XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 
    public class ViewHolder{ 

     public TextView   time; 
      public ImageView    image; 
     public TextView   header; 
     public TextView   description; 

    } 
} 
+1

あなたのアダプタの画像を設定したことがない前に

holder.image.setTag(agendas.get(position).getImageURL()); 

を置きますか? ImageLoaderクラスは使用されていませんか? –

+0

申し訳ありませんが、間違ったアダプタを貼り付けました。私はそれをどのように使用しているかを反映するように編集しました。 – DecodeGnome

答えて

2

はちょうど

imageLoader.DisplayImage(agendas.get(position).getImageURL(), activity, holder.image, null); 
+1

魅力のように動作し、乾杯! – DecodeGnome

+0

それは私のために働いていなかった@Jahanzaib。他に可能な解決策は何でしょうか? – bhaskar

2

画像が新しいスレッドにダウンロードされます。あなたがやっていることは、非同期ファイルのダウンロードです。 問題は、実際にダウンロードする前にイメージを「使用する」ことです。結果として、画像を表示することができない。あなたがしなければならない何

画像がダウンロードされたときにコールバックのようなSTHを作成します。アダプタで実装されるインタフェースを作成することで、これを実現できます。

インターフェイスメソッドが呼び出されるたびに、イメージがダウンロードされ、 "notifyDatasetChange()"を呼び出してアダプタに通知することができます。

+0

okですが、ImageLoaderクラスのどこでコールバックを追加する必要がありますか?私はそれが正常に動作することはできません.. – DecodeGnome

関連する問題