ANDROID:在listView中加载异步图片

joh*_*ann 3 android listview adapter android-asynctask

我想显示带有自定义适配器的ListView(带图片和文本).

图像是从远程服务器加载的,所以我决定使用AsyncTask.

实际上,图片显示效果很好但如果我快速向下滚动,则在1/2秒内显示错误的图片(加载后,会出现正确的图片)

这是我的适配器代码:

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

        }

    }

}
Run Code Online (Sandbox Code Playgroud)

谢谢 !

Chr*_*ian 6

像Shubhayu提到的,你是在为你的ListView行看到错误的图像,因为您的适配器是回收着眼于构建每一行,你的异步任务会保持到回收的观点引用.当您的任务完成后,他们将更新ImageView,此时可能实际上对应于其他行.

在每次调用getView时膨胀一个新视图的问题是你在滚动时最终会导致ListView性能不佳.您(正确地)使用convertView来减少这种开销,您只是错过了将ImageView正确绑定到Async的部分.我在这里找到了一个解决方案:http://android-developers.blogspot.com/2010/07/multithreading-for-performance.html

总之,它基本上是将Drawable扩展为封装任务,并且只有在任务与图像相关联时才更新图像."处理并发"一节解决了您所面临的问题.特别是,请阅读该部分之前的段落,以了解导致问题的原因摘要.


Shu*_*ayu 5

这也发生在我身上.我想到的是,在新图像被绑定到imageview之前,旧图像显示了一段时间.在您的情况下,正在发生的事情是您的正确信息显示onPostExecute(),这需要一段时间,并且在此之前显示转换视图中设置的任何数据.你有两种方法可以解决这个问题,

在你的getView()中,将if {} ... else {}块更改为just

layoutItem = (LinearLayout) mInflater.inflate(R.layout.auto_gi, parent, false);
Run Code Online (Sandbox Code Playgroud)

要么

在getView()中使用一些占位符图像设置imageview,直到调用onPostExecute().

希望这可以帮助.

  • 请注意,通过第一种方法,您实际上是为列表视图的每一行(一个非常昂贵的操作)膨胀视图,这意味着您的ListView将不会平滑滚动. (3认同)