虽然ListView功能很强大,但是也有很多缺点。比如说性能差、扩展性不好等
为此,Android5.0之后提供了一个更强大的滚动控件——RecyclerView,可以说是一个增强版的ListView,优化了ListView的各种不足。
注:本文将以kotlin代码的方式编写,我会尽量写上注释,如有不懂可以提问!
今天先写一个简单的案例,目的在于理清关系;如后期有机会的话,再写一个万能的、多级联动的案例给大家。
案例之前先介绍一下各自的任务,以便理解:
用户滑动屏幕切换视图时,上一个视图会回收利用。主要任务是视图回收再利用,循环往复。
主要任务是容纳View视图。
从模型层获取数据,然后提供给RecyclerView显示。主要任务是创建ViewHolder和将模型层的数据绑定到ViewHolder上。
RecyclerView不会亲自摆放屏幕上的列表项,摆放列表项的任务被委托给了LayoutManager。主要任务是指定RecyclerView的布局方式。
RecyclerView属于新增的控件,Android将RecyclerView定义在support库里。若要使用RecyclerView,第一步是要在build.gradle中添加对应的依赖库。既然要用kotlin代码去实现,那么还要添加kotlin的依赖库。打开app/build.gradle文件,在dependencies闭包中添加如下内容:
implementation "androidx.core:core-ktx:+"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.recyclerview:recyclerview:1.1.0'
implementation 'androidx.cardview:cardview:1.0.0'
添加完之后记得Sync Now同步。
在activity_main.xml中添加RecyclerView控件,并指定id,然后将宽度和高度都设置为match_parent,这样RecyclerView就沾满了整个布局的空间。代码如下:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
class Fruit(val name:String, val imageId: Int)
在layout目录下新建fruit_item.xml,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="60dp"
app:layout_constraintTop_toTopOf="parent">
<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="60dp"/>
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>
ViewHolder承载的是每一个列表项的视图,所以当使用RecyclerView的时候需要先对ViewHolder进行初始化定义。
package com.example.myadapter.holder
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.example.myadapter.R
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val fruitImage: ImageView = view.findViewById(R.id.fruitImage)
val fruitName: TextView = view.findViewById(R.id.fruitName)
}
MyAdapter继承RecyclerView.Adapter必须重写onCreateViewHolder()、onBindViewHolder()和getItemCount()三个方法。
package com.example.myadapter.adapter
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.example.myadapter.Fruit
import com.example.myadapter.R
import com.example.myadapter.holder.ViewHolder
class MyAdapter(private val fruitList: List<Fruit>) : RecyclerView.Adapter<ViewHolder>(){
// 用于创建ViewHolder实例,并把加载的布局传入到构造函数去,再把ViewHolder实例返回
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view: View = LayoutInflater.from(parent.context).inflate(R.layout.fruit_item, parent, false)
return ViewHolder(view)
}
// 返回RecyclerView的子项数目
override fun getItemCount(): Int = fruitList.size
// 将数据绑定在ViewHolder上,用于对子项的数据进行赋值,会在每个子项被滚动到屏幕内时执行
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
// position得到当前项的Fruit实例
val fruit: Fruit = fruitList[position]
holder.fruitImage.setImageResource(fruit.imageId)
holder.fruitName.text = fruit.name
}
}
package com.example.myadapter
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.myadapter.adapter.MyAdapter
import kotlinx.android.synthetic.main.activity_main.*
import java.util.ArrayList
class MainActivity : AppCompatActivity(){
private val listFruit = ArrayList<Fruit>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 初始化水果数据
initFruits()
/* 1.获取RecyclerView对象
final RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
kotlin可以直接在xml中调用控件ID,固此第一步可省略 */
// 2.创建线性布局管理器(默认是垂直方向)
val layoutManager = LinearLayoutManager(this)
// 3.为RecyclerView指定布局管理对象,此处就是直接调用RecyclerView控件的id:recyclerView
recyclerView.layoutManager = layoutManager
// 4.创建Adapter,并将水果数据listFruit传入到MyAdapter的构造函数中
val adapter = MyAdapter(listFruit)
// 5.填充Adapter,调用adapter方法完成适配器设置,这样RecyclerView和数据之间的关联就建立完成了
recyclerView.adapter = adapter
}
private fun initFruits() {
// repeat(times: Int)方法是重复执行代码块内容,参数代表执行的次数
repeat(2) {
listFruit.add(Fruit("Apple", R.drawable.apple_pic))
listFruit.add(Fruit("Banana", R.drawable.banana_pic))
listFruit.add(Fruit("Orange", R.drawable.orange_pic))
listFruit.add(Fruit("Watermelon", R.drawable.watermelon_pic))
listFruit.add(Fruit("Pear", R.drawable.pear_pic))
listFruit.add(Fruit("Grape", R.drawable.grape_pic))
listFruit.add(Fruit("Pineapple", R.drawable.pineapple_pic))
listFruit.add(Fruit("Strawberry", R.drawable.strawberry_pic))
listFruit.add(Fruit("Cherry", R.drawable.cherry_pic))
listFruit.add(Fruit("Mango", R.drawable.mango_pic))
}
}
}