2012-03-23 9 views
3

カスタマイズされたアダプタ(画像とテキスト付き)でListViewを表示したいと思います。アンドロイド:リストビューで非同期ピクチャをロード

イメージは離れたサーバーからロードされるので、私はAsyncTaskを使用することに決めました。

実際には、画像がうまく表示されますが、私はすぐにスクロールダウンした場合、間違った絵を1/2 secondes中に表示される(ロード後に、正しい画像が表示されます)

ここでは私のアダプタのコードです:

public class GiAdapter extends BaseAdapter { 

    private Context mContext; 

    private List<SiteStaff> mListAppInfo; 
    private HashMap<Integer, ImageView> views; 
    private HashMap<String,Bitmap> oldPicts = new HashMap<String,Bitmap>(); 
    private LayoutInflater mInflater; 
    private boolean auto; 

    private final String BUNDLE_URL = "url"; 
    private final String BUNDLE_BM = "bm"; 
    private final String BUNDLE_POS = "pos"; 
    private final String BUNDLE_ID = "id"; 

    public GiAdapter(Context context, List<SiteStaff> list) { 
     mContext = context; 
     mListAppInfo = list; 
     views = new HashMap<Integer, ImageView>(); 
     mInflater = LayoutInflater.from(mContext); 

    } 

    @Override 
    public int getCount() { 
     return mListAppInfo.size(); 
    } 

    @Override 
    public Object getItem(int position) { 
     return mListAppInfo.get(position).getId(); 
    } 

    @Override 
    public long getItemId(int position) { 
     return mListAppInfo.get(position).getId(); 
    } 

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

     LinearLayout layoutItem; 

     // reuse of convertView 
     if (convertView == null) { 
      layoutItem = (LinearLayout) mInflater.inflate(R.layout.auto_gi, parent, false); 
     } else { 
      layoutItem = (LinearLayout) convertView; 
     } 

     // infos for the current element 
     SiteStaff entry = mListAppInfo.get(position); 

     //set some text fields 
     TextView name = (TextView) layoutItem.findViewById(R.id.name); 
     TextView size = (TextView) layoutItem.findViewById(R.id.size); 

     name.setText(entry.getName()); 
     size.setText(entry.getSize()); 

     // get the imageView for the current object 
     ImageView v = (ImageView) layoutItem.findViewById(R.id.gi_image); 

     // put infos in bundle and send to the LoadImage class 
     Bundle b = new Bundle(); 

     //url of the pict 
     b.putString(BUNDLE_URL, entry.getUrl()); 

     //position in the listView 
     b.putInt(BUNDLE_POS, position); 

     //id of the current object 
     b.putInt(BUNDLE_ID, entry.getId()); 

     //put info in the map in order to display in the onPostExecute 
     views.put(position, v); 

     // thread 
     new LoadImage().execute(b); 

     return layoutItem; 

    } 

    //asyncTackClass for loadingpictures 
    private class LoadImage extends AsyncTask<Bundle, Void, Bundle> { 

     @Override 
     protected Bundle doInBackground(Bundle... b) { 

      Bitmap bm =null; 

      //cache: for better performance, check if url alredy exists 
      if(oldPicts.get(b[0].getString(BUNDLE_URL))==null){ 
       bm = Utils.getBitMapFromUrl(b[0].getString(BUNDLE_URL)); 
       oldPicts.put(b[0].getString(BUNDLE_URL),bm); 
      }else{ 
       bm = oldPicts.get(b[0].getString(BUNDLE_URL)); 
      } 

      // get info from bundle 
      Bundle bundle = new Bundle(); 
      bundle.putParcelable(BUNDLE_BM, bm); 
      bundle.putInt(BUNDLE_POS, b[0].getInt(BUNDLE_POS)); 

      return bundle; 

     } 

     @Override 
     protected void onPostExecute(Bundle result) { 
      super.onPostExecute(result); 

      //get picture saved in the map + set 
      ImageView view = views.get(result.getInt(BUNDLE_POS)); 
      Bitmap bm = (Bitmap) result.getParcelable(BUNDLE_BM); 

      if (bm != null){ //if bitmap exists... 
       view.setImageBitmap(bm); 
      }else{ //if not picture, display the default ressource 
       view.setImageResource(R.drawable.unknow); 
      } 

     } 

    } 

} 

ありがとうございました!

答えて

5

これは私にも起こりました。私が理解したことは、新しいイメージがイメージビューにバインドされる前に、古いものがしばらく表示されていたということでした。あなたのケースでは、起こっていることは、あなたの正しい情報がonPostExecute()を表示していることです。これはいつかかかり、変換ビューに設定されているデータが表示されます。 uは、主にこの問題を解決することができ、2つの方法、あなたのgetViewメソッドで

()がありますが、ちょうど

layoutItem = (LinearLayout) mInflater.inflate(R.layout.auto_gi, parent, false); 

または

場合{} ... ELSE {}ブロックはとImageViewの設定変更onPostExecute()が呼び出されるまで、getView()のプレースホルダー・イメージの一部が呼び出されます。

これが役に立ちます。

+0

ありがとうございます〜私はあなたのコードを試して、画像が完全に表示されました! – johann

+3

最初のアプローチでは、リストビューの各行(かなり高価な操作)のビューが膨らんでいるため、ListViewはスムーズにスクロールしません。 – Christian

1

BaseAdapterを拡張してgetView(int position、View convertView、ViewGroup parent)を実装すると、convertViewは実際にリサイクルされたビューになります。

これは破壊されたビューです(最後に表示されたビューは画面から離れます)。あなたが行うことができることは、古いデータを表示しないようにconvertViewをクリアすることです。おそらく、画像を含むビューを見つけて、layoutItem.removeAll()を実行します。うまくいけば、それはビューをクリアし、あなたにはクリーンなレイアウトを提供します。

+0

ありがとうございました! BaseAdapterクラスのロジックを理解するのに役立ちました。 – johann

6

Shubhayuのように、アダプタが各行を構成するためにビューをリサイクルしているため、Asyncタスクがそのリサイクルされたビューへの参照を保持しているため、ListViewの行に間違ったイメージが表示されます。タスクが完了すると、ImageViewが更新されます。これは実際にはこの時点までに他の行に対応しています。

getViewのすべての呼び出しで新しいビューを膨張させると、スクロール時にListViewのパフォーマンスが低下することになります。あなたは(正確に)このオーバーヘッドを減らすためにconvertViewを使用しています。ちょうどImageViewをAsyncに正しく結びつけている部分がありません。私はここで解決策を見つけました:http://android-developers.blogspot.com/2010/07/multithreading-for-performance.html

要約すると、基本的には、タスクをカプセル化し、タスクが画像に適切に結び付けられている場合にのみ画像を更新するようにDrawableを拡張しています。あなたが直面している問題は、「並行処理」セクションで取り上げられています。特に、そのセクションの直前の段落を読んで、問題を引き起こしていることの概要を確認してください。

関連する問題