You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ListView滚动时收藏夹列表图片消失问题求助

ListView滚动时收藏夹列表图片消失问题求助

你好,我来帮你分析下这个问题的核心原因和对应的解决办法:

问题根源

你遇到的图片消失问题,核心原因是InputStream是一次性读取的流对象:

  • 你在加载收藏夹时,把联系人头像的InputStream存在了customListImg数组里,第一次调用BitmapFactory.decodeStream(imageId[position])时,流的读取指针会移动到末尾;
  • 当ListView滚动复用Item视图时,再次调用这个方法,流已经没有数据可以读取了,自然解码不出Bitmap,所以图片就消失了;
  • 而通话记录的图片是用setImageResource加载的本地资源,每次调用都会重新从资源文件读取,所以没有这个问题。

另外你的Adapter的getView方法没有实现正确的ViewHolder复用机制,每次都inflate新的View,不仅效率低,也会让这个流的问题更明显。

解决方案

这里给你两个可行的解决思路,推荐优先用第一种或者第二种优化后的方案:

方案1:提前将InputStream转成Bitmap缓存

直接在加载收藏夹数据时,把InputStream解码成Bitmap存储,避免复用View时流已经被读完的问题:

  1. 先修改收藏夹的图片列表类型:
// 把InputStream的列表改成Bitmap列表
ArrayList<Bitmap> customListImg = new ArrayList<>();
  1. 加载收藏夹时,直接解码并缓存Bitmap,同时关闭流避免资源泄漏:
InputStream thumbnailInputStream = openPhoto(ContactId);
if (thumbnailInputStream != null) {
    Bitmap bitmap = BitmapFactory.decodeStream(thumbnailInputStream);
    customListImg.add(bitmap);
    // 记得关闭流
    try {
        thumbnailInputStream.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
} else {
    // 如果获取不到头像,添加默认头像
    customListImg.add(BitmapFactory.decodeResource(getResources(), R.drawable.default_avatar));
}
  1. 修改Adapter的对应代码:
// 把InputStream[]改成Bitmap[]
private final Bitmap[] imageId;

public CustomList2 (Activity context, String[] web, Bitmap[] imageId) {
    super(context, R.layout.callog_layout, web);
    this.context = context;
    this.web = web;
    this.imageId = imageId;
}

@Override
public View getView(int position, View view, ViewGroup parent) {
    // 实现ViewHolder复用,提升ListView性能
    ViewHolder holder;
    if (view == null) {
        LayoutInflater inflater = context.getLayoutInflater();
        view = inflater.inflate(R.layout.callog_layout, parent, false);
        holder = new ViewHolder();
        holder.txtTitle = view.findViewById(R.id.txt);
        holder.imageView = view.findViewById(R.id.img);
        view.setTag(holder);
    } else {
        holder = (ViewHolder) view.getTag();
    }

    holder.txtTitle.setText(web[position]);
    holder.imageView.setImageBitmap(imageId[position]);
    return view;
}

// 定义ViewHolder类缓存控件
static class ViewHolder {
    TextView txtTitle;
    ImageView imageView;
}

方案2:缓存ContactId,每次getView时重新加载头像

如果担心Bitmap占用内存过多,可以只缓存联系人的ID和名称,每次复用View时根据ID重新获取头像流并解码,同时实现ViewHolder复用:

  1. 先定义一个数据类来存储收藏夹联系人的信息:
public class FavoriteContact {
    public String nameWithPhone;
    public long contactId;

    public FavoriteContact(String nameWithPhone, long contactId) {
        this.nameWithPhone = nameWithPhone;
        this.contactId = contactId;
    }
}
  1. 加载收藏夹时,只存储必要的信息:
ArrayList<FavoriteContact> favoriteContacts = new ArrayList<>();
// 原有查询Cursor的代码...
String name = cursor.getString(1) + phones;
long contactId = Long.parseLong(id);
favoriteContacts.add(new FavoriteContact(name, contactId));
// 不再需要存储InputStream或Bitmap
  1. 修改Adapter为基于FavoriteContact的适配,同时实现ViewHolder:
public class CustomList2 extends ArrayAdapter<FavoriteContact> {
    private final Activity context;

    public CustomList2(Activity context, ArrayList<FavoriteContact> contacts) {
        super(context, R.layout.callog_layout, contacts);
        this.context = context;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;
        if (convertView == null) {
            LayoutInflater inflater = context.getLayoutInflater();
            convertView = inflater.inflate(R.layout.callog_layout, parent, false);
            holder = new ViewHolder();
            holder.txtTitle = convertView.findViewById(R.id.txt);
            holder.imageView = convertView.findViewById(R.id.img);
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        FavoriteContact contact = getItem(position);
        holder.txtTitle.setText(contact.nameWithPhone);
        
        // 每次复用View时,根据contactId重新获取头像
        InputStream inputStream = ((MainActivity)context).openPhoto(contact.contactId);
        if (inputStream != null) {
            Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
            holder.imageView.setImageBitmap(bitmap);
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else {
            holder.imageView.setImageResource(R.drawable.default_avatar);
        }
        return convertView;
    }

    static class ViewHolder {
        TextView txtTitle;
        ImageView imageView;
    }
}
  1. 最后设置Adapter的代码改成:
CustomList2 adapter = new CustomList2(MainActivity.this, favoriteContacts);
lvFavourites.setAdapter(adapter);

额外注意事项

  • 不管用哪种方案,都要记得关闭InputStream,避免出现资源泄漏的问题;
  • 如果联系人头像比较大,建议在子线程中解码Bitmap,避免阻塞UI线程(可以用Coroutine、AsyncTask或者HandlerThread实现);
  • 可以用LruCache来缓存已经加载过的Bitmap,减少重复解码的开销,提升滚动流畅度。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.08 10:57:57