• Android开发基础——RecyclerView


    RecyclerView是比LitView更为强大的控件,其优化了ListView的不足。Android官方也更推荐使用RecyclerView。

    基本用法

    RecyclerView属于新增控件,Google将RecyclerView控件定义在AndroidX当中,用户只需要在项目的build.gradle中添加RecyclerView库的依赖,就能够保证在所有Android系统版本上都可以使用RecyclerView控件。

    在app/build.gradle文件中的dependencis中添加如下内容:

    1. dependencies {
    2. implementation 'androidx.core:core-ktx:1.3.2'
    3. implementation 'androidx.appcompat:appcompat:1.2.0'
    4. implementation 'com.google.android.material:material:1.3.0'
    5. implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
    6. implementation 'androidx.recyclerview:recyclerview:1.2.0'
    7. testImplementation 'junit:junit:4.+'
    8. androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    9. androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
    10. }

    然后修改布局中的代码:

    1. "1.0" encoding="utf-8"?>
    2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    3. android:layout_width="match_parent"
    4. android:layout_height="match_parent">
    5. <androidx.recyclerview.widget.RecyclerView
    6. android:id="@+id/recyclerView"
    7. android:layout_width="match_parent"
    8. android:layout_height="match_parent"/>
    9. LinearLayout>

    由于RecyclerView并不是内置在系统SDK中的,因此需要完整的包路径。

    这里构造和之前同样的Fruit类和fruit_item.xml布局文件。

    然后构建新的FruitAdapter适配器,使该适配器继承自RecyclerView.Adapter,并将泛型指定为FruitAdapter.ViewHolder。

    1. class FruitAdapter(val fruitList:List):
    2. RecyclerView.Adapter(){
    3. inner class ViewHolder(view:View):RecyclerView.ViewHolder(view) {
    4. val fruitImage: ImageView = view.findViewById(R.id.fruitImage)
    5. val fruitName: TextView = view.findViewById(R.id.fruitName)
    6. }
    7. override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
    8. val view = LayoutInflater.from(parent.context).inflate(R.layout.fruit_item, parent, false)
    9. return ViewHolder(view)
    10. }
    11. override fun onBindViewHolder(holder: ViewHolder, position: Int) {
    12. val fruit = fruitList[position]
    13. holder.fruitImage.setImageResource(fruit.imageId)
    14. holder.fruitName.text = fruit.name
    15. }
    16. override fun getItemCount() = fruitList.size
    17. }

    上面是RecyclerView适配器的标准写法,首先定义一个内部类ViewHolder,其继承自RecyclerView.ViewHolder,然后ViewHolder的主构造函数要传入一个View参数,该参数通常就是RecyclerView子项的最外层布局,然后用户就可以通过findViewById获取布局中ImageView和TextView的实例了。

    而FruitAdapter继承自RecyclerView.Adapter,就必须要重写onCreateViewHolder/onBindViewHolder/getItemCount三个方法:

    • onCreateViewHolder:用于创建ViewHolder实例,在该方法中加载fruit_item布局,然后创建一个ViewHolder实例,并将加载出来的布局传入构造函数中,最后将ViewHolder的实例返回
    • onBindViewHolder:用于对RecyclerView子项的数据进行复制,会在每个子项被滚动到屏幕内的时候执行,这里通过position参数得到当前项的Fruit实例,然后将之设置到ViewHolder
    • getItemCount:返回数据源的长度
    1. class MainActivity : AppCompatActivity() {
    2. private val fruitList = ArrayList()
    3. override fun onCreate(savedInstanceState: Bundle?) {
    4. super.onCreate(savedInstanceState)
    5. setContentView(R.layout.activity_main)
    6. initFruits()
    7. val layoutManager = LinearLayoutManager(this)
    8. recyclerView.layoutManager = layoutManager
    9. val adapter = FruitAdapter(fruitList)
    10. recyclerView.adapter = adapter
    11. }
    12. private fun initFruits() {
    13. repeat(2) {
    14. fruitList.add(Fruit("Apple", R.drawable.apple_pic))
    15. fruitList.add(Fruit("Banana", R.drawable.banana_pic))
    16. fruitList.add(Fruit("Orange", R.drawable.orange_pic))
    17. fruitList.add(Fruit("Watermelon", R.drawable.watermelon_pic))
    18. fruitList.add(Fruit("Pear", R.drawable.pear_pic))
    19. fruitList.add(Fruit("Grape", R.drawable.grape_pic))
    20. fruitList.add(Fruit("Pineapple", R.drawable.pineapple_pic))
    21. fruitList.add(Fruit("Strawberry", R.drawable.strawberry_pic))
    22. fruitList.add(Fruit("Cherry", R.drawable.cherry_pic))
    23. fruitList.add(Fruit("Mango", R.drawable.mango_pic))
    24. }
    25. }
    26. }

    可以看到,这里设置了同样的initFruits方法,用于初始化所有的数据,然后在onCreate方法中先创建LinearLayoutManager对象,并将之设置到RecyclerView中。LayoutManager用于指定RecyclerView的布局方式,这里使用的是LinearLayoutManager是线性布局的意思,可以实现和ListView类似的效果。之后创建了FruitAdapter实例,并将数据传入FruitAdapter的构造函数中,最后调用RecyclerView的setAdapter方法来完成适配器设置。程序运行后的结果为:

     实现横向滚动和瀑布流布局

    ListView扩展性并不好,其只能实现纵向滚动的效果,而RecyclerView却还能够实现横向流动的效果。

    首先对fruit_item布局进行修改:

    1. "1.0" encoding="utf-8"?>
    2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    3. android:orientation="vertical"
    4. android:layout_width="80dp"
    5. android:layout_height="match_content">
    6. <ImageView
    7. android:id="@+id/fruitImage"
    8. android:layout_width="40dp"
    9. android:layout_height="40dp"
    10. android:layout_gravity="center_vertical"
    11. android:layout_marginTop="10dp"/>
    12. <TextView
    13. android:id="@+id/fruitName"
    14. android:layout_width="wrap_content"
    15. android:layout_height="wrap_content"
    16. android:layout_gravity="center_vertical"
    17. android:layout_marginTop="10dp"/>
    18. LinearLayout>

    之前布局元素是水平排列的,这里将之修改为垂直排列。然后修改Activity中的代码:

    1. override fun onCreate(savedInstanceState: Bundle?) {
    2. super.onCreate(savedInstanceState)
    3. setContentView(R.layout.activity_main)
    4. initFruits()
    5. val layoutManager = LinearLayoutManager(this)
    6. layoutManager.orientation = LinearLayoutManager.HORIZONTAL
    7. recyclerView.layoutManager = layoutManager
    8. val adapter = FruitAdapter(fruitList)
    9. recyclerView.adapter = adapter
    10. }

    这里只是将layoutManager的排列方向设为了水平方向。程序运行后的结果为:

     RecyclerView之所以能够实现ListView无法实现的效果是因为RecyclerView的布局管理由LayoutManager完成。除了LinearLayoutManager之外,RecyclerView还提供了GridLayoutManager和StaggeredGridLayoutManager这两种内置的布局排列方式。前者可以实现网格布局,后者则可以实现瀑布流布局。

    这里看一下瀑布流布局,首先是修改fruit_item布局:

    1. "1.0" encoding="utf-8"?>
    2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    3. android:orientation="vertical"
    4. android:layout_width="match_parent"
    5. android:layout_height="wrap_content"
    6. android:margin="5dp">
    7. <ImageView
    8. android:id="@+id/fruitImage"
    9. android:layout_width="40dp"
    10. android:layout_height="40dp"
    11. android:layout_gravity="center_vertical"
    12. android:layout_marginTop="10dp"/>
    13. <TextView
    14. android:id="@+id/fruitName"
    15. android:layout_width="wrap_content"
    16. android:layout_height="wrap_content"
    17. android:layout_gravity="left"
    18. android:layout_marginTop="10dp"/>
    19. LinearLayout>

    这里将LinearLayout的宽度改为了match_parent,这是因为瀑布流布局的宽度应该是根据布局的列数自动适配的,而不是固定值。然后修改Activity中的代码:

    1. class MainActivity : AppCompatActivity() {
    2. private val fruitList = ArrayList()
    3. override fun onCreate(savedInstanceState: Bundle?) {
    4. super.onCreate(savedInstanceState)
    5. setContentView(R.layout.activity_main)
    6. initFruits()
    7. val layoutManager = StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL)
    8. recyclerView.layoutManager = layoutManager
    9. val adapter = FruitAdapter(fruitList)
    10. recyclerView.adapter = adapter
    11. }
    12. private fun initFruits() {
    13. repeat(2) {
    14. fruitList.add(Fruit(getRandomLengthString("Apple"), R.drawable.apple_pic))
    15. fruitList.add(Fruit(getRandomLengthString("Banana"), R.drawable.banana_pic))
    16. fruitList.add(Fruit(getRandomLengthString("Orange"), R.drawable.orange_pic))
    17. fruitList.add(Fruit(getRandomLengthString("Watermelon"), R.drawable.watermelon_pic))
    18. fruitList.add(Fruit(getRandomLengthString("Pear"), R.drawable.pear_pic))
    19. fruitList.add(Fruit(getRandomLengthString("Grape"), R.drawable.grape_pic))
    20. fruitList.add(Fruit(getRandomLengthString("Pineapple"), R.drawable.pineapple_pic))
    21. fruitList.add(Fruit(getRandomLengthString("Strawberry"), R.drawable.strawberry_pic))
    22. fruitList.add(Fruit(getRandomLengthString("Cherry"), R.drawable.cherry_pic))
    23. fruitList.add(Fruit(getRandomLengthString("Mango"), R.drawable.mango_pic))
    24. }
    25. }
    26. private fun getRandomLengthString(str:String):String {
    27. val n = (1..20).random()
    28. val builder = StringBuilder()
    29. repeat(n) {
    30. builder.append(str)
    31. }
    32. return builder.toString()
    33. }
    34. }

    其实上面的代码并没有修改多少,只是将原有的LinearLayoutManager修改为StaggeredGridLayoutManager。StaggeredGridLayoutManager的构造函数接收两个参数,第一个参数用于指定布局的列数,3表示3列,第二个参数用于指定布局的排列方向,这里为纵向排列。同时这里使用getRandomLengthString生成不同长度的字符串来显示瀑布流的效果。程序运行结果为:

     RecyclerView的点击事件

    和ListView不同,RecyclerView需要用户自己给子项具体的View注册点击事件。

    修改FruitAdapter中的代码:

    1. override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
    2. val view = LayoutInflater.from(parent.context).inflate(R.layout.fruit_item, parent, false)
    3. val viewHolder = ViewHolder(view)
    4. viewHolder.itemView.setOnClickListener {
    5. val position = viewHolder.bindingAdapterPosition
    6. val fruit = fruitList[position]
    7. Toast.makeText(parent.context, "You clicked view ${fruit.name}", Toast.LENGTH_SHORT).show()
    8. }
    9. viewHolder.fruitImage.setOnClickListener {
    10. val position = viewHolder.bindingAdapterPosition
    11. val fruit = fruitList[position]
    12. Toast.makeText(parent.context, "You clicked image ${fruit.name}", Toast.LENGTH_SHORT).show()
    13. }
    14. return viewHolder
    15. }

    在上面的代码中,仍然是在onCreateViewHolder中注册点击事件,并同时为最外层布局和ImageView都注册了点击事件,itemView表示最外层布局,fruitImage表示图片,这就是RecyclerView的特别之处。程序运行结果为:

      而显然点击文本是不会有响应的。

  • 相关阅读:
    《操作系统-真象还原》10. 输入输出系统
    论文复现|Panoptic Deeplab(全景分割PyTorch)
    supervisord 进程管理器 Laravel执行队列
    【安卓APP毕业设计源码】android在线点单系统APP
    【Langchain Agent研究】SalesGPT项目介绍(五)
    CentOS(2)——rpm和yum区别与联系
    股票战法课程之业绩跟股价的关系
    Splunk自学笔记
    SSL证书的加密算法有哪些?
    JAVA基础——day07
  • 原文地址:https://blog.csdn.net/SAKURASANN/article/details/126921116