Android RecyclerView: Ce este, învață cu exemple simple
⚡ Rezumat inteligent
RecyclerView is an advanced Android widget that displays large, scrollable datasets efficiently by recycling a limited number of views. It relies on an adapter, a layout manager, and a view holder to bind data collections that change at runtime.

În ce este RecyclerView Android?
RecyclerView is a more flexible and advanced version of GridView and ListView. It is a container for displaying large datasets that can be scrolled efficiently by maintaining a limited number of views. You can use the RecyclerView widget when you have data collections whose elements change at runtime depending on a network event or user action.
Vizualizări
Android platforma folosește clasele View și ViewGroup pentru a desena elemente pe ecran. Aceste clase sunt abstract and are extended to different implementations to suit a use-case. TextView, for example, has the simple purpose of displaying text content on the screen. EditText extends from the same View class and adds more functionality to enable the user to input data.
It is possible to create our own custom views to achieve more flexibility when developing user interfaces. The View class provides methods we can override to draw on the screen and a means to pass in parameters such as width, height, and our own custom attributes that we would want to add to our View to make it behave as we wish.
ViewGroups
The ViewGroup class is a kind of View but, unlike the simple View class whose responsibility is simply displaying, the ViewGroup gives us the ability to put multiple views into one view, which we can reference as a whole. In this case, the View created at the top level that we add other simple views to is called the “parent,” and the views that are added inside are “children.”
We can imagine a View as an Array and a ViewGroup as an Array of Arrays. Given that an Array of Arrays is an Array itself, we can see how a ViewGroup can be treated like a View.
var arr1 = [1,2,3] //imagine a simple View as an Array //we can imagine this as a NumberTextView which doesn't really exist //but we could imagine there's one that makes it easy to use numbers var arr2 = ["a","b","c"] // We can imagine this as another simple view var nestedArr = [arr1,arr2] //in our analogy, we can now group views //together and the structure that would hold that would be what we call the ViewGroup
The ViewGroup also enables us to define how the children are organized inside the view, for example, whether they are laid out vertically or horizontally. We can have different rules for interaction inside the View. For example, TextViews following each other should have a 12dp distance, while an ImageView followed by a TextView should have a distance of 5dp.
Acesta ar fi cazul dacă am fi dezvoltatping propriul nostru ViewGroup de la zero. Pentru a simplifica aceste configurații, Android oferă o clasă numită LayoutParams, pe care o putem folosi pentru a introduce aceste configurații.
Android documentaţie provides some default parameters we would implement when configuring our own ViewGroup. Some common parameters pertain to width, height, and margin. By default, these configurations have the structure android:layout_height for height and android:layout_width for width. In this regard, when you create your ViewGroup, you can further create LayoutParams specific to the way you want your ViewGroup to behave.
Android comes with default Views and ViewGroups we can use to do many of the common tasks we need. One example we have mentioned is a TextView. That is a simple view that comes with configurable aspects such as height, width, and textSize. We also have an ImageView for displaying images and EditText, as we mentioned, among many others. Android are, de asemenea, ViewGroups personalizate la care putem adăuga vizualizările noastre și obține comportamentul așteptat.
LinearLayout
LinearLayout allows us to add View items into it. LinearLayout has an orientation attribute that dictates how it will be laid out on the screen. It also has LinearLayout.LayoutParams that dictate the rules for the views inside. For example, the attribute android:center_horizontal would center the views along the horizontal axis, whereas android:center_vertical would center the content of the view along the vertical axis.
Here are some images to help understand centering. We take this to be a simple TextView inside a 200px by 200px space; the centering attributes would make it behave as follows.
android: center_horizontal
Conținut centrat pe orizontală
android:center_vertical
Vertically centered content
android:centru
Conținut centrat
Componentele de bază ale RecyclerView
Componentele de bază ale RecyclerView
Următoarele sunt componentele importante ale RecyclerView:
RecyclerView.Adapter
Groundwork #1 – Model de adaptor
An adapter is a device that transforms the attributes of one system or device to those of an otherwise incompatible device or system. Some adapters modify signal or power attributes, while others simply adapt the physical form of one connector to another.
A simple real-life example that explains an adapter is when we need to connect devices together, but they have connection ports that do not match each other. This could be the case when you visit a different country that uses different kinds of sockets. If you carry your phone or laptop charger, it would be impossible to connect it to the power outlets. However, you would not give up but simply get an adapter that comes in between the power outlet and your charger and enables charging to happen.
This is the case in programming when we want to connect two data structures together to fulfill a task, but their default ports do not have a way to communicate with each other.
We will use the simple example of a device and a charger. We will have two instances of chargers, an American one and a British one.
class AmericanCharger() { var chargingPower = 10 } class BritishCharger(){ var chargingPower = 5 }
We will then create two devices.
class AmericanDevice() class BritishDevice()
Ca exemplu, putem crea apoi câteva instanțe ale dispozitivelor pentru a juca împreună.
var myAmericanPhone = new AmericanDevice() var myBritishPhone = new BritishDevice()
We will then introduce the concept of charging for both devices by adding a method in the devices called charge(). The method takes in its respective charger and does charging based on it.
sealed trait Device class AmericanDevice : Device{ fun charge(charger:AmericanCharger){ //Do some American charging } } class BritishDevice: Device{ fun charge(charger:BritishCharger){ //Do some British charging } }
In this case, based on our analogy, we would for one reason or another be in need of using a BritishCharger when using an AmericanDevice, or vice versa.
În lumea programării, acest lucru se întâmplă de obicei atunci când amestecăm biblioteci care oferă aceeași funcționalitate (în contextul nostru, funcționalitatea noastră comună se încarcă). Ar trebui să găsim o modalitate de a activa acest lucru.
If we follow the analogy, we will need to go to an electronics shop and buy an adapter that enables us to charge AmericanDevices given BritishChargers. From the programming perspective, we will be the manufacturer of the adapter.
We will make an adapter that matches the exact pattern we would need for creating the other. We will implement it as a class as follows. It does not necessarily need to be a class and could be a function that highlights what the adapter pattern generally does. We use a class as it matches up with most usage on Android.
class AmericanToBritishChargerAdapter(theAmericanCharger:AmericanCharger){ fun returnNewCharger(): BritishCharger{ //convert the American charger to a BritishCharger //we would change the American charging functionality //to British charging functionality to make sure the //adapter doesn't destroy the device. The adapter could //, for example, control the power output by dividing by 2 //our adapter could encompass this functionality in here var chargingPower:Int = theAmericanCharger.chargingPower / 2 var newBritishCharger = new BritishCharger() newBritishCharger.chargingPower = theAmericanCharger.chargingPower/2 return newBritishCharger } }
In the programming world, the difference in the sockets is analogous to the difference in the methods used for charging. The chargers having different methods would make it impossible to use the chargers.
var myBritishDevice = new BritishDevice() var americanChargerIFound = new AmericanCharger()
Trying to call the charge() method on myBritishDevice with americanChargerIFound would not work, as the AmericanDevice only accepts an AmericanCharger. So it is impossible to do this:
var myBritishDevice = new BritishDevice() var americanChargerIFound = new AmericanCharger() myBritishDevice.charge(americanChargerIFound)
In this scenario, the adapter we created, AmericanToBritishChargerAdapter, can now come in handy. We can use the returnNewCharger() method to create a new BritishCharger, which we can use to charge. All we need to do is create an instance of our adapter and feed it the AmericanCharger we have, and it will create a BritishCharger we can use.
var myBritishDevice = new BritishDevice() var americanChargerIFound = new AmericanCharger() //We create the adapter and feed it the americanCharger var myAdapter = AmericanToBritishChargerAdapter(americanChargerIFound) //calling returnNewCharger from myAdapter would return a BritishCharger var britishChargerFromAdapter = myAdapter.returnNewCharger() //and once we have the britishCharger we can now use it myBritishDevice.charge(britishChargerFromAdapter)
RecyclerView.LayoutManager
When dealing with a ViewGroup, we have Views placed inside it. The LayoutManager has the task of describing how the Views are laid out inside.
For comparison, when working with a LinearLayout ViewGroup, the use case we want is the ability to place the items either vertically or horizontally. This is easily implemented by adding an orientation attribute, which tells us how the LinearLayout will be placed on the screen. We can do this by using the android:orientation=VERTICAL|HORIZONTAL atribut.
We also have another ViewGroup called GridLayout. Its use case is when we want to place Views in a rectangular grid structure. This could be for reasons such as making the data we present to the app user easy to consume. By design, the GridLayout enables configurations to help you reach this goal by defining the dimensions of the grid; for example, we can have a 4×4 grid or a 3×2 grid.
RecyclerView.ViewHolder
ViewHolder este un abdomentract class that we also extend from RecyclerView. The ViewHolder provides us with common methods to help us reference a View we have placed on the RecyclerView, even after the recycling machinery in the RecyclerView has changed various references we do not know about.
Liste mari
RecyclerViews are used when we want to present a really large set of Views to the user, all while not exhausting the RAM pe dispozitivul nostru pentru fiecare instanță a View creată.
If we take the case of a contact list, we would have a general idea of how one contact would look in the list. What we would then do is create a template layout, which is actually a View, with slots where various data from our contacts list will fill. The following is pseudo code that explains the whole purpose:
//OneContactView
<OneContact>
<TextView>{{PlaceHolderForName}}</TextView>
<TextView>{{PlaceHolderForAddress}}</TextView>
<ImageView>{{PlaceHolderForProfilePicture}}</ImageView>
<TextView>{{PlaceHolderForPhoneNumber}}</TextView>
</OneContact>
We would then have a ContactList of this nature:
<ContactList> </ContactList>
If we were hardcoding the contents, we would not have a programmatic way of adding new content to the list without rewriting the app. Fortunately for us, adding a View to a ViewGroup is supported by an addView(view:View) method. Even so, it is not how the RecyclerView gets children views added to it.
In our use case, we would have a long list of contacts. For each contact in the list, we would need to create a OneContactView and populate the data inside the View to match the fields in our Contact class. Then once we have the view, we would need to add it to the RecyclerView to show the list.
data class Contact(var name:String, var address:String, var pic:String, var phoneNumber:Int) var contact1 = Contact("Guru","Guru97", "SomePic1.jpg", 991) var contact2 = Contact("Guru","Guru98", "SomePic2.jpg", 992) var contact3 = Contact("Guru","Guru99", "SomePic3.jpg", 993) var myContacts:ArrayList<Contact> = arrayListOf<Contact>(contact1,contact2,contact3)
We have an array of contacts. The OneContactView contains slots to take contents from the Contact class and display them. In RecyclerView, we have to add Views into it so it can help us with its recycling ability.
The RecyclerView does not really allow us to add a view but enables us to add a ViewHolder. So, in this scenario, we have two pieces we want to connect but that do not match. This is where our adapter comes in. RecyclerView provides us with an adapter, much like our AmericanToBritishChargerAdapter() from earlier that enabled us to convert our AmericanCharger, which was unusable with our BritishDevice, into something usable, akin to a power adapter in real life.
In this scenario, the adapter would take our array of contacts and our View, and from there generate ViewHolders that the RecyclerView is willing to accept.
The RecyclerView provides an interface we can extend to create our adapter through the RecyclerView.Adapter class. Inside this adapter is a way to create the ViewHolder class that the RecyclerView wants to work with. So, what we have is the same situation as before, but with one extra thing, the adapter.
We have an array of contacts, a view to display one contact (OneContactView), and a RecyclerView, which is a list of Views that provides recycling services but is only willing to take on ViewHolders. In this scenario, we now have a RecyclerView.Adapter class, which has a method to create ViewHolders inside.
fun createViewHolder(@NonNull parent: ViewGroup, viewType: Int): ViewHolder
RecyclerView.ViewHolder este un element abstract class that takes our View as an argument and converts it to a ViewHolder. It uses the wrapper pattern that is used to extend the abilities of classes.
Groundwork #2 – Model de înveliș
We will use a simple example to demonstrate how we can make animals speak.
sealed trait Animal{ fun sound():String } data class Cat(name:String):Animal{ fun sound(){ "Meow" } } data class Dog(name:String):Animal{ fun sound(){ "Woof" } } var cat1 = Cat("Tubby") var dog1 = Dog("Scooby") cat1.sound() //meow dog1.sound() //woof
In the above example, we have two animals. If, by chance, we wanted to add a method to make them speak, but the library author was no fun, we could still find a way. What we would need is a wrapper for our Animal class. We would do this by taking in the Animal as a constructor for our class.
class SpeechPoweredAnimalByWrapper(var myAnimal:Animal){ fun sound(){ myAnimal.sound() } fun speak(){ println("Hello, my name is ${myAnimal.name}") } }
Now we can pass in an animal instance to the SpeechPoweredAnimalByWrapper. Calling the sound() method on it would call the passed-in animal sound() method. We also have an additional speak() method, which counts as new functionality we add to the animals passed in. We can use it as follows:
var cat1 = Cat("Garfield") cat1.sound()//"meow" cat1.speak()// doesn't work as it isn't implemented var talkingCat = new SpeechPoweredAnimalByWrapper(cat1) talkingCat.sound() //"meow" the sound method calls the one defined for cat1 talkingCat.speak() //"Hello, my name is Garfield"
Using this pattern, we can take classes and add functionality. All we need is to pass a class instance and new methods defined by our wrapping clasă.
In the case above, we used a concrete class. It is also possible to implement the same in an abstract class. We would need to change the SpeechPoweredAnimalByWrapper class to abstract, and we are done. We will change the class name to something shorter to make it more readable.
abstract class SpeechPowered(var myAnimal:Animal){ fun sound(){ myAnimal.sound() } fun speak(){ println("Hello, my name is ${myAnimal.name}") } }
It is the same as before, but it would mean something else. In a normal class, we can have an instance of a class in the same way we created cat1 and dog1. Abstract classes, however, are not meant to be instantiated but are meant for extending other classes. So how would we use the new SpeechPowered(var myAnimal:Animal) abstract class? We can use it by creating new classes that will extend it and, in turn, gain its functionality.
In our example, we will create a class SpeechPoweredAnimal that extends the class.
class SpeechPoweredAnimal(var myAnimal:Animal):SpeechPowered(myAnimal)
var cat1 = Cat("Tubby") var speakingKitty = SpeechPoweredAnimal(cat1) speakingKitty.speak() //"Hello, my name is Tubby"
Acesta este același model folosit în ViewHolder. Clasa RecyclerView.ViewHolder este un element abstract class that adds functionality to the View, much like we added the speak method to the animals. The added functionality is what makes it work when dealing with the RecyclerView.
This is how we would create a OneContactViewHolder from OneContactView:
//The View argument we pass is converted to a ViewHolder which uses the View to give it more abilities and in turn work with the RecyclerView class OneContactViewHolder(ourContactView: View) : RecyclerView.ViewHolder(ourContactView)
RecyclerView has an adapter that allows us to connect our contacts array to the ContactsView with the RecyclerView.
Adăugarea unei vizualizări
The ViewGroup does not redraw automatically but follows a particular schedule. It may be the case on your device that it redraws every 10ms or 100ms; or, if we pick an absurd number, say 1 minute, when we add a View to a ViewGroup, you will see the changes 1 minute later when the ViewGroup “refreshes.”
RecyclerView.Recycler
Groundwork #3 – Caching
One of the best examples of where we regularly do refreshes is in the browser. Let us imagine, for example, the site we are visiting is static and is not sending content dynamically; we would need to keep refreshing to see the changes.
For this example, let us imagine the site in question is Twitter. We would have a series of static tweets listed, and the only way we could see new tweets would be through clicking the refresh button to refetch the content.
Repainting the whole screen is obviously a costly thing. Imagine if we had limited bandwidth with our phone provider, and our tweet list had a lot of images and videos; it would be costly to redownload all the contents of the page on every refresh.
We would need a way to store the already-loaded tweets and ensure our next request has the ability to say which tweets it already has. Therefore, it does not redownload everything and only gets the new tweets, and also checks if some tweet that had been saved locally is no longer there so that it can delete it locally. What we are describing is called caching.
The information we send to the website about the contents we have is called meta-data. So in a real sense, we are not just saying “we want to load your site,” we say “we want to load your site, and here is some of the content we already saved from the last time we loaded; please use it to only send us what is not in there, so we do not use a lot of bandwidth.”
Apeluri de aspect – Lista de tweeturi trebuie să fie nebună
An example of a layout call is scrollToPosition. This is a common example present in things like chatting apps. If someone in a chat thread replies to a chat bubble from earlier, some chatting apps include the reply and a link to the chat bubble, which upon clicking navigates you to where your original message was.
In the case where we call this method before we have a LayoutManager added to our RecyclerView and before we have a RecyclerView.Adapter, the scrollToPosition(n:Int) is simply ignored.
Comunicarea între componentele RecyclerView
Groundwork #4 – Callbacks
The RecyclerView, in doing its work, has a lot of moving parts. It has to deal with the LayoutManager, which tells us how to organize the Views, either linearly or in a grid. It has to deal with an adapter that does the job of converting our items (contactList) into Views (OneContactView) and then into ViewHolders (OneContactViewHolder) that the RecyclerView is willing to work with using the methods it provides.
The raw material for the RecyclerView is our Views, e.g. OneContactView, and a data source.
contactList:Array<Contact>
We have used a simple scenario as a starting point to get a feel of what the RecyclerView is trying to achieve. A base case of when we have a static array of 1000 contacts we want to show the user is easy to understand. The RecyclerView machinery really starts to take life when the list is no longer static. With a dynamic list, we have to think of what happens to the View on the screen when we add an item to the list or remove an item from the list.
RecyclerView.LayoutManager
Apart from deciding how our views are laid out, either linearly or in a grid, the LayoutManager does a lot of work under the hood in helping the Recycler know when to do the recycling.
It is responsible for keeping track of the Views currently visible on the screen and communicating this information to the recycling mechanism. As a user scrolls downwards, the LayoutManager is responsible for informing the recycling system of the Views that go out of focus at the top so that they can be reused, instead of remaining there and consuming memory or being destroyed only to create new ones.
This means the LayoutManager needs to keep track of where the user is as they scroll our list. It does this by having a list of positions that are index based, i.e. the first item starts from 0 and increases to match the number of items in our list.
If we can view 10 items on our list of, say, 100, at the beginning the LayoutManager is aware that it has in focus View-0 all the way to View-9. As we scroll, the LayoutManager is able to calculate the views that get out of focus.
The LayoutManager is able to release these views to the recycling mechanism so that they can be reused (new data can be bound to them; e.g. a View’s contact data can be removed and new contact data from the next segment can replace the placeholders).
This would be a happy case if the list we have is static, but one of the most common use cases of using a RecyclerView is with dynamic lists where data can come from an online endpoint or even maybe from a sensor. Not only is data added, but data on our list is sometimes removed or updated.
The dynamic state of our data can make it very difficult to reason about the LayoutManager. For this reason, the LayoutManager maintains its own list about the items and positions, separate from the list the recycling component uses. This ensures it does its layout job correctly.
At the same time, the RecyclerView’s LayoutManager does not want to misrepresent the data it has. In order to operate correctly, the LayoutManager synchronizes with the RecyclerView.Adapter at given intervals (60ms), sharing information about our list items, i.e. items added, updated, removed, or moved from one position to another. The LayoutManager, upon receiving this information, reorganizes the contents on the screen to match the changes when necessary.
Many of the core operations that deal with RecyclerView rotate around communication between the RecyclerView.LayoutManager and RecyclerView.Adapter that stores our lists of sometimes static or sometimes dynamic data.
In addition, RecyclerView presents us with methods we can use to listen in on events, such as onBindViewHolder when our RecyclerView.Adapter binds content from our list (e.g. a contact) to a ViewHolder so that it gets used to display the info on screen.
Another is onCreateViewHolder, which tells us when the RecyclerView.Adapter takes a regular View like OneContactView and converts it into a ViewHolder item that the RecyclerView can work with.
Apart from the core mechanisms that enable recycling, the RecyclerView provides ways to customize behavior without affecting recycling. Reuse of Views makes it difficult to do common things we are used to doing with static views, such as reacting to onClick events.
As we are aware, the RecyclerView.LayoutManager care prezintă Vizualizările utilizatorului ar putea avea, pentru un moment, o listă diferită de articole de la RecyclerView.Adapter that has the list stored in a database or streaming in from a source. Putting OnClick events directly on Views can lead to unexpected behavior, such as deleting the wrong contact.
Gradle
If we want to use RecyclerView, we need to add it as a dependency in our build.gradle file. In the following example, we have used implementation “androidx.recyclerview:recyclerview:1.1.0”, which is the most current version as per this article.
După adăugarea dependenței la nostru Gradle fișier, ni se va solicita Android Studio to synchronize the changes. This is how our Gradle file will look after adding a RecyclerView in an empty project with just the defaults.
apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' android { compileSdkVersion 29 buildToolsVersion "29.0.2" defaultConfig { applicationId "com.guru99.learnrecycler" minSdkVersion 17 targetSdkVersion 29 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'androidx.core:core-ktx:1.1.0' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test.ext:junit:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' implementation "androidx.recyclerview:recyclerview:1.1.0" }
We only have one layout file at the moment. We will start with a simple example where we use a RecyclerView to show a list of fruit names on the screen.
Listă de obiecte
We will navigate to our MainActivity file and create an array with fruit names inside, right before the onCreate() method that was generated during set up.
package com.guru99.learnrecycler import androidx.appcompat.app.AppCompatActivity import android.os.Bundle class MainActivity : AppCompatActivity() { var fruitNames:Array<String> = arrayOf<String>("Banana", "Mango", "Passion fruit", "Orange", "Grape") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) } }
Our next goal will be to present this list on the screen using a RecyclerView. To do this, we will navigate to the layout directory that holds our layouts and create a View that will be responsible for showing one fruit.
Aspect care va fi folosit pentru fiecare articol din lista noastră
<?xml version="1.0" encoding="utf-8"?> <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/fruitName" /> </TextView>
In the TextView above, we have added an id field that will be used for identifying a View. It is not generated by default. We have given our TextView the id fruitName to match the data, which will be bound to it.
Adăugarea RecyclerView la aspectul principal
In the same activity, there is a main_layout.xml layout file that was generated for us by default. If we chose an empty project, it will have generated an XML containing a ConstraintLayout and inside will be a TextView with “Hello” text. We will delete all the contents and have the layout only contain the RecyclerView as below:
<?xml version="1.0" encoding="utf-8"?> <androidx.recyclerview.widget.RecyclerView 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:id="@+id/fruitRecyclerView" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity" />
We have also added an id attribute for the RecyclerView, which we will use to reference it in our code.
android:id="@+id/fruitRecyclerView"
We will then navigate back to our MainActivity file. Using the ids we have created, we will be able to reference the Views we have just created. We will start by referencing the RecyclerView using the findViewById() method provided by Android. We will do this in our onCreate() method. Our onCreate() method will look as follows.
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) var myFruitRecyclerView: RecyclerView = findViewById(R.id.fruitRecyclerView) }
Creați un ViewHolder
Next, we will create a RecyclerView.ViewHolder, which is responsible for taking our View and converting it to a ViewHolder, which the RecyclerView uses to display our items. We will do this right after our onCreate() method.
package com.guru99.learnrecycler import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.view.View import androidx.recyclerview.widget.RecyclerView class MainActivity : AppCompatActivity() { var fruitNames:Array<String> = arrayOf<String>("Banana", "Mango", "Passion fruit", "Orange", "Grape") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) var myFruitRecyclerView: RecyclerView = findViewById(R.id.fruitRecyclerView) } class FruitViewHolder(fruitView: View): RecyclerView.ViewHolder(fruitView) }
Creați un RecyclerViewAdapter
Next, we will create a FruitArrayAdapter class that extends the RecyclerView.Adapter class. The FruitArrayAdapter we create will be responsible for the following: it will take fruit names from the fruit array, create a ViewHolder using our view one_fruit_view.xml, then bind the fruit to a ViewHolder and dynamically bind the contents to the View we created.
package com.guru99.learnrecycler import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.view.View import androidx.recyclerview.widget.RecyclerView class MainActivity : AppCompatActivity() { var fruitNames:Array<String> = arrayOf<String>("Banana", "Mango", "Passion fruit", "Orange", "Grape") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) var myFruitRecyclerView: RecyclerView = findViewById(R.id.fruitRecyclerView) } class FruitViewHolder(fruitView: View): RecyclerView.ViewHolder(fruitView) class FruitArrayAdapter(var fruitArray: Array<String>) : RecyclerView.Adapter<FruitViewHolder>() }
Android Studio will add red squiggles on our FruitArrayAdapter, telling us we need to implement methods that the RecyclerView can use to connect our array to a ViewHolder.
class FruitListAdapter(var fruitArray: Array<String>) : RecyclerView.Adapter<FruitViewHolder>() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FruitViewHolder { } override fun getItemCount(): Int { } override fun onBindViewHolder(holder: FruitViewHolder, position: Int) { } }
We will start with the easiest bit from the generated code, the getItemCount() method. We know how to get the number of items in our array by calling the array size property.
class FruitListAdapter(var fruitArray: Array<String>) : RecyclerView.Adapter<FruitViewHolder>() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FruitViewHolder { } override fun getItemCount(): Int { return fruitArray.size } override fun onBindViewHolder(holder: FruitViewHolder, position: Int) { } }
Then we will implement the method onCreateViewHolder. This is where the RecyclerView asks us to help it construct a FruitViewHolder. If we recall, this is how our FruitViewHolder class looked:
class FruitViewHolder(fruitView: View): RecyclerView.ViewHolder(fruitView)
It requires our fruitView, which we created as an XML file one_fruit_view.xml. We can create a reference to this XML and convert it to a View as follows.
class FruitListAdapter(var fruitArray: Array<String>) : RecyclerView.Adapter<FruitViewHolder>() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FruitViewHolder { var fruitView = LayoutInflater.from(parent.context).inflate(R.layout.one_fruit_view, parent, false) var fruitViewHolder = FruitViewHolder(fruitView) return fruitViewHolder } override fun getItemCount(): Int { return fruitArray.size } override fun onBindViewHolder(holder: FruitViewHolder, position: Int) { } }
The remaining bit is the onBindViewHolder override.
fun onBindViewHolder(holder: FruitViewHolder, position: Int)
The RecyclerView.Adapter asks with a position integer, which we use to fetch an item from our list. It also provides us with a holder so that we can bind the item we get from the fruitArray to the View that is held inside the view holder. The View held inside a ViewHolder is accessible through the field ViewHolder.itemView. Once we get the view, we can use the id fruitName we created earlier to set the content.
override fun onBindViewHolder(holder: FruitViewHolder, position: Int) { var ourFruitTextView = holder.itemView.findViewById<TextView>(R.id.fruitName) var aFruitName = fruitArray.get(position) ourFruitTextView.setText(aFruitName) }
With that, our FruitArrayAdapter is complete and looks as follows.
class FruitListAdapter(var fruitArray: Array<String>) : RecyclerView.Adapter<FruitViewHolder>() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FruitViewHolder { var fruitView = LayoutInflater.from(parent.context).inflate(R.layout.one_fruit_view, parent, false) var fruitViewHolder = FruitViewHolder(fruitView) return fruitViewHolder } override fun getItemCount(): Int { return fruitArray.size } override fun onBindViewHolder(holder: FruitViewHolder, position: Int) { var ourFruitTextView = holder.itemView.findViewById<TextView>(R.id.fruitName) var aFruitName = fruitArray.get(position) ourFruitTextView.setText(aFruitName) } }
Finally, we are ready to wire up the remaining pieces of our RecyclerView, which are creating a LayoutManager that will tell the RecyclerView how to display the contents of the list, whether to show in a linear manner using LinearLayoutManager or in a grid using GridLayoutManager or StaggeredGridLayoutManager.
Creați Manager de aspect
We will go back inside our onCreate function and add the LayoutManager.
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) var myFruitRecyclerView: RecyclerView = findViewById(R.id.fruitRecyclerView) var fruitLinearLayout = LinearLayoutManager(this) myFruitRecyclerView.layoutManager =fruitLinearLayout }
Conectați adaptorul nostru la articole și setați-l pe RecyclerView
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) var myFruitRecyclerView: RecyclerView = findViewById(R.id.fruitRecyclerView) var fruitLinearLayout = LinearLayoutManager(this) myFruitRecyclerView.layoutManager =fruitLinearLayout var fruitListAdapter = FruitListAdapter(fruitNames) myFruitRecyclerView.adapter =fruitListAdapter }
We have also created a fruitListAdapter instance and fed it the array of fruit names. And basically, we are all done. The complete MainActivity.kt file looks as follows.
package com.guru99.learnrecycler import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.TextView import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView class MainActivity : AppCompatActivity() { var fruitNames:Array<String> = arrayOf<String>("Banana", "Mango", "Passion fruit", "Orange", "Grape") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) var myFruitRecyclerView: RecyclerView = findViewById(R.id.fruitRecyclerView) var fruitLinearLayout = LinearLayoutManager(this) myFruitRecyclerView.layoutManager =fruitLinearLayout var fruitListAdapter = FruitListAdapter(fruitNames) myFruitRecyclerView.adapter =fruitListAdapter } class FruitViewHolder(fruitView: View): RecyclerView.ViewHolder(fruitView) class FruitListAdapter(var fruitArray: Array<String>) : RecyclerView.Adapter<FruitViewHolder>() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FruitViewHolder { var fruitView = LayoutInflater.from(parent.context).inflate(R.layout.one_fruit_view, parent, false) var fruitViewHolder = FruitViewHolder(fruitView) return fruitViewHolder } override fun getItemCount(): Int { return fruitArray.size } override fun onBindViewHolder(holder: FruitViewHolder, position: Int) { var ourFruitTextView = holder.itemView.findViewById<TextView>(R.id.fruitName) var aFruitName = fruitArray.get(position) ourFruitTextView.setText(aFruitName) } } }




