为什么 ListView 中偶尔会加载错误的图像?

2024-02-17

我有一个列表视图,它以异步方式加载每个单元格中的图像。当我尝试缓慢向下滚动时(加载当前视图中的所有图像后),它可以完美地工作。 但是,当我尝试在加载它们之前向下滚动并向上滚动时,我遇到了这个问题。细胞开始显示与它们不对应的图像。

我的 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;
        }

    }


}

Edit:

我向 Holder 添加了一个新的实例变量:

public class Holder
{
    TextView tvTitle;
    ImageView ivPrimaryImage;
    TextView tvLocality;
    TextView tvDateCreated;
    int position;
}

在 getView 中设置相同的: 持有者.position = 位置; 并将持有者对象传递给异步任务: new GetPrimaryImages(位置,持有者).execute(图像);

并修改Async调用类如下: 1.http调用添加cancel 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;
        }

    }


}

似乎正在发挥作用。 :)

现在我的疑问是缓存图像的最佳方法是什么?应该将其写入文件吗?每次我向上滚动时都会从中读取它?


问题是,当您的异步任务结束其后台操作时,它链接到的元素已被回收以保存集合中的另一个元素。

让我们关注元素位置,假设您的列表视图最多可以显示 4 个元素。

列表视图第一次调用 getview 获取前 4 个元素,并创建并运行四个异步任务。 然后滚动到显示位置 11 - 15,第一个元素(与位置 1 相关的元素)在异步任务结束之前被回收到位置 11。

然后asynctask结束,你看到的是与post 11相关的图像和与post 1相关的位图。

避免这种情况的一种方法是在 asynctask 中知道视图已被回收,正如 Lucas Rocha 的这篇旧文章中所建议的那样。

列表视图的性能技巧 http://lucasr.org/2012/04/05/performance-tips-for-androids-listview/

查看帖子以了解有关列表视图如何工作的见解:

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么 ListView 中偶尔会加载错误的图像? 的相关文章

随机推荐