在我的应用中,有一个ListView
使用自定义CursorAdapter
将数据加载到其中,基本上是ImageView
和TextView
。我正在AsyncTask
加载图像,现在的问题是,最初所有图像都分配给正确的文本,但是当我快速上下滚动时,它会为每个文本绑定随机图像。由于我使用的是CursorAdapter,我不能在这里使用ViewHolder
,那么我该如何解决这个问题?
以下是我的示例代码:
public class MyAdapter extends CursorAdapter {
private LayoutInflater mLayoutInflater;
@SuppressWarnings("unused")
private Context mContext;
public MyAdapter(Context context, Cursor c, int flags) {
super(context, c, flags);
mContext = context;
mLayoutInflater = LayoutInflater.from(context);
}
public void bindView(View view, Context context, Cursor cursor) {
String title = cursor.getString(cursor.getColumnIndex(MediaStore.MediaColumns.TITLE));
String album_id = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM_ID));
TextView text = (TextView)view.findViewById(R.id.txtTitle);
text.setText(title);
Uri sArtworkUri = Uri.parse("content://media/external/audio/albumart");
Uri uri = ContentUris.withAppendedId(sArtworkUri, Integer.valueOf(album_id));
new MyImageLoader(context,view).execute(uri);
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return mLayoutInflater.inflate(R.layout.row, parent, false);
}
private class MyImageLoader extends AsyncTask<Uri, Void, Bitmap>{
Context context;
View view;
MyImageLoader(Context context,View view){
this.context = context;
this.view = view;
}
@Override
protected Bitmap doInBackground(Uri... uri) {
ContentResolver res = context.getContentResolver();
InputStream in = null;
try {
in = res.openInputStream(uri[0]);
}
catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Bitmap artwork = BitmapFactory.decodeStream(in);
return artwork;
}
protected void onPostExecute(Bitmap bmp){
ImageView iv = (ImageView)view.findViewById(R.id.imgIcon);
if(bmp!=null)
//iv.setImageBitmap(bmp);
iv.setImageBitmap(Bitmap.createScaledBitmap(bmp, 100, 100, false));
}
}
}
更新:我应用了setTag方法,现在洗牌次数较少,但是当我快速滚动时,旧图像会持续一秒钟,直到加载正确的图像。这是新代码:
public void bindView(View view, Context context, Cursor cursor) {
String title = cursor.getString(cursor.getColumnIndex(MediaStore.MediaColumns.TITLE));
String album_id = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM_ID));
ImageView iv = (ImageView)view.findViewById(R.id.imgIcon);
TextView text = (TextView)view.findViewById(R.id.txtTitle);
text.setText(title);
Uri sArtworkUri = Uri.parse("content://media/external/audio/albumart");
Uri uri = ContentUris.withAppendedId(sArtworkUri, Integer.valueOf(album_id));
// *****TAG SET*****
iv.setTag(uri);
//***PASSING BOTH URI AND IMAGEVIEW TO CONSTRUCTOR***
new MyImageLoader(context,view,iv,uri).execute(uri);
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View v = mLayoutInflater.inflate(R.layout.row, parent, false);
//v.setTag(R.id.imgIcon, v.findViewById(R.id.imgIcon));
return v;
}
private class MyImageLoader extends AsyncTask<Object, Void, Bitmap>{
Context context;
View v;
ImageView iv;
Uri u;
MyImageLoader(Context context,View v,ImageView iv,Uri u){
this.context = context;
this.v = v;
this.iv = iv;
this.u = u;
}
@Override
protected synchronized Bitmap doInBackground(Object... param) {
ContentResolver res = context.getContentResolver();
InputStream in = null;
try {
Uri uri= (Uri)param[0];
in = res.openInputStream(uri);
}
catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Bitmap artwork = BitmapFactory.decodeStream(in);
return artwork;
}
protected void onPostExecute(Bitmap bmp){
if(bmp!=null)
{ ImageView iv = (ImageView)v.findViewById(R.id.imgIcon);
if(iv.getTag().toString().equals(u.toString()))
iv.setImageBitmap(Bitmap.createScaledBitmap(bmp, 100, 100, false));
}
}
}
更新:在调用后台任务之前设置占位符图片可以让它更好。
答案 0 :(得分:2)
我使用的一个解决方案是通过Uri
将ImageView
附加到setTag()
。然后,当您转到更新ImageView
时,请检查您要应用的图片的Uri
是否与getTag()
的值相匹配。如果是,请继续并更新ImageView
。如果没有,则ImageView
被回收,您可以跳过更新。
另一种方法是ImageView
上的use setHasTransientState(true)
(或者可能在行上 - 没有尝试过这个),当你决定你有一个缓存未命中并且需要启动{{1 }}。这将导致AsyncTask
避免回收该行,直到进行匹配的AdapterView
调用。在将动画应用到setHasTransientState(false)
行并试图避免回收问题的情况下,Chet Haase有a DevBytes video on this。但是,ListView
是API级别16的新功能,因此,如果您尝试支持Android 4.0或更早版本的设备,则这将不是一个可用的选项。