实现搜索功能时,如何防止Glide加载旧结果图片到新ImageView?
嘿,这个问题我太熟了——做搜索列表或者动态刷新的列表时,Glide的图片加载很容易因为视图复用搞出“串图”的乌龙,本质就是旧的加载任务还没跑完,ImageView就被复用给新的搜索结果了,等旧任务完成就把旧图怼上去了。给你几个亲测有效的解决方案:
1. 绑定新数据前先清除旧加载任务(最推荐)
这是最简单直接的办法,在你给ImageView加载新图片之前,先调用Glide的clear()方法,把这个ImageView上所有pending的加载任务都取消掉,从根源上避免旧图覆盖新图。
比如在RecyclerView Adapter的onBindViewHolder里这么写:
@Override public void onBindViewHolder(SearchViewHolder holder, int position) { SearchResult currentItem = mSearchResults.get(position); // 先清空这个ImageView的所有旧加载请求 Glide.with(holder.itemView.getContext()).clear(holder.resultImageView); // 再加载当前搜索结果的图片 Glide.with(holder.itemView.getContext()) .load(currentItem.getImageUrl()) .placeholder(R.drawable.placeholder_image) // 可以加个占位图提升体验 .into(holder.resultImageView); }
clear()会终止所有和这个ImageView关联的加载操作,不管是内存缓存、磁盘缓存的读取还是网络请求,都直接停掉,完美避免旧任务“抢跑”。
2. 给ImageView打Tag,加载完成后校验匹配
如果clear()还偶尔出现问题(比如极端快速的搜索刷新),可以给ImageView设置一个和当前图片URL绑定的Tag,在图片加载完成后检查Tag是否匹配,不匹配就不显示。
代码示例:
@Override public void onBindViewHolder(SearchViewHolder holder, int position) { SearchResult currentItem = mSearchResults.get(position); String targetUrl = currentItem.getImageUrl(); // 给ImageView设置当前URL作为Tag holder.resultImageView.setTag(targetUrl); Glide.with(holder.itemView.getContext()) .load(targetUrl) .listener(new RequestListener<Drawable>() { @Override public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) { return false; // 让Glide处理失败逻辑 } @Override public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) { // 加载完成后,检查ImageView的Tag还是不是当前的URL if (targetUrl.equals(holder.resultImageView.getTag())) { holder.resultImageView.setImageDrawable(resource); } return true; // 返回true,告诉Glide我们自己处理了图片显示 } }) .into(holder.resultImageView); }
这个相当于加了一道保险,确保只有当前数据对应的图片才会显示在ImageView上。
3. 避免全局请求暂停(谨慎使用)
如果你是在搜索页面做全局的请求控制,可以在发起新搜索时暂停Glide的请求,等新数据加载完成后再恢复,但这个方法要注意不要影响其他页面的图片加载:
// 发起新搜索时暂停请求 Glide.with(searchActivity).pauseRequests(); // 新搜索结果加载完成后,恢复请求并刷新列表 newSearchResults.observe(searchActivity, results -> { mSearchResults = results; Glide.with(searchActivity).resumeRequests(); mAdapter.notifyDataSetChanged(); });
这个方法适合搜索频率不高的场景,频繁暂停恢复可能会导致加载队列混乱,所以优先用前两个方法。
另外提个小细节:如果你的搜索结果列表用的是RecyclerView,一定要正确调用notifyDataSetChanged()或者更精细的notifyItem系列方法,避免视图复用出现不必要的混乱。
内容的提问来源于stack exchange,提问作者Artur A

