正文开始:嘿,伙计,是时候聊聊GridView了!
你是不是经常刷着淘宝,看着那些排列整齐、琳琅满目的商品,心里默默点赞:“这界面,舒服!”?或者翻着自己的手机相册,享受着照片被完美归类的快感?
这背后的大功臣,就是我们今天的主角——GridView(网格视图)。
你可以把它想象成一个超级有原则的数学课代表,它坚决不允许任何View(视图元素)在它的地盘上乱来,必须行是行,列是列,排排坐,吃果果。它也是Android高级UI组件里的“百搭单品”,一件优秀的“格子衫”,能瞬间提升你App的精致度和实用性。
一、 GridView是谁?它和ListView是亲戚吗?
简单来说,GridView就是一个在二维网格上展示数据的视图组件。
它和ListView(列表视图)确实是“堂兄弟”,都继承自同一个老爸 AbsListView。所以,它们有很多相似之处:
- 都需要适配器(Adapter):作为数据和视图之间的“月老”,Adapter负责告诉GridView:“数据长这样,你应该怎么把它画出来。”
- 都支持滚动:数据多了都能滑滑滑。
- 视图复用机制:超级省内存!只创建一屏能看到的Item(项),滑出去的就回收给新来的用,环保小能手。
那它们最大的区别在哪?方向!
- ListView:是一维的,垂直的“单身公寓楼”,所有View一个接一个地垂直排列。
- GridView:是二维的,是“大型小区”,有行也有列。数据会先从左到右填满一行,然后再换下一行。
所以,当你需要平铺式、并列式地展示内容时(比如表情包选择器、App主菜单、视频封面流),GridView就是你的不二之选。
二、 GridView的核心技能包:怎么把它“驯服”?
想用好GridView,你得摸清它的“脾气”,主要通过XML属性或者Java/Kotlin代码来控制它。
1. 决定你家“格子”长啥样:关键XML属性
numColumns:最重要的属性! 决定一行有几列。
-
auto_fit:自动填充。配合columnWidth使用,计算屏幕宽度能放下几列,非常智能。- 一个具体数字,比如
3:固定3列,就是这么死板但稳定。
columnWidth:设置每一列的宽度。和numColumns="auto_fit"是黄金搭档。horizontalSpacing/verticalSpacing:设置格子之间的水平和垂直间距。别让格子们挤在一起,要给它们呼吸的空间!stretchMode:当列宽不能完全填满屏幕时,拉伸模式。
-
columnWidth:不拉伸,保持列宽固定(默认)。spacingWidth:拉伸列之间的间距。推荐用这个,能保证你的格子不变形。
gravity:每个格子内部内容的对齐方式(比如居中、靠左)。
2. 注入灵魂:Adapter适配器
光有骨架不行,还得有血肉。Adapter就是GridView的灵魂工程师。
// 以Kotlin为例,创建一个简单的ImageAdapter
class ImageAdapter(
private val context: Context,
private val imageResIds: List<Int> // 假设是一组图片资源ID
) : BaseAdapter() {
// 数据集有多少个Item
override fun getCount(): Int = imageResIds.size
// 获取指定位置的数据项
override fun getItem(position: Int): Any = imageResIds[position]
// 获取每个Item的唯一ID,一般用position就行
override fun getItemId(position: Int): Long = position.toLong()
// **最重要的方法!创建和绑定视图**
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
val view: View
val holder: ViewHolder
// 1. 视图复用:有回收的View就用,没有就创建新的(环保!)
if (convertView == null) {
// 第一次,初始化View和ViewHolder
view = LayoutInflater.from(context).inflate(R.layout.grid_item, parent, false)
holder = ViewHolder(view)
view.tag = holder // 把ViewHolder“藏”在View里
} else {
// 第二次及以后,直接使用回收的View和里面的ViewHolder
view = convertView
holder = view.tag as ViewHolder
}
// 2. 绑定数据:把当前位置的数据设置到ViewHolder的视图上
val imageResId = imageResIds[position]
holder.imageView.setImageResource(imageResId)
holder.textView.text = "Item $position"
return view
}
// ViewHolder模式,避免每次都findViewById,提升滑动的流畅度!
private class ViewHolder(view: View) {
val imageView: ImageView = view.findViewById(R.id.iv_image)
val textView: TextView = view.findViewById(R.id.tv_title)
}
}
敲黑板! 上面代码里的 ViewHolder 和 convertView 是性能优化的关键,能让你滑动列表时如丝般顺滑,一定要用上!
三、 让它“活”起来:处理Item的点击事件
一个不能点的GridView,就像一盘不能吃的菜,中看不中用。给它加上点击监听,so easy!
// 在Activity或Fragment中
gridView.onItemClickListener = AdapterView.OnItemClickListener { parent, view, position, id ->
// position就是你点击的那个Item的位置
val clickedImageResId = imageResIds[position]
Toast.makeText(this, "你点击了第${position + 1}个格子", Toast.LENGTH_SHORT).show()
// 这里可以跳转到详情页、放大图片等等,随你发挥!
}
四、 实战!打造一个“时尚单品”展示墙(完整示例)
理论说再多,不如代码跑一跑。我们来手把手撸一个商品展示网格。
1. 布局文件:activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="潮流单品合集"
android:textSize="24sp"
android:textStyle="bold"
android:gravity="center"
android:layout_marginBottom="16dp"/>
<GridView
android:id="@+id/gridView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:numColumns="auto_fit" <!-- 自动适配列数 -->
android:columnWidth="160dp" <!-- 每列宽度 -->
android:verticalSpacing="16dp" <!-- 垂直间距 -->
android:horizontalSpacing="16dp" <!-- 水平间距 -->
android:stretchMode="spacingWidthUniform" <!-- 拉伸间距,保持格子大小 -->
android:gravity="center" />
</LinearLayout>
2. 网格Item的布局:grid_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="@drawable/bg_grid_item" <!-- 加个圆角背景,更美观 -->
android:padding="8dp">
<ImageView
android:id="@+id/iv_image"
android:layout_width="match_parent"
android:layout_height="120dp"
android:scaleType="centerCrop"
android:src="@drawable/placeholder" />
<TextView
android:id="@+id/tv_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="商品标题"
android:textSize="14sp"
android:textStyle="bold"
android:padding="8dp"
android:gravity="center"
android:maxLines="1"
android:ellipsize="end" />
<Button
android:id="@+id/btn_buy"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="立即购买"
android:textSize="12sp"
android:backgroundTint="@color/teal_700" />
</LinearLayout>
3. 主Activity:MainActivity.kt
class MainActivity : AppCompatActivity() {
private lateinit var gridView: GridView
// 模拟数据:图片资源ID和商品标题
private val productList = listOf(
Product(R.drawable.product1, "复古印花T恤"),
Product(R.drawable.product2, "高腰直筒牛仔裤"),
Product(R.drawable.product3, "设计师联名帆布鞋"),
Product(R.drawable.product4, "极简风双肩包"),
Product(R.drawable.product5, "潮流棒球帽"),
Product(R.drawable.product6, "个性手机壳"),
// ... 可以继续加更多
)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
gridView = findViewById(R.id.gridView)
// 设置适配器,注入灵魂!
val adapter = ProductAdapter(this, productList)
gridView.adapter = adapter
// 处理点击事件
gridView.onItemClickListener = AdapterView.OnItemClickListener { parent, view, position, id ->
val selectedProduct = productList[position]
Toast.makeText(this, "你种草了:${selectedProduct.title}", Toast.LENGTH_SHORT).show()
// 实际开发中,这里可以跳转到商品详情页
}
}
}
// 数据类,用来封装商品数据
data class Product(val imageResId: Int, val title: String)
// 适配器
class ProductAdapter(
private val context: Context,
private val productList: List<Product>
) : BaseAdapter() {
override fun getCount(): Int = productList.size
override fun getItem(position: Int): Any = productList[position]
override fun getItemId(position: Int): Long = position.toLong()
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
val view: View
val holder: ViewHolder
if (convertView == null) {
view = LayoutInflater.from(context).inflate(R.layout.grid_item, parent, false)
holder = ViewHolder(view)
view.tag = holder
} else {
view = convertView
holder = view.tag as ViewHolder
}
val product = productList[position]
holder.imageView.setImageResource(product.imageResId)
holder.textView.text = product.title
// 甚至可以处理内部按钮的点击事件
holder.buyButton.setOnClickListener {
Toast.makeText(context, "恭喜你,成功下单【${product.title}】!", Toast.LENGTH_LONG).show()
}
return view
}
private class ViewHolder(view: View) {
val imageView: ImageView = view.findViewById(R.id.iv_image)
val textView: TextView = view.findViewById(R.id.tv_title)
val buyButton: Button = view.findViewById(R.id.btn_buy)
}
}
运行效果: 一个3列(根据屏幕宽度自动计算)的商品展示墙就诞生了!每个商品有图片、标题和购买按钮,点击格子有反馈,点击按钮还能直接购买,体验拉满!
结语:别犹豫,盘它!
好了,看到这里,你已经从GridView的“路人”晋级为可以跟它“称兄道弟”的熟人了。它不是什么高深莫测的黑科技,而是一个实用、高效、能立刻为你App颜值加分的工具。
记住那几个关键属性,用好Adapter和ViewHolder,处理好点击事件,你的应用界面就能告别“菜市场既视感”,瞬间拥有高级“橱窗感”。
还等什么?赶紧打开Android Studio,把上面的代码Copy过去跑起来,亲手打造你的第一个“格子衫”应用吧!遇到问题?别怕,那只是你和代码之间“增进感情”的小游戏。祝你编码愉快!

被折叠的 条评论
为什么被折叠?



