RecyclerView相对于ListView拥有更高的性能和扩展性
不过如果理解了ListView的性能优化,就能对RecyclerView有更深刻的认识
有关ListView的优化,可阅读上一篇博文《ListView的创建和优化》
由于RecyclerView
是新增控件,在使用之前需要在app/build.gradle
文件中添加其依赖
dependencies {
//·······略·······
implementation 'androidx.recyclerview:recyclerview:1.1.0'
}
然后我们修改布局文件,为其添加一个RecyclerView
控件
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
由于RecyclerView并没有内置在系统SDK中,所以需要写出完整的包路径
接着我们为其创建一个适配器
//创建一个适配器,其继承于RecyclerView.Adapter,泛型指定为FruiAdapter.ViewHolder(需要自定义的内部类)
class FruitAdapter(val fruitList:List<Fruit>): RecyclerView.Adapter<FruitAdapter.ViewHolder>() {
//用于缓存控件实例的内部类ViewHolder
inner class ViewHolder(view:View):RecyclerView.ViewHolder(view){
val fruitImage:ImageView = view.findViewById(R.id.fruitImage)
val fruitName:TextView = view.findViewById(R.id.fruitName)
}
//重写父类的三个方法
//创建并自动缓存布局
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.fruit_layout,parent,false)
return ViewHolder(view)
}
//当子项进入屏幕时调用,对子项进行赋值
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
//获取当前项的元素
val fruit = fruitList[position]
//将元素的值赋给当前子项的各个控件
holder.fruitImage.setImageResource(fruit.imageId)
holder.fruitName.text = fruit.name
}
//告诉RecyclerView有多少个子项,直接返回列表大小即可
override fun getItemCount(): Int {
return fruitList.size
}
}
最后修改MainActivity
,开始使用RecyclerView
class MainActivity : AppCompatActivity() {
//数据源
private val fruitList = ArrayList<Fruit>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//初始化数据列表
initfruit()
//用于指定RecyclerView的布局方式(相对于ListView的巨大优势)
val layoutManager = LinearLayoutManager(this)
recyclerView.layoutManager = layoutManager
//设置适配器
val adapter = FruitAdapter(fruitList)
recyclerView.adapter = adapter
}
private fun initfruit(){
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))
}
}
}
ListView的布局排列是由自身去管理的,而RecyclerView则将这个工作交给了LayoutManager.
LayoutManager有一套可扩展的布局排列接口,子类只要按照接口的规范来实现,就能实现不同排列方式的布局
由纵向滚动变为横向滚动十分的简单
首先我们要改变子项布局元素的排列方式为垂直
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="80dp"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/fruitImage"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_gravity="center_horizontal"
android:layout_marginLeft="10dp" />
<TextView
android:id="@+id/fruitName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginLeft="10dp"/>
LinearLayout>
然后在MainActivity
中调用LinearLayoutManager
的setOrientation()
方法来设置布局的排列方式,只需要添加一行代码即可
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//·······略·······
val layoutManager = LinearLayoutManager(this)
//添加下面这行代码来表明布局排列方式为水平
layoutManager.orientation = LinearLayoutManager.HORIZONTAL
recyclerView.layoutManager = layoutManager
//·······略·······
}
}
先修改子项的布局文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp">
<ImageView
android:id="@+id/fruitImage"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_gravity="center_horizontal"
android:layout_marginLeft="10dp" />
<TextView
android:id="@+id/fruitName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:layout_marginLeft="10dp"/>
LinearLayout>
由于瀑布流布局的宽度应该是根据列数来自动适配的,因此LinearLayout的宽度应改为match_parent。
使用layout_margin来让子项之间留一点间距
TextView改为左对齐,比较美观
最后,只需要修改MainActivity
的onCreate()
方法中的一行代码即可
class MainActivity : AppCompatActivity() {
//·······略·······
//将StaggeredGridLayoutManager实例设置到RecyclerView
val layoutManager = StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL)
recyclerView.layoutManager = layoutManager
//·······略·······
}
StaggeredGridLayoutManager的构造方法接受两个参数
第一个参数表示列数
第二个参数表示布局的排列方式,我们这里设置了垂直排列
修改FruitAdapter
类中的onCreateViewHolder()
方法
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.fruit_layout,parent,false)
val viewHolder = ViewHolder(view)
//为最外层布局设置点击事件
viewHolder.itemView.setOnClickListener{
//获取用户点击的position
val position = viewHolder.adapterPosition
//获取position处的数据列表元素
val fruit = fruitList[position]
//使用Toast展示
Toast.makeText(parent.context,"Click ${fruit.name}",Toast.LENGTH_SHORT).show()
}
return viewHolder
}
上述中itemView
代表最外层布局,也可以将其改为任意布局中的控件(比如fruitImage)
RecyclerView
的强大之处也在于此,它可以实现任意子项中任意控件或者布局的点击事件