如何修复RecyclerView的StaggeredGridLayoutManager滚动时内容分散错位问题
我最近碰到个需求:要从API拉取远程图片,然后做成Pinterest那种瀑布流网格展示。一开始用普通GridView倒是能正常显示图片,但瀑布流的错落感完全做不出来。查了好多资料试了各种方法,终于搞定了,把方案整理出来给大家参考:
实现Pinterest风格瀑布流展示API图片的方案
核心技术选型
- 图片加载与缓存:选用Fresco,它对远程图片的加载、缓存逻辑处理得很成熟,省了不少自己造轮子的功夫
- 瀑布流布局容器:放弃无法实现错落布局的GridView,改用搭配
StaggeredGridLayoutManager的RecyclerView,这是实现瀑布流的核心
具体实现步骤
1. 自定义条目布局
每个图片条目用Fresco提供的SimpleDraweeView来承载,写一个极简的item布局文件item_masonry.xml:
<?xml version="1.0" encoding="utf-8"?> <com.facebook.drawee.view.SimpleDraweeView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:fresco="http://schemas.android.com/apk/res-auto" android:id="@+id/simple_drawee_view" android:layout_width="match_parent" android:layout_height="wrap_content" fresco:placeholderImage="@drawable/img_placeholder" fresco:failureImage="@drawable/img_load_fail" fresco:viewAspectRatio="1.0"/>
2. 自定义RecyclerView适配器(MasonryAdapter)
写一个继承自RecyclerView.Adapter的自定义适配器,把API返回的图片URL和SimpleDraweeView绑定:
public class MasonryAdapter extends RecyclerView.Adapter<MasonryAdapter.MasonryViewHolder> { private List<String> imageUrlList; public MasonryAdapter(List<String> imageUrlList) { this.imageUrlList = imageUrlList; } @NonNull @Override public MasonryViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View itemView = LayoutInflater.from(parent.getContext()) .inflate(R.layout.item_masonry, parent, false); return new MasonryViewHolder(itemView); } @Override public void onBindViewHolder(@NonNull MasonryViewHolder holder, int position) { Uri imageUri = Uri.parse(imageUrlList.get(position)); holder.draweeView.setImageURI(imageUri); // 随机设置item高度,模拟瀑布流错落感 ViewGroup.LayoutParams params = holder.draweeView.getLayoutParams(); params.height = getRandomItemHeight(); holder.draweeView.setLayoutParams(params); } @Override public int getItemCount() { return imageUrlList.size(); } static class MasonryViewHolder extends RecyclerView.ViewHolder { SimpleDraweeView draweeView; public MasonryViewHolder(@NonNull View itemView) { super(itemView); draweeView = itemView.findViewById(R.id.simple_drawee_view); } } private int getRandomItemHeight() { // 生成200-500之间的随机高度 return new Random().nextInt(300) + 200; } }
3. 配置RecyclerView与StaggeredGridLayoutManager
在Activity/Fragment中初始化RecyclerView,设置布局管理器和适配器:
RecyclerView masonryRecycler = findViewById(R.id.rv_masonry); // 设置2列垂直瀑布流布局 StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL); // 解决滑动时item位置错乱的问题 layoutManager.setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_NONE); masonryRecycler.setLayoutManager(layoutManager); // 假设已经从API获取到图片URL列表imageList MasonryAdapter adapter = new MasonryAdapter(imageList); masonryRecycler.setAdapter(adapter);
补充提示
- 如果想要根据图片实际宽高比例设置item高度(而非随机),可以通过Fresco的图片加载监听获取宽高信息,再计算对应高度,这样瀑布流会更贴合图片本身比例
- 务必记得在自定义Application中初始化Fresco:
public class MyApp extends Application { @Override public void onCreate() { super.onCreate(); Fresco.initialize(this); } }
内容的提问来源于stack exchange,提问作者Sukanta Paul
相关产品推荐
相关产品推荐

