第一行代码--初步学习--UI开发--ListView
ListView绝对可以称得上是Android中最常用的控件之一,几乎所有的应用程序都会用到它。由于手机屏幕空间都比较有限,能够一次性在屏幕上显示的内容并不多,当我们的程序中有大量的数据需要展示的时候,就可以借助ListView来实现。ListView允许用户通过手指上下滑动的方式将屏幕外的数据滚动到屏幕内,同时屏幕上原有的数据则会滚动出屏幕。相信你其实每天都在使用这个控件,比如查看手机联系人列表,翻阅微博的最新消息等等。
核心组成(四要素)
ListView:列表容器,负责滚动与布局;
数据集合:
List/ 数组等,存放要展示的数据;Item 布局:单个列表项的 XML(如纯文本、图文);
Adapter(适配器):“数据→视图” 的桥梁,系统常用:
ArrayAdapter:快速展示纯文本列表;
SimpleAdapter:展示图文混合列表;
BaseAdapter:自定义复杂布局(最常用)。
ListView的简单用法
XML布局
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/main" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <ListView android:id="@+id/list_view" android:layout_width="match_parent" android:layout_height="match_parent"> </ListView> </androidx.constraintlayout.widget.ConstraintLayout>Ktolin代码实现简单的滑动
不过,数组中的数据是无法直接传递给ListView的,我们还需要借助适配器来完成。Android中提供了很多适配器的实现类,其中我认为最好用的就是ArrayAdapter。它可以通过泛型来指定要适配的数据类型,然后在构造函数中把要适配的数据传入即可。ArrayAdapter有多个构造函数的重载,你应该根据实际情况选择最合适的一种。这里由于我们提供的数据都是字符串,因此将ArrayAdapter的泛型指定为String,然后在ArrayAdapter的构造函数中依次传入当前上下文、ListView子项布局的id,以及要适配的数据。
package com.example.learning import android.os.Bundle import android.widget.ArrayAdapter import android.widget.ListView import androidx.appcompat.app.AppCompatActivity import com.example.learning.R class MainActivity : AppCompatActivity() { // 数据源 private val data = arrayOf( "Apple", "Banana", "Orange", "Watermelon", "Pear", "Grape", "Pineapple", "Strawberry", "Cherry", "Mango","1","1","3","4","5","1","1","3","4","5" ) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // 绑定布局(这里会自动识别 activity_main) setContentView(R.layout.activity_main) // 去掉你那部分报错的 window inset 代码(如果不需要边缘到边缘效果) // enableEdgeToEdge() // ViewCompat.setOnApplyWindowInsetsListener(...) 这段暂时注释掉,避免干扰 // 初始化 ListView 和 Adapter val listView: ListView = findViewById(R.id.list_view) val adapter = ArrayAdapter( this, android.R.layout.simple_list_item_1, // 系统自带的单行文本布局 data ) listView.adapter = adapter // 可选:添加点击事件 // listView.onItemClickListener = { _, _, position, _ -> // val selected = data[position] // // 可以在这里弹出 Toast 或做其他操作 // // Toast.makeText(this, "你点击了: $selected", Toast.LENGTH_SHORT).show() // } } }既然ListView是用于展示大量数据的,那我们就应该先将数据提供好。这些数据可以是从网上下载的,也可以是从数据库中读取的,应该视具体的应用程序场景来决定。
定制ListView的界面
首先需要准备好一组图片,分别对应上面提供的每一种水果,待会我们要让这些水果名称的旁边都有一个图样。
1.定义实体类作为ListView适配器的适配类型 新建类fruit
package com.example.learning // data class 自动生成 equals/hashCode/toString,适合做列表数据 data class Fruit( val name: String, // 水果名称 val imageRes: Int // 图片资源ID(drawable/mipmap 里的图片) )2.接下来需要创建一个自定义的适配器,这个适配器继承自ArrayAdapter,并将泛型指定为Fruit类,新建类FruitAdapter
package com.example.learning import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.BaseAdapter import android.widget.ImageView import android.widget.TextView class FruitAdapter(private val fruitList: List<Fruit>) : BaseAdapter() { // 返回数据总数 override fun getCount(): Int = fruitList.size // 返回当前位置的数据对象 override fun getItem(position: Int): Fruit = fruitList[position] // 返回当前位置的ID(直接用 position 即可) override fun getItemId(position: Int): Long = position.toLong() // 核心:创建/复用 View 并绑定数据 override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View { val view: View val holder: ViewHolder // 1. 复用逻辑:convertView 为空时才 inflate 新布局 if (convertView == null) { view = LayoutInflater.from(parent?.context) .inflate(R.layout.list_item, parent, false) holder = ViewHolder(view) view.tag = holder // 把 holder 存到 view.tag 里,下次复用 } else { view = convertView holder = view.tag as ViewHolder // 从 tag 取出 holder,避免 findViewById } // 2. 绑定数据到控件 val fruit = fruitList[position] holder.tvName.text = fruit.name holder.ivIcon.setImageResource(fruit.imageRes) return view }3.自定义 Item 布局list_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="horizontal" android:gravity="center_vertical" android:padding="12dp"> <!-- 图片 --> <ImageView android:id="@+id/ivIcon" android:layout_width="50dp" android:layout_height="50dp" android:scaleType="centerCrop"/> <!-- 文字 --> <TextView android:id="@+id/tvName" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="18sp" android:layout_marginStart="16dp"/> </LinearLayout>4.在MainActivity.kt中使用
package com.example.learning import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.widget.ListView class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val listView = findViewById<ListView>(R.id.listView) // 1. 准备测试数据(图片用你自己 drawable 里的资源,这里先用默认图标示例) val fruitList = listOf( Fruit("苹果", R.mipmap.ic_launcher), Fruit("香蕉", R.mipmap.ic_launcher), Fruit("橙子", R.mipmap.ic_launcher), Fruit("西瓜", R.mipmap.ic_launcher), Fruit("草莓", R.mipmap.ic_launcher), Fruit("芒果", R.mipmap.ic_launcher) ) // 2. 创建适配器并设置给 ListView val adapter = FruitAdapter(fruitList) listView.adapter = adapter } }BaseAdapter 核心方法
| 方法 | 作用 |
|---|---|
getCount() | 返回列表数据总数 |
getItem() | 返回指定位置的数据对象 |
getItemId() | 返回指定位置的唯一 ID |
getView() | 核心方法,创建 / 复用 Item 并绑定数据 |
ListView的点击事件
条目整体点击事件(最常用)
在MainActivity里给 ListView 设置:
// listView 是你布局里的 ListView id listView.onItemClickListener = object : AdapterView.OnItemClickListener { override fun onItemClick(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { val item = fruitList[position] Toast.makeText(this@MainActivity, "选中:${item.name}", Toast.LENGTH_SHORT).show() }参数解释(记住 4 个参数)
onItemClickListener = { parent, view, position, id -> }parent:ListView 本身view:被点击的那一行 Item 布局position重点:点击条目索引(从 0 开始)id:条目 id
条目内部控件点击(比如点图片、点文字)
在FruitAdapter 的 getView里设置:
override fun getView(...) { // ...复用代码省略... val fruit = fruitList[position] holder.tvName.text = fruit.name holder.ivIcon.setImageResource(fruit.imageRes) // 单独给图片加点击 holder.ivIcon.setOnClickListener { Toast.makeText(parent?.context, "点击了图片:${fruit.name}", Toast.LENGTH_SHORT).show() } // 单独给文字加点击 holder.tvName.setOnClickListener { Toast.makeText(parent?.context, "点击了文字:${fruit.name}", Toast.LENGTH_SHORT).show() } return view }