9、网格布局实战:用 GridLayoutManager 实现 2 列网格
聊完了线性布局,咱们来看看网格布局。说实话,网格在移动端太常见了——商品列表、相册、应用图标,清一色的网格。我个人觉得,掌握 GridLayoutManager 是 RecyclerView 进阶的必经之路。
9.1 GridLayoutManager 是什么?
GridLayoutManager 是 RecyclerView 自带的布局管理器。它把列表分成若干列,每个 item 占一列。说白了,就是把你手机屏幕横着切几刀,每个格子放一个 item。
它的构造方法很简单:
GridLayoutManager(context, spanCount: Int)
GridLayoutManager(context, spanCount: Int, orientation: Int, reverseLayout: Boolean)
- spanCount:列数。2 就是两列,3 就是三列。
- orientation:方向。VERTICAL 是纵向滚动,HORIZONTAL 是横向滚动。
- reverseLayout:是否反向布局。一般用 false。
9.2 两列网格的完整实现
咱们直接上代码。假设我们要展示一个商品列表,每个商品有图片和名称。
9.2.1 布局文件
先写 item 布局。注意,网格的 item 宽度会被 GridLayoutManager 自动平分,所以不用写死宽高。
<!-- item_product.xml -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="8dp">
<ImageView
android:id="@+id/ivProduct"
android:layout_width="match_parent"
android:layout_height="120dp"
android:scaleType="centerCrop" />
<TextView
android:id="@+id/tvName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:textSize="14sp"
android:maxLines="2"
android:ellipsize="end" />
</LinearLayout>
9.2.2 适配器
适配器和 LinearLayoutManager 时一模一样,不需要任何改动。这就是 RecyclerView 的优雅之处——布局管理器只管布局,适配器只管数据绑定。
class ProductAdapter(
private val productList: List<Product>
) : RecyclerView.Adapter<ProductAdapter.ProductViewHolder>() {
class ProductViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val ivProduct: ImageView = itemView.findViewById(R.id.ivProduct)
val tvName: TextView = itemView.findViewById(R.id.tvName)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProductViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_product, parent, false)
return ProductViewHolder(view)
}
override fun onBindViewHolder(holder: ProductViewHolder, position: Int) {
val product = productList[position]
holder.tvName.text = product.name
// 图片加载用 Glide 或 Coil,这里省略
}
override fun getItemCount() = productList.size
}
9.2.3 在 Activity 或 Fragment 中设置
关键就在这一步。设置 GridLayoutManager,指定列数为 2。
class ProductActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_product)
val recyclerView = findViewById<RecyclerView>(R.id.recyclerView)
val adapter = ProductAdapter(generateMockData())
// 设置两列网格
val layoutManager = GridLayoutManager(this, 2)
recyclerView.layoutManager = layoutManager
recyclerView.adapter = adapter
}
private fun generateMockData(): List<Product> {
return listOf(
Product("商品A", R.drawable.img_a),
Product("商品B", R.drawable.img_b),
// ... 更多数据
)
}
}
9.3 网格布局的核心逻辑
我画了一张图,帮你理解 GridLayoutManager 是怎么工作的。
你看,每个 item 的宽度是屏幕宽度除以 2。如果屏幕宽度是 360dp,那每个 item 就是 180dp 宽。高度由 item 布局自己决定。
9.4 避坑指南
我在项目中踩过不少网格的坑,分享几个给你。
有些新手会在 item 布局里写 android:layout_width="180dp"。千万别!GridLayoutManager 会自动分配宽度,你写死了反而会出问题。用 match_parent 就好。
网格里通常有很多图片。我曾经在一个商品列表里加载了 200 张高清图,结果 OOM 了。建议用 Glide 或 Coil,并设置合适的尺寸:
Glide.with(context).load(url).override(360, 360).into(imageView)
如果你在每个 item 上加 margin,你会发现两边的 item 和屏幕边缘间距不一致。我建议用 ItemDecoration 来统一处理间距,后面会专门讲。
9.5 横向网格?也可以
GridLayoutManager 支持横向滚动。你只需要把 orientation 设为 HORIZONTAL:
val layoutManager = GridLayoutManager(this, 2, HORIZONTAL, false)
这时候,spanCount 变成了行数。每行 2 个 item,横向滚动。我一般在做横向分类标签时用这个,比如「热门推荐」那一行。
9.6 总结一下
两列网格其实就这么简单。你只需要记住:
- 用
GridLayoutManager(context, 2)设置两列 - 适配器不用改,和 LinearLayoutManager 一样
- item 宽度用 match_parent,高度自适应
- 图片加载注意内存,间距用 ItemDecoration
嗯,网格布局的基础就这些。下一节咱们会聊更高级的——怎么让某些 item 跨列,比如第一个 item 占一整行,后面两个占一行。那个稍微有点绕,但掌握了今天的内容,你已经有基础了。
好了,去试试吧。把 LinearLayoutManager 换成 GridLayoutManager,看看你的列表瞬间变成网格是什么效果。