我有一个listview,可以在异步中的每个单元格中加载图像。当我尝试慢慢向下滚动时(在加载当前视图中的所有图像之后),它可以完美地工作。 但是当我尝试在它们被加载并向上滚动之前向下滚动时,我面临这个问题。单元格开始显示与它们不对应的图像。
我的getView方法如下所示:
public View getView(final int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
View rowView = null;
if(convertView == null) {
rowView = inflater.inflate(R.layout.list_posts_item, null);
final Holder holder=new Holder();
holder.tvTitle=(TextView) rowView.findViewById(R.id.tvTitleNamePost);
holder.ivPrimaryImage=(ImageView) rowView.findViewById(R.id.ivPrimaryImage);
holder.tvLocality=(TextView) rowView.findViewById(R.id.tvLocalityPosts);
holder.tvDateCreated=(TextView) rowView.findViewById(R.id.tvDateCreated);
rowView.setTag(holder);
}else {
rowView=convertView;
}
Holder holder = (Holder)rowView.getTag();
holder.ivPrimaryImage.setId(position);
holder.ivPrimaryImage.setTag(listOfPosts.get(position).getPostId());
holder.ivPrimaryImage.setImageBitmap(null); // Added for flickering issue
holder.tvTitle.setText(listOfPosts.get(position).getTitle());
holder.tvLocality.setText(listOfPosts.get(position).getLocality());
holder.tvDateCreated.setText(listOfPosts.get(position).getCreatedDate());
postId = listOfPosts.get(position).getPostId();
Image image = new Image();
image.setImg(holder.ivPrimaryImage);
if (!"N".equalsIgnoreCase(listOfPosts.get(position).getHasImage()) ) {
if(!tagsCaching.containsKey(postId))
new GetPrimaryImages().execute(image);
else
holder.ivPrimaryImage.setImageBitmap(tagsCaching.get(postId));
}
return rowView;
}
我的异步调用类看起来像这样:
public class GetPrimaryImages extends AsyncTask<Image, Void, Bitmap> {
ImageView imageView = null;
protected Bitmap doInBackground(Image... images) {
this.imageView=images[0].getImg();
// Building Parameters
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("postid",(String)(this.imageView.getTag()) ));
json = jsonParser.makeHttpRequest(CommonResources.getURL("get_primary_image"),
"POST", params);
if(json == null){
return null;
}
Log.d("Fetching Image",imageView.getTag()+ json.toString());
tagsDownloaded.add((String)imageView.getTag());
// check for success tag
String TAG_SUCCESS = "success";
try {
int success = json.getInt(TAG_SUCCESS);
if (success == 0) {
image = json.getString("primaryimage");
}
} catch (JSONException e) {
e.printStackTrace();
}
return getImage(image);
}
/**
* After completing background task Dismiss the progress dialog
* **/
protected void onPostExecute(Bitmap result) {
tagsCaching.put((String)imageView.getTag(), result);
imageView.setImageBitmap(result);
}
public Bitmap getImage(String imageString) {
if("null".equalsIgnoreCase(imageString)){
return null;
}else{
byte[] decodedString = Base64.decode(imageString, Base64.DEFAULT);
Bitmap decodedByte = BitmapFactory.decodeByteArray(decodedString, 0, decodedString.length);
//image.setImageBitmap(decodedByte);
return decodedByte;
}
}
}
编辑:
我向Holder添加了一个新的实例变量:
public class Holder
{
TextView tvTitle;
ImageView ivPrimaryImage;
TextView tvLocality;
TextView tvDateCreated;
int position;
}
在getView中设置相同: holder.position = position; 并将holder对象传递给Async任务: 新的GetPrimaryImages(职位,持有人).execute(image);
并修改了Async调用类,如下所示: 1.添加取消http呼叫 2.更改了onPostExecute方法
public class GetPrimaryImages extends AsyncTask<Image, Void, Bitmap> {
int mPosition;
Holder mHolder;
public GetPrimaryImages(int position, Holder holder){
mPosition = position;
mHolder = holder;
}
ImageView imageView = null;
protected Bitmap doInBackground(Image... images) {
this.imageView=images[0].getImg();
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("postid",(String)(this.imageView.getTag()) ));
JSONObject json;
if(mHolder.position == mPosition)
json = jsonParser.makeHttpRequest(CommonResources.getURL("get_primary_image"),
"POST", params);
else {
json = null;
cancel(true);
}
// check log cat fro response
if(json == null){
return null;
}
Log.d("Fetching Image",imageView.getTag()+ json.toString());
tagsDownloaded.add((String)imageView.getTag());
// check for success tag
String TAG_SUCCESS = "success";
try {
int success = json.getInt(TAG_SUCCESS);
if (success == 0) {
image = json.getString("primaryimage");
}
} catch (JSONException e) {
e.printStackTrace();
}
return getImage(image);
}
protected void onPostExecute(Bitmap result) {
if (mHolder.position == mPosition) {
tagsCaching.put((String) imageView.getTag(), result);
imageView.setImageBitmap(result);
}
}
public Bitmap getImage(String imageString) {
//needs to wait
if("null".equalsIgnoreCase(imageString)){
return null;
}else{
byte[] decodedString = Base64.decode(imageString, Base64.DEFAULT);
Bitmap decodedByte = BitmapFactory.decodeByteArray(decodedString, 0, decodedString.length);
//image.setImageBitmap(decodedByte);
return decodedByte;
}
}
}
似乎工作正常。 :)
现在我怀疑是什么是缓存图像的最佳方法?应该将其写入文件?每次向上滚动时都要从中读取它?
答案 0 :(得分:3)
问题是,当您的异步任务结束其后台操作时,它链接到的元素已被回收以容纳您的集合中的另一个元素。
让我们关注元素位置,让我们说你的列表视图最多可以显示4个元素。
listview第一次调用前4个元素的getview,并创建并运行4个asynctasks。 然后滚动到11-15位置,第一个元素(与位置1相关的元素)在异步任务结束之前被回收到位置11。
然后asynctask结束,你看到的是与post 11相关的图像,其中位图与post 1相关。
一种避免这种情况的方法是在asynctask中了解视图被回收,正如Lucas Rocha的这篇旧帖中所建议的那样。
Performance tips with listview
查看帖子以获取有关listview如何工作的见解:
答案 1 :(得分:0)
主要&#34;问题&#34;是ListViews实现重用视图和串行提供AsyncTasks。
1)在ListView的适配器中,您可以正确地重用项目。在ListView中,只渲染了几个项目(屏幕上可见的项目+上下几个)。如果您开始滚动屏幕上的项目将被销毁,并且他们的视图将asi参数作为convertView传递给public View getView(final int position, View convertView, ViewGroup parent)
。
这是第一个问题。列表是重复使用项目。
2)第二件事是AsyncTask是在另一个线程上执行的,但是在同一个线程上执行了更多的 asyncTask实例。这意味着它们是连续执行的。如果您足够快地滚动,那么您可以使用AsyncTask在加载图像时提出更多请求。有一次,只有少数(我认为5)AsyncTask的工作在队列中等待。另一个成功被忽略了。因此,如果您快速滑动,则会获得5个已满的队列,而另一个图像请求将被忽略。
就是这样。滚动,开始加载少量图像,忽略新显示的图像。当你在所有AsyncTasks结束并且你得到一些&#34;随机&#34;之后停下来(优先显示)列表项中加载的图像。
<强> Sollution 强>
这件事被讨论了曼尼时代并得到了解决。只需使用Picaso库就足够了:
https://futurestud.io/blog/picasso-adapter-use-for-listview-gridview-etc/
答案 2 :(得分:0)
要添加答案,我将实现图像缓存(例如,作为URL到WeakReference到图像的hashmap)。 getView()
将访问该缓存,如果图像不存在,则保留请求。加载图像时,缓存将检查请求列表并通知发布请求的视图(将URL和图像都传递给它们)。视图会将通知中传递的URL与其当前URL进行比较,并使用该图像或忽略它(如果视图超出屏幕或被重用,则必须忽略该图像。)
为什么要求列表。有可能多个视图设法请求一些图像并在加载图像之前重新使用(特别是如果您多次向上和向下滚动列表)。