4.5--最常用和最难用的控件:ListView

ListView 就是滑动列表。

4.5.1 ListView 的简单用法

我们新建一个项目ListViewTest ,然后在布局文件中加入ListView 控件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <ListView
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:entries="@array/items"
        />

</LinearLayout>

这里要说一个属性android:entries ,它可以调用我们在strings.xml 资源文件中定义的 string-array 标签中的数据展示在每个item 都会占用ListView单独的一行。

strings.xml

<resources>
    <string name="app_name">ListViewTest</string>
    <string-array name="items">
        <item>Apple</item>
        <item>Banana</item>
        <item>Orange</item>
        <item>Watermelon</item>
        <item>Pear</item>
        <item>Grape</item>
        <item>Pineapple</item>
        <item>Strawberry</item>
        <item>Cherry</item>
        <item>Mango</item>
        <item>Apple</item>
        <item>Banana</item>
        <item>Orange</item>
        <item>Watermelon</item>
        <item>Pear</item>
        <item>Grape</item>
        <item>Pineapple</item>
        <item>Strawberry</item>
        <item>Cherry</item>
        <item>Mango</item>
    </string-array>
</resources>

这是静态添加,下面我们在代码中动态添加数据不去使用android:entries 属性,因为ListView 展示的数据通常都是在网络请求过来的。

class MainActivity : AppCompatActivity() {

    private val data = listOf("Apple","Banana","Orange","Watermelon","Pear","Grape","Pineapple","Strawberry","Cherry","Mango",
        "Apple","Banana","Orange","Watermelon","Pear","Grape","Pineapple","Strawberry","Cherry","Mango"
        )
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        listView.adapter = ArrayAdapter(this,android.R.layout.simple_list_item_1,data)

    }
}

ListView 的数据是由 Adapter 适配器来提供的,包括它内部用来展示控件的界面,都是由适配器根据数据源的多少遍历而出。我们给ListView 设置的一个适配器 ArrayAdapter,对适配器提供了上下文,布局文件,和数据源。

 

4.5.2 定制ListView 的界面

光是文本内容的ListView 太过单调,我们可以通过自定义Adapter 来实现带有其他组件的子界面ListView而不是只有一个TextView 文本。

首先创建一个 class 用来存储数据,Fruit 类。

package com.example.listviewtest

class Fruit(val name:String,val imageId:Int)

然后我们来创建自定义的子布局文件。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal" 
    android:layout_width="match_parent"
    android:layout_height="60dp">
    <ImageView
        android:id="@+id/fruitImage"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:layout_gravity="center_vertical"
        android:layout_marginLeft="10dp"
        />
    <TextView
        android:id="@+id/fruitName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:layout_marginLeft="10dp"
        />
</LinearLayout>

这样就可以了,一张图片和一个名字。

接下来是重头戏了,创建一个类 来继承 ArrayAdapter 重写它的getView 函数来自定义布局

class FruitAdapter(context: Context, private val resourceId:Int, data:List<Fruit>) : ArrayAdapter<Fruit>(context,resourceId,data) {

    override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
        val viewHolder:ViewHolder
        val view:View
        if (convertView == null){
            view = LayoutInflater.from(context).inflate(resourceId,parent,false)
            val fruitImage:ImageView = view.findViewById(R.id.fruitImage)
            val fruitName:TextView = view.findViewById(R.id.fruitName)
            viewHolder = ViewHolder(fruitImage,fruitName)
            view.tag = viewHolder
        }else {
            view = convertView
            viewHolder = view.tag as ViewHolder
        }
        val fruit = getItem(position)
        fruit?.let {
            viewHolder.apply {
                fruitName.text = fruit.name
                fruitImage.setImageResource(fruit.imageId) }
        }

        return view
    }

    inner class ViewHolder(val fruitImage:ImageView,val fruitName:TextView)
}

getView 的常规优化写法。只有第一次convertView 为空的时候 进行布局文件的加载,之后通过不断的在view.tag 中进行取得 ViewHolder 中的对象进行赋值,而不是每一次内部调用getView的时候都进行加载布局文件。

然后我们来定义一下MainActivity 中的数据源和实例化Adapter :

首先创建一个 List 数据源

private val fruitList = ArrayList<Fruit>()

然后初始化数据源。

private fun initFruit(): List<Fruit> {
        repeat(2){
            fruitList.add(Fruit("Apple",R.drawable.apple_pic))
            fruitList.add(Fruit("Banana",R.drawable.banana_pic))
            fruitList.add(Fruit("Orange",R.drawable.orange_pic))
            fruitList.add(Fruit("Watermelon",R.drawable.watermelon_pic))
            fruitList.add(Fruit("Pear",R.drawable.pear_pic))
            fruitList.add(Fruit("Grape",R.drawable.grape_pic))
            fruitList.add(Fruit("Pineapple",R.drawable.pineapple_pic))
            fruitList.add(Fruit("Strawberry",R.drawable.strawberry_pic))
            fruitList.add(Fruit("Cherry",R.drawable.cherry_pic))
            fruitList.add(Fruit("Mango",R.drawable.mango_pic))
        }
        return fruitList
    }

repeat 标准函数会将Lambda 表达式中的代码执行 n次,n =传入的次数。repeat(n){}

 

4.5.4 ListView 的点击事件

现在的ListView 我们实现的只是视觉效果,如果每个子项item 不能点击的话,这个控件就没有实际的意义了。接下来就来实现item 点击事件的监听。

只需要在listView 加入了Adapter 后调用:

listView.setOnItemClickListener { _, _, position, _ ->
            val fruit = fruitList[position]
            Toast.makeText(this,fruit.name,Toast.LENGTH_SHORT).show()
        }

setOnItemClickListener 就是每个子项的点击监听回调。这里为什么会出现 _ 这种参数呢?Kotlin 允许我们将没有用到的参数使用下划线来替代,因此这种写法也是合法并且更加推荐的!

 

 

其实我们还有一种自定义Adapter的方式,就是创建一个类 继承BaseAdapter 这也是最受欢迎的定义Adapter 方式了,完全的自定义,但是基本逻辑都不会变的,我们来看一下。

class MyAdapter(private val context: Context, private val data:List<Fruit>, private val resourceId:Int) : BaseAdapter() {
    override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
        val view:View
        val viewHolder : ViewHolder
        if (convertView == null){
            view = LayoutInflater.from(context).inflate(resourceId,parent,false)
            val fruitName:TextView=view.findViewById(R.id.fruitName)
            val fruitImage:ImageView=view.findViewById(R.id.fruitImage)
            viewHolder = ViewHolder(fruitImage, fruitName)
            view.tag = viewHolder
        }else{
            view = convertView
            viewHolder = view.tag as ViewHolder
        }
        val fruit = getItem(position)
        fruit?.let {
            viewHolder.apply {
                fruitImage.setImageResource(fruit.imageId)
                fruitName.text=fruit.name
            }
        }
        return view
    }

    override fun getItem(position: Int): Fruit =data[position]

    override fun getItemId(position: Int): Long = position.toLong()

    override fun getCount(): Int = data.size

    inner class ViewHolder(val fruitImage: ImageView, val fruitName: TextView)

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值