RecyclerView图片位置异常排查:Picasso加载网格图错位重复问题
问题分析与解决方案
你遇到的图片错位/重复显示问题,核心原因有两个,咱们一个个拆解:
1. 错误的位置计算导致数据匹配混乱
你在获取userPost时写了dataset.get(photoViewHolder.getAdapterPosition() - 1),这完全是多余且错误的操作!
RecyclerView的getAdapterPosition()方法返回的就是当前ViewHolder在Adapter中对应的正确索引位置,根本不需要手动减1。当RecyclerView复用ViewHolder时,这个错误的索引会让你从dataset里拿到完全不匹配的Post对象,自然就会加载错误的图片,甚至出现重复显示的情况。
2. 未取消复用ViewHolder的旧Picasso请求
RecyclerView复用ViewHolder时,之前加载图片的请求可能还在后台运行,当旧请求完成后,会把图片设置到已经被复用的ImageView上,直接覆盖掉新设置的图片,这也是图片错位的常见诱因。
修正后的完整代码
@Override public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, final int position) { PhotoViewHolder photoViewHolder = (PhotoViewHolder) viewHolder; // 直接使用参数里的position,正常场景下和getAdapterPosition()完全一致,更简洁 Post userPost = dataset.get(position); File imageDir = context.getDir(IMAGE_DIR, Context.MODE_PRIVATE); // 先取消当前ImageView的所有Pending请求,避免复用旧请求覆盖新图片 Picasso.with(context).cancelRequest(photoViewHolder.imageView); if (networkUtils.isOnline()) { Picasso.with(context) .load(userPost.getImage()) .placeholder(R.color.image_placeholder_color) .centerCrop() .fit() .into(photoViewHolder.imageView); } else { String postId = String.valueOf(userPost.getPostId()); // 用File的构造方法拼接路径,比字符串拼接更安全,避免分隔符错误 File localImage = new File(imageDir, postId + IMAGE_SUFFIX); if (localImage.exists()) { Picasso.with(context) .load(localImage) .placeholder(R.color.image_placeholder_color) .centerCrop() .fit() .into(photoViewHolder.imageView); } else { // 本地无文件时,主动设置占位符,避免显示错误内容 photoViewHolder.imageView.setImageResource(R.color.image_placeholder_color); } } photoViewHolder.imageView.setOnClickListener(view -> BusProvider.getInstance().post(new OpenUserProfilePhotoEvent( userPost.getPostId(), Integer.valueOf(accountId)))); }
额外优化建议
- 优先使用
onBindViewHolder参数里的position,除非有拖拽/排序等特殊需求,它和getAdapterPosition()的效果完全一致,代码更简洁。 - 离线场景下一定要判断本地文件是否存在,否则Picasso会默认显示错误图(如果没配置的话),影响用户体验。
- 禁用
setIsRecyclable(false)是正确的选择,毕竟RecyclerView的核心优势就是复用ViewHolder,禁用它会大幅降低列表性能。
这样修改后,既保留了RecyclerView的复用机制,又能彻底解决图片错位和重复的问题。
内容的提问来源于stack exchange,提问作者Mes
相关产品推荐
相关产品推荐

