来自 url 的图像使用自定义光标适配器闪烁
Images from url flickering with custom cursor adapter
我正在查询 SQLite 数据库并将数据放入列表视图中。数据库行之一包含图像 Url 字段(也可以是 Uri)。
图像已按应有的方式加载,但是当我滚动列表时,所有图像都开始闪烁,有些正在改变位置或显示在不同的地方。
我已经知道发生此行为是因为列表视图在滚动时重复使用行,但我不知道如何解决此行为。我也不能在这个项目中使用像 Picasso 这样的外部库。
这是我的适配器代码:
public class FilmsListCustomAdapter extends CursorAdapter {
private LayoutInflater cursorInflater;
public FilmsListCustomAdapter(Context context, Cursor c, int flags) {
super(context, c, flags);
cursorInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
TextView filmTitle = (TextView) view.findViewById(R.id.filmListTitle);
TextView filmScore = (TextView) view.findViewById(R.id.filmListScore);
ImageView filmImage = (ImageView)view.findViewById(R.id.filmListPoster);
ImageView filmSeen = (ImageView)view.findViewById(R.id.filmListSeen);
String title = cursor.getString( cursor.getColumnIndex("title") );
String score = cursor.getString(cursor.getColumnIndex("score"));
String url = cursor.getString( cursor.getColumnIndex("url") );
int seen = cursor.getInt( cursor.getColumnIndex("seen") );
if(Patterns.WEB_URL.matcher(url).matches()){
LoadImage loadImage = new LoadImage(context,filmImage);
loadImage.execute(url);
}
else{
Bitmap bmp = BitmapFactory.decodeFile(url);
CamImage camImage = new CamImage(context,Uri.parse(url));
Bitmap rotetedIm = camImage.rotateCamImage(bmp,url);
if(rotetedIm!=null){filmImage.setImageBitmap(Bitmap.createScaledBitmap(rotetedIm, 850, rotetedIm.getHeight(), false));}
else{filmImage.setImageResource(R.drawable.no_poster);}
}
GlobalMethods methods = new GlobalMethods(context);
filmTitle.setTypeface(methods.getWalkFont());
filmTitle.setText(title);
filmScore.setText(score);
if(seen==1){filmSeen.setImageResource(R.drawable.eye);}
else{filmSeen.setImageResource(0);}
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup viewGroup) {
return cursorInflater.inflate(R.layout.film_row, viewGroup, false);
}
}
可能发生的情况是:
你得到一张图片的网络 URL 并排队 AsyncTask
以下载它以获得 ImageView
您滚动并且 ImageView
被回收
这次 ImageView
得到一个本地 URL,所以你立即这样做
先前排队的 AsyncTask
完成并在您刚放入的图像上加载了一个现在不相关的图像
清除此问题的关键是确保在回收 ImageView
后取消任务。一种方法是在 ImageView
的标记中放置对 AsyncTask
的引用。当你得到一个回收的 ImageView
时,你检查标签并查看是否有正在进行的任务并在开始新任务之前取消它。
你应该在 Android 开发者博客上查看这篇文章,它会详细解释这个问题以及如何解决它:
Multithreading For Performance | Android Developers Blog
我认为这篇文章是在并行线程中将 AsyncTask
更改为 运行 时写回的,因为他们谈论任务无序完成。他们已经恢复到串行执行,所以我认为这部分不再适用,但概念是相似的,因为您立即加载本地图像就像一个乱序执行的任务。
另外两件事我会考虑:
在getView
中,总是先调用imageView.setImageBitmap(null)
来清除回收ImageView
中的任何剩余图像。我喜欢用代表 "Image Loading" 状态的非常中性的灰色位图初始化 ImageView
s。
使用 AsyncTask
解码本地文件以及检索网络文件。我敢打赌,当你这样做时,你的列表滚动看起来会更顺畅。
我正在查询 SQLite 数据库并将数据放入列表视图中。数据库行之一包含图像 Url 字段(也可以是 Uri)。
图像已按应有的方式加载,但是当我滚动列表时,所有图像都开始闪烁,有些正在改变位置或显示在不同的地方。
我已经知道发生此行为是因为列表视图在滚动时重复使用行,但我不知道如何解决此行为。我也不能在这个项目中使用像 Picasso 这样的外部库。
这是我的适配器代码:
public class FilmsListCustomAdapter extends CursorAdapter {
private LayoutInflater cursorInflater;
public FilmsListCustomAdapter(Context context, Cursor c, int flags) {
super(context, c, flags);
cursorInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
TextView filmTitle = (TextView) view.findViewById(R.id.filmListTitle);
TextView filmScore = (TextView) view.findViewById(R.id.filmListScore);
ImageView filmImage = (ImageView)view.findViewById(R.id.filmListPoster);
ImageView filmSeen = (ImageView)view.findViewById(R.id.filmListSeen);
String title = cursor.getString( cursor.getColumnIndex("title") );
String score = cursor.getString(cursor.getColumnIndex("score"));
String url = cursor.getString( cursor.getColumnIndex("url") );
int seen = cursor.getInt( cursor.getColumnIndex("seen") );
if(Patterns.WEB_URL.matcher(url).matches()){
LoadImage loadImage = new LoadImage(context,filmImage);
loadImage.execute(url);
}
else{
Bitmap bmp = BitmapFactory.decodeFile(url);
CamImage camImage = new CamImage(context,Uri.parse(url));
Bitmap rotetedIm = camImage.rotateCamImage(bmp,url);
if(rotetedIm!=null){filmImage.setImageBitmap(Bitmap.createScaledBitmap(rotetedIm, 850, rotetedIm.getHeight(), false));}
else{filmImage.setImageResource(R.drawable.no_poster);}
}
GlobalMethods methods = new GlobalMethods(context);
filmTitle.setTypeface(methods.getWalkFont());
filmTitle.setText(title);
filmScore.setText(score);
if(seen==1){filmSeen.setImageResource(R.drawable.eye);}
else{filmSeen.setImageResource(0);}
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup viewGroup) {
return cursorInflater.inflate(R.layout.film_row, viewGroup, false);
}
}
可能发生的情况是:
你得到一张图片的网络 URL 并排队
AsyncTask
以下载它以获得ImageView
您滚动并且
ImageView
被回收这次
ImageView
得到一个本地 URL,所以你立即这样做先前排队的
AsyncTask
完成并在您刚放入的图像上加载了一个现在不相关的图像
清除此问题的关键是确保在回收 ImageView
后取消任务。一种方法是在 ImageView
的标记中放置对 AsyncTask
的引用。当你得到一个回收的 ImageView
时,你检查标签并查看是否有正在进行的任务并在开始新任务之前取消它。
你应该在 Android 开发者博客上查看这篇文章,它会详细解释这个问题以及如何解决它:
Multithreading For Performance | Android Developers Blog
我认为这篇文章是在并行线程中将 AsyncTask
更改为 运行 时写回的,因为他们谈论任务无序完成。他们已经恢复到串行执行,所以我认为这部分不再适用,但概念是相似的,因为您立即加载本地图像就像一个乱序执行的任务。
另外两件事我会考虑:
在
getView
中,总是先调用imageView.setImageBitmap(null)
来清除回收ImageView
中的任何剩余图像。我喜欢用代表 "Image Loading" 状态的非常中性的灰色位图初始化ImageView
s。使用
AsyncTask
解码本地文件以及检索网络文件。我敢打赌,当你这样做时,你的列表滚动看起来会更顺畅。