我想显示带有自定义适配器的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);
}
}
}
}
谢谢!
答案 0 :(得分:6)
与Shubhayu提到的一样,您在ListView行中看到错误的图像,因为您的适配器正在回收视图以构造每一行,并且您的异步任务正在维护对该回收视图的引用。当您的任务完成后,他们将更新ImageView,此时可能实际上对应于其他一行。
在每次调用getView时膨胀新视图的问题是滚动时ListView的性能会不好。您(正确地)使用convertView来减少这种开销,您只是错过了将ImageView正确绑定到异步的部分。我在这里找到了一个解决方案:http://android-developers.blogspot.com/2010/07/multithreading-for-performance.html
总之,它基本上扩展了Drawable以封装任务,并且只有在任务与图像紧密相关时才更新图像。部分&#34;处理并发&#34;解决你所面临的问题。特别是,请阅读该部分之前的段落,以了解导致问题的原因。
答案 1 :(得分:5)
这也发生在我身上。我想到的是,在新图像被绑定到imageview之前,旧图像显示了一段时间。在您的情况下,正在发生的事情是您的正确信息显示onPostExecute(),这需要一段时间,并且在此之前显示转换视图中设置的任何数据。你有两种方法可以解决这个问题,
在你的getView()中,将if {} ... else {}块更改为
layoutItem = (LinearLayout) mInflater.inflate(R.layout.auto_gi, parent, false);
或
在getView()中使用一些占位符图像设置imageview,直到调用onPostExecute()。
希望这有帮助。
答案 2 :(得分:1)
当你扩展BaseAdapter并实现getView(int position,View convertView,ViewGroup parent)时,convertView实际上是一个循环视图。
被破坏的视图(最后一个视图被推离屏幕)。你可以做的是清除convertView,因此它不会显示陈旧的数据。或许找到带有图像的视图并执行layoutItem.removeAll()。希望这将清除视图并为您提供干净的布局。