使用Glide的Android列表视图 - 加载后位图翻倍

4
我正在开发一个安卓应用程序。我的其中一个片段包含一个简单的列表视图,显示朋友列表。每个朋友都可以有自己的个人资料图片 - 它是由Glide库设置的。当用户没有设置个人资料图片时,会显示默认图片。我的问题是,每次列表中的第一个元素都会得到相同的图片,这个图片是在列表中最后一个不是默认图片的元素上设置的。我的意思如图所示:

doubled profile pic

用户wiktor已设置个人资料图片,如您所见,第一个位置bonzo有wiktor的个人资料图片(bonzo应该有默认图片)

同时,列表中删除用户存在问题:

list after delete

如您所见,我从好友列表中删除了majka,接下来的元素获取了她的图片。

默认的个人资料图片在行布局 XML 中设置为 drawable。

这是我的 ListView 适配器代码:

public class FriendsAdapter extends ArrayAdapter<FriendData> {

static class FriendHolder {

    TextView friendName;
    TextView friendRank;
    ImageView friendIcon;
    ImageButton deleteFriendBtn;
    ImageButton banFriendBtn;
}

private List<FriendData> list;

public FriendsAdapter(Context context, int resource, List<FriendData> objects) {

    super(context, resource, objects);
    list = objects;
}

@Override
public View getView(final int position, View convertView, final ViewGroup parent) {

    final FriendData element = getItem(position);
    final FriendHolder viewHolder;
    if (convertView == null) {

        viewHolder = new FriendHolder();
        LayoutInflater inflater = LayoutInflater.from(getContext());
        convertView = inflater.inflate(R.layout.friend_layout, parent, false);
        viewHolder.friendIcon = (ImageView) convertView.findViewById(R.id.friendIcon);
        viewHolder.friendName = (TextView) convertView.findViewById(R.id.friendName);
        viewHolder.friendRank = (TextView) convertView.findViewById(R.id.friendRank);
        viewHolder.deleteFriendBtn = (ImageButton) convertView.findViewById(R.id.deleteFriendBtn);
        viewHolder.banFriendBtn = (ImageButton) convertView.findViewById(R.id.banFriendBtn);
        convertView.setTag(viewHolder);
    } else {
        viewHolder = (FriendHolder) convertView.getTag();
    }

    if (element.getPhoto() != null) {

        String photo = S3ImageHandler.SMALL_PROFILE_ICON_PREFIX + element.getPhoto();
        String url = String.format(S3ImageHandler.AMAZON_PROFILE_DOWNLOAD_LINK, photo);
        Glide.with(getContext())
                .load(url)
                .asBitmap()
                .diskCacheStrategy(DiskCacheStrategy.ALL)
                .placeholder(R.drawable.user_small)
                .into(new BitmapImageViewTarget(viewHolder.friendIcon) {
                    @Override
                    protected void setResource(Bitmap resource) {

                        RoundedBitmapDrawable circularBitmapDrawable = RoundedBitmapDrawableFactory.create(getContext().getResources(), resource);
                        circularBitmapDrawable.setCircular(true);
                        viewHolder.friendIcon.setImageDrawable(circularBitmapDrawable);
                    }
                });
    }

    viewHolder.friendName.setText(element.getId());
    viewHolder.friendRank.setText(String.format("%s %d", getContext().getString(R.string.text_rank), element.getRank()));
    viewHolder.deleteFriendBtn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
            confirmDelete(element, position, parent);
        }
    });
    viewHolder.banFriendBtn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            confirmBan(element, position, parent);
        }
    });

    return convertView;
}

从好友列表中删除用户:

        remove(element);
        notifyDataSetChanged();

你们中有人看到我哪里做错了吗?如果能得到帮助,我会非常感激。谢谢 :)


1
跨链接:https://github.com/bumptech/glide/issues/949 - TWiStErRob
1个回答

11

您正在重复使用列表项(根据可回收视图的定义),这意味着如果将图像设置为项目并且不清除它,则该图像将保留在那里。 因此,即使 setText 更改标签,viewHolder.friendIcon 也不受影响。 修复很简单:

if (element.getPhoto() != null) {
    Glide.with(getContext())...
} else {
    Glide.clear(viewHolder.friendIcon); // tell Glide that it should forget this view
    viewHolder.friendIcon.setImageResource(R.drawable.user_small); // manually set "unknown" icon
}

同时从xml中移除可绘制对象,或至少更改为tools:src,这将有助于减少膨胀时间;无论如何,该值每次都会被Glide覆盖。


为了降低复杂性,还有一种替代方案:

class FriendData {
    // if you can't modify this class you can also put it in a `static String getPhotoUrl(Element)` somewhere
    public void String getPhotoUrl() {
        if (this.getPhoto() == null) return null;
        String photo = S3ImageHandler.SMALL_PROFILE_ICON_PREFIX + this.getPhoto();
        String url = String.format(S3ImageHandler.AMAZON_PROFILE_DOWNLOAD_LINK, photo);
        return url;
    }
}

然后将整个 if (element.getPhoto() != null) {...} 替换为:

Glide.with(getContext())
     .load(element.getPhotoUrl()) // this may be null --\
     .asBitmap() //                                     |
     .diskCacheStrategy(DiskCacheStrategy.ALL) //       |
     .placeholder(R.drawable.user_small) //             |
     .fallback(R.drawable.user_small) // <--------------/
     .into(new BitmapImageViewTarget(viewHolder.friendIcon) { ... })
;

这样做也会有正确的结果,因为即使没有图像 URL,Glide 也会处理并设置一些内容,请查看 JavaDoc 或 fallback 的源代码。
此外,还应考虑使用 CircleCrop。除了缓存的好处外,它还支持 GIF,因为您可以删除 .asBitmap() 和自定义目标。

感谢 tools:src.placeholder.fallback。在我的情况下,一个带有LinearLayout作为列表项的ListView被显示出来了。在您的解决方案之前,我的ImageViews在滚动期间显示和消失。但是现在,在加载图像时,会出现短暂的抖动。因此,我将恢复使用Picasso。 - CoolMind
@CoolMind 不需要为 Glide 检查相同的 URL;如果图像当前可见,则会命中内存缓存并且不会重新加载,因此不会出现闪烁。我认为,如果您遇到闪烁问题,可能是其他方面出了问题,与这个问答无关。 - TWiStErRob
1
@CoolMind,我在谈论内存缓存,如果你没有明确禁用它,那么它是启用的。根据这些有限的信息,似乎闪烁很可能是由于监听器不必要地启动动画(检查布尔参数)或TransitionDrawable引起的,因为Glide默认使用.crossFade()。如果你想和我一起找出它发生的原因,可以在Github上开一个问题。 - TWiStErRob
@TWiStErRob,是的,内存缓存没有被禁用。我在屏幕顶部使用常规线性动画(Animation a = new Animation() {...})。当用户滚动ListView时,这个头部(始终可见)变得更薄,ListView变得更高。此外,用户可以点击标题使其再次变大。在这些时刻,图像可能会闪烁,但几秒钟后一切都正常了。经过对相同URL的检查后,我在我的设备上没有看到这个问题。 - CoolMind
@TWiStErRob,我知道你是一个很棒的专家,如果遇到问题,我会开个 issue 的。谢谢。 - CoolMind
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接