Android development foundation - RecyclerView

RecyclerView is a more powerful control than LitView, which optimizes the shortcomings of ListView. Android officials also recommend using RecyclerView.

basic usage

RecyclerView is a new control. Google defines the RecyclerView control in AndroidX. Users only need to add the RecyclerView library dependency in the project's build.gradle to ensure that the RecyclerView control can be used on all Android system versions.

Add the following to the dependencies in the app/build.gradle file:

dependencies {

    implementation 'androidx.core:core-ktx:1.3.2'
    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'com.google.android.material:material:1.3.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
    implementation 'androidx.recyclerview:recyclerview:1.2.0'
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}

Then modify the code in the layout:

<?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="match_parent">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</LinearLayout>

Since RecyclerView is not built into the system SDK, a full package path is required.

The same Fruit class and fruit_item.xml layout file as before are constructed here.

Then build a new FruitAdapter adapter, make the adapter inherit from RecyclerView.Adapter, and specify the generic as FruitAdapter.ViewHolder.

class FruitAdapter(val fruitList:List<Fruit>):
    RecyclerView.Adapter<FruitAdapter.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_item, 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
    }

    override fun getItemCount() = fruitList.size
}

The above is the standard way of writing the RecyclerView adapter. First, define an internal class ViewHolder, which inherits from RecyclerView.ViewHolder, and then the main constructor of ViewHolder needs to pass in a View parameter, which is usually the outermost layout of the RecyclerView subitem, and then the user You can get the instances of ImageView and TextView in the layout through findViewById.

And FruitAdapter inherits from RecyclerView.Adapter, you must rewrite the three methods of onCreateViewHolder/onBindViewHolder/getItemCount:

  • onCreateViewHolder: used to create a ViewHolder instance, load the fruit_item layout in this method, then create a ViewHolder instance, pass the loaded layout into the constructor, and finally return the ViewHolder instance
  • onBindViewHolder: Used to copy the data of the RecyclerView subitems, which will be executed when each subitem is scrolled into the screen. Here, the Fruit instance of the current item is obtained through the position parameter, and then set to the ViewHolder
  • getItemCount: returns the length of the data source
class MainActivity : AppCompatActivity() {

    private val fruitList = ArrayList<Fruit>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        initFruits()
        val layoutManager = LinearLayoutManager(this)
        recyclerView.layoutManager = layoutManager
        val adapter = FruitAdapter(fruitList)
        recyclerView.adapter = adapter
    }

    private fun initFruits() {
        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))
        }
    }
}

As you can see, the same initFruits method is set here to initialize all data, and then in the onCreate method first create a LinearLayoutManager object and set it to the RecyclerView. LayoutManager is used to specify the layout method of RecyclerView. Here, LinearLayoutManager is used to mean linear layout, which can achieve a similar effect to ListView. After that, a FruitAdapter instance is created, and the data is passed into the FruitAdapter constructor, and finally the setAdapter method of RecyclerView is called to complete the adapter setting. The result after running the program is:

 Implement horizontal scrolling and waterfall layout

The scalability of ListView is not good, it can only achieve the effect of vertical scrolling, but RecyclerView can also achieve the effect of horizontal flow.

First modify the fruit_item layout:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="80dp"
    android:layout_height="match_content">

    <ImageView
        android:id="@+id/fruitImage"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:layout_gravity="center_vertical"
        android:layout_marginTop="10dp"/>

    <TextView
        android:id="@+id/fruitName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:layout_marginTop="10dp"/>

</LinearLayout>

The previous layout elements were arranged horizontally, and here they are changed to be arranged vertically. Then modify the code in Activity:

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        initFruits()
        val layoutManager = LinearLayoutManager(this)
        layoutManager.orientation = LinearLayoutManager.HORIZONTAL
        recyclerView.layoutManager = layoutManager
        val adapter = FruitAdapter(fruitList)
        recyclerView.adapter = adapter
    }

Here we just set the arrangement direction of layoutManager to the horizontal direction. The result after running the program is:

 The reason why RecyclerView can achieve the effect that ListView cannot achieve is because the layout management of RecyclerView is done by LayoutManager. In addition to LinearLayoutManager, RecyclerView also provides two built-in layout arrangements, GridLayoutManager and StaggeredGridLayoutManager. The former can implement grid layout, while the latter can implement waterfall flow layout.

Here's a look at the waterfall flow layout. First, modify the fruit_item layout:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:margin="5dp">

    <ImageView
        android:id="@+id/fruitImage"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:layout_gravity="center_vertical"
        android:layout_marginTop="10dp"/>

    <TextView
        android:id="@+id/fruitName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="left"
        android:layout_marginTop="10dp"/>

</LinearLayout>

Here, the width of LinearLayout is changed to match_parent, because the width of the waterfall flow layout should be automatically adapted according to the number of columns of the layout, rather than a fixed value. Then modify the code in Activity:

class MainActivity : AppCompatActivity() {

    private val fruitList = ArrayList<Fruit>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        initFruits()
        val layoutManager = StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL)
        recyclerView.layoutManager = layoutManager
        val adapter = FruitAdapter(fruitList)
        recyclerView.adapter = adapter
    }

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

    private fun getRandomLengthString(str:String):String {
        val n = (1..20).random()
        val builder = StringBuilder()
        repeat(n) {
            builder.append(str)
        }
        return builder.toString()
    }
}

In fact, the above code has not been modified much, but the original LinearLayoutManager is changed to StaggeredGridLayoutManager. The constructor of StaggeredGridLayoutManager receives two parameters, the first parameter is used to specify the number of columns of the layout, 3 means 3 columns, and the second parameter is used to specify the arrangement direction of the layout, here is the vertical arrangement. At the same time, getRandomLengthString is used here to generate strings of different lengths to show the effect of the waterfall. The result of running the program is:

 Click event of RecyclerView

Unlike ListView, RecyclerView requires users to register click events for specific Views of subitems.

Modify the code in FruitAdapter:

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.fruit_item, parent, false)
        val viewHolder = ViewHolder(view)

        viewHolder.itemView.setOnClickListener {
            val position = viewHolder.bindingAdapterPosition
            val fruit = fruitList[position]
            Toast.makeText(parent.context, "You clicked view ${fruit.name}", Toast.LENGTH_SHORT).show()
        }

        viewHolder.fruitImage.setOnClickListener {
            val position = viewHolder.bindingAdapterPosition
            val fruit = fruitList[position]
            Toast.makeText(parent.context, "You clicked image ${fruit.name}", Toast.LENGTH_SHORT).show()
        }

        return viewHolder
    }

In the above code, the click event is still registered in onCreateViewHolder, and the click event is registered for both the outermost layout and ImageView at the same time. itemView represents the outermost layout, and fruitImage represents the image. This is the special feature of RecyclerView. The result of running the program is:

  And obviously clicking on the text will not respond.

Guess you like

Origin blog.csdn.net/SAKURASANN/article/details/126921116