Android RecyclerView : Qu'est-ce que c'est, apprenez avec des exemples simples
Qu'est-ce que RecyclerView dans Android?
Le RecycleurVoir est un widget qui est une version plus flexible et avancรฉe de GridView et ListView. Il s'agit d'un conteneur permettant d'afficher de grands ensembles de donnรฉes qui peuvent dรฉfiler efficacement en conservant un nombre limitรฉ de vues. Vous pouvez utiliser le widget RecyclerView lorsque vous disposez de collections de donnรฉes dont les รฉlรฉments changent au moment de l'exรฉcution en fonction d'un รฉvรฉnement rรฉseau ou d'une action de l'utilisateur.
Vues
Le Android La plateforme utilise les classes View et ViewGroup pour dessiner des รฉlรฉments ร l'รฉcran. Ces classes sont absolumenttracLes classes TextView et EditText sont รฉtendues ร diffรฉrentes implรฉmentations pour s'adapter ร un cas d'utilisation. TextView, par exemple, a pour simple fonction d'afficher du texte ร l'รฉcran. EditText hรฉrite de la mรชme classe View et ajoute des fonctionnalitรฉs permettant ร l'utilisateur de saisir des donnรฉes.
Il est possible de crรฉer nos propres vues personnalisรฉes afin de bรฉnรฉficier d'une plus grande flexibilitรฉ lors du dรฉveloppement.ping Interfaces utilisateur. La classe View fournit des mรฉthodes que nous pouvons redรฉfinir pour dessiner ร l'รฉcran et un moyen de transmettre des paramรจtres tels que la largeur, la hauteur et nos propres attributs personnalisรฉs que nous souhaiterions ajouter ร notre vue pour qu'elle se comporte comme nous le souhaitons.
Afficher les groupes
La classe ViewGroup est une sorte de View mais, contrairement ร la simple classe View dont la responsabilitรฉ est simplement d'afficher, ViewGroup nous donne la possibilitรฉ de regrouper plusieurs vues dans une seule vue, que nous pouvons rรฉfรฉrencer dans son ensemble. Dans ce cas, la vue crรฉรฉe au niveau supรฉrieur ร laquelle nous ajoutons d'autres vues simples (nous pouvons รฉgalement ajouter des viewGroups) est appelรฉe le ยซ parent ยป et les vues ajoutรฉes ร l'intรฉrieur sont des ยซ enfants ยป.
Nous pouvons imaginer une vue sous forme de tableau et un ViewGroup sous forme de tableau de tableaux. รtant donnรฉ qu'un tableau de tableaux est un tableau lui-mรชme, nous pouvons voir comment un ViewGroup peut รชtre traitรฉ comme une vue.
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 anology, we can now group views //together and the structure that would hold that would be what we call the ViewGroup
Le ViewGroup nous permet รฉgalement de dรฉfinir comment les enfants sont organisรฉs ร l'intรฉrieur de la vue, par exemple, sont disposรฉs verticalement ou horizontalement. Nous pouvons avoir diffรฉrentes rรจgles d'interaction ร l'intรฉrieur de la vue. Par exemple, les TextViews qui se suivent doivent avoir une distance de 12 dp tandis que les ImageViews suivis de TextView doivent avoir une distance de 5 dp.
Ce serait le cas si nous รฉtions dรฉveloppรฉsping Nous avons crรฉรฉ notre propre ViewGroup ร partir de zรฉro. Pour simplifier ces configurations, Android fournit une classe appelรฉe LayoutParams, que nous pouvons utiliser pour saisir ces configurations.
Android Documentation fournit certains paramรจtres par dรฉfaut que nous implรฉmenterions lors de la configuration de notre propre ViewGroup. Certains paramรจtres courants concernent la largeur, la hauteur et la marge. Par dรฉfaut, ces configurations ont la structure android:layout_height pour la hauteur, par exemple, android:layout_width ร cet รฉgard, lorsque vous crรฉez votre ViewGroup, vous pouvez en outre crรฉer des LayoutParams spรฉcifiques ร la faรงon dont vous souhaitez que votre ViewGroup se comporte.
Android est livrรฉ avec des vues et des ViewGroups par dรฉfaut que nous pouvons utiliser pour effectuer de nombreuses tรขches courantes dont nous avons besoin. Un exemple que nous avons mentionnรฉ est un TextView. Il s'agit d'une vue simple comportant des aspects configurables tels que la hauteur, la largeur, la taille du texte, etc. Nous avons un ImageView pour afficher les images et EditText comme nous l'avions mentionnรฉ, entre autres. Android dispose รฉgalement de ViewGroups personnalisรฉs auxquels nous pouvons ajouter nos vues et obtenir le comportement attendu.
Disposition linรฉaire
LinearLayout nous permet d'y ajouter des รฉlรฉments View. LinearLayout est un attribut d'orientation qui dicte la maniรจre dont il sera disposรฉ ร l'รฉcran. Il a รฉgalement LinearLayout.LayoutParams qui dicte les rรจgles pour les vues ร l'intรฉrieur, par exemple, l'attribut android:center_horizontal centrerait les vues le long de l'axe horizontal tandis que `android:center_vertical centrerait le contenu de la vue le long de l'axe vertical.
Voici quelques images pour comprendre le centrage. Nous considรฉrerions qu'il s'agit d'un simple TextView dans un espace de 200 px sur 200 px, les attributs de centrage le feraient se comporter comme suit.
Android : centre_horizontal

Android : centre_vertical

Android:centre

Composants principaux de RecyclerView

Voici les composants importants de RecyclerView :
RecyclerView.AdapterRecyclerView.Adapter
Base nยฐ1 โ Modรจle dโadaptateur
Un adaptateur est un pรฉriphรฉrique qui transforme les attributs d'un systรจme ou d'un pรฉriphรฉrique en ceux d'un pรฉriphรฉrique ou d'un systรจme autrement incompatible. Certains d'entre eux modifient les attributs du signal ou de la puissance, tandis que d'autres adaptent simplement la forme physique d'un connecteur ร un autre.
Un exemple simple que nous trouvons dans la vie rรฉelle pour expliquer un adaptateur est lorsque nous devons connecter des appareils ensemble, mais qu'ils ont des ports de connexion qui ne correspondent pas les uns aux autres. Cela peut รชtre le cas lorsque vous visitez un autre pays oรน ils utilisent diffรฉrents types de prises. Si vous transportez votre chargeur de tรฉlรฉphone ou dโordinateur portable, il serait impossible de le connecter aux prises de courant. Cependant, vous n'abandonnerez pas mais vous obtiendrez simplement un adaptateur qui s'interposerait entre la prise de courant et votre chargeur et permettrait la recharge.
C'est le cas en programmation lorsque nous voulons connecter deux structures de donnรฉes ensemble afin d'accomplir la tรขche, mais que leurs ports par dรฉfaut n'ont aucun moyen de communiquer entre eux.
Nous utiliserons l'exemple simple d'un appareil et d'un chargeur. Nous aurons deux instances de chargeurs. Un amรฉricain et un britannique
class AmericanCharger() {
var chargingPower = 10
}
class BritishCharger(){
var charginPower = 5
}
Nous allons ensuite crรฉer deux appareils
class AmericanDevice() class BritishDevice()
ร titre d'exemple, nous pouvons ensuite crรฉer des instances des appareils pour jouer.
var myAmericanPhone = new AmericanDevice() var myBritishPhone = new BritishDevice()
Nous prรฉsenterons ensuite le concept de recharge pour les deux appareils en ajoutant une mรฉthode dans les appareils appelรฉe charge() .
La mรฉthode prend en entrรฉe son chargeur respectif et effectue une charge en fonction de celui-ci.
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
}
}
Dans ce cas, sur la base de notre analogie, nous aurions, pour une raison ou une autre, besoin d'utiliser un BritishCharger lors de l'utilisation d'un appareil amรฉricain ou vice versa.
Dans le monde de la programmation, cela se produit gรฉnรฉralement lors du mรฉlange de bibliothรจques offrant les mรชmes fonctionnalitรฉs (dans notre contexte, notre fonctionnalitรฉ partagรฉe est payante). Nous aurions besoin de trouver un moyen de permettre cela.
Si nous suivons l'analogie, nous devrons nous rendre dans un magasin d'รฉlectronique et acheter un adaptateur qui nous permettra de recharger les appareils amรฉricains dotรฉs de chargeurs britanniques. Du point de vue de la programmation, c'est nous qui serons le fabricant de l'adaptateur.
Nous allons crรฉer un adaptateur pour l'un qui correspond au modรจle exact dont nous aurions besoin pour crรฉer l'autre. Nous allons l'implรฉmenter en tant que classe comme suit. Il ne doit pas nรฉcessairement s'agir d'une classe et peut รชtre une fonction qui met en รฉvidence ce que fait gรฉnรฉralement le modรจle d'adaptateur. Nous utiliserons une classe car elle correspond ร la plupart des utilisations sur 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 charingPower:Int = charger.chargingPower / 2
var newBritishCharger = new BritishCharger()
newBritishCharger.chargingPower = theAmericanCharger.chargingPower/2
return newBritishCharger
}
}
Dans le monde de la programmation, la diffรฉrence entre les prises est analogue ร la diffรฉrence entre les mรฉthodes utilisรฉes ร lโintรฉrieur pour le chargement. Les chargeurs ayant des mรฉthodes diffรฉrentes rendraient impossible leur utilisation.
var myBritishDevice = new BritishDevice() var americanChargerIFound = new AmericanCharger()
Essayer d'appeler la mรฉthode charge() dans myBritishDevice avec americanChargerIFound ne fonctionnerait pas car AmericanDevice n'accepte qu'un AmericanCharger
Donc c'est impossible de faire รงa
var myBritishDevice = new BritishDevice() var americanChargerIFound = new AmericanCharger() myBritishDevice.charge(americanChargerIFound)
Dans ce scรฉnario, l'adaptateur que nous avons crรฉรฉ
AmericanToBritishChargerAdapter peut dรฉsormais sโavรฉrer utile. Nous pouvons utiliser la mรฉthode returnNewCharger() pour crรฉer un nouveau BritishCharger, que nous pouvons utiliser pour facturer. Tout ce dont nous avons besoin est de crรฉer une instance de notre adaptateur et de l'alimenter avec l'AmericanCharger dont nous disposons, et cela crรฉera un BritishCharger que nous pourrons utiliser.
var myBritishDevice = new BritishDevice() var americanChargerIFound = new AmericanCharger() //We create the adapter and feed it the americanCharger var myAdapter = AmericanToBritishChargerAdapter(theAmericanCharger) //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
Lorsqu'il s'agit d'un ViewGroup, nous aurions des vues placรฉes ร l'intรฉrieur. C'est LayoutManager qui aurait pour tรขche de dรฉcrire la maniรจre dont les vues sont disposรฉes ร l'intรฉrieur.
ร des fins de comparaison, lorsque nous travaillons avec Linearlayout ViewGroup, le cas d'utilisation que nous souhaitons est la possibilitรฉ de placer les รฉlรฉments verticalement ou horizontalement. Ceci est facilement implรฉmentรฉ en ajoutant un attribut d'orientation, qui nous indique comment la disposition linรฉaire sera placรฉe sur l'รฉcran. Nous pouvons le faire en utilisant android:orientation=VERTICAL|HORIZONTAL attribuer.
Nous avons รฉgalement un autre ViewGroup appelรฉ GridLayout, son cas d'utilisation est lorsque nous souhaitons placer des vues dans une structure de grille rectangulaire. Cela pourrait รชtre dรป ร des raisons telles que rendre les donnรฉes que nous prรฉsentons ร l'utilisateur de l'application faciles ร consommer. De par sa conception, le GridLayout permet aux configurations de vous aider ร atteindre cet objectif en ayant des configurations oรน nous pouvons dรฉfinir les dimensions de la grille par exemple, nous pouvons avoir une grille 4ร4, une grille 3 x 2.
RecyclerView.ViewHolderRecyclerView.ViewHolder
Le ViewHolder est un ABStracLa classe `t`, que nous hรฉritons รฉgalement de `RecyclerView`, nous fournit des mรฉthodes communes permettant de rรฉfรฉrencer une vue placรฉe dans le `RecyclerView`, mรชme aprรจs que le mรฉcanisme de recyclage de ce dernier ait modifiรฉ diverses rรฉfรฉrences dont nous n'avons pas connaissance.
Grandes listes
Les RecyclerViews sont utilisรฉs lorsque nous souhaitons prรฉsenter un trรจs grand ensemble de vues ร l'utilisateur, sans รฉpuiser notre RAM sur notre appareil pour chaque instance de la vue crรฉรฉe.
Si nous devions prendre le cas d'une liste de contacts, nous aurions une idรฉe gรฉnรฉrale de l'apparence d'un contact dans la liste. Ce que nous ferions ensuite, c'est crรฉer une mise en page de modรจle โ qui est en fait une vue โ avec des emplacements oรน seront remplies diverses donnรฉes de notre liste de contacts. Voici un pseudo-code qui explique tout le but :
//OneContactView
<OneContact>
<TextView>{{PlaceHolderForName}}</TextView>
<TextView>{{PlaceHolderForAddress}}</TextView>
<ImageView>{{PlaceHolderForProfilePicture}}</ImageView>
<TextView>{{PlaceHolderForPhoneNumber}}</TextView>
</OneContact>
Nous aurions alors une ContactList de cette nature
<ContactList> </ContactList>
Si c'est le cas, nous codions en dur le contenu, nous n'aurions pas de moyen programmatique d'ajouter du nouveau contenu ร la liste sans rรฉรฉcrire l'application. Heureusement pour nous. L'ajout d'une vue ร un ViewGroup est pris en charge par un addView(view:View) mรฉthode.
Mรชme si tel est le cas, ce n'est pas ainsi que RecyclerView ajoute des vues enfants.
Dans notre cas d'utilisation, nous aurions une longue liste de contacts. Pour chaque contact de la liste, nous devrons crรฉer OneContactView et remplir les donnรฉes ร l'intรฉrieur de la vue pour qu'elles correspondent aux champs de notre classe Contact. Ensuite, une fois que nous aurons la vue, nous devrons l'ajouter ร RecyclerView pour afficher la liste.
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)
Nous avons un ensemble de contacts appelรฉ OneContactView. Il contient des emplacements pour rรฉcupรฉrer le contenu de la classe Contact et l'afficher. Dans RecyclerView, nous devons y ajouter des vues afin que cela puisse nous aider avec sa capacitรฉ de recyclage.
Le RecyclerView ne nous permet pas vraiment d'ajouter une vue mais nous permet d'ajouter un ViewHolder. Donc, dans ce scรฉnario, nous avons deux รฉlรฉments que nous voulons connecter mais qui ne correspondent pas. C'est lร qu'intervient notre adaptateur. RecyclerView nous fournit un adaptateur trรจs similaire ร notre AmericanToBritishChargerAdapter() d'avant, cela nous a permis de convertir notre AmericanCharger qui รฉtait inutilisable avec notre BritishDevice en quelque chose d'utilisable, semblable ร un adaptateur secteur dans la vraie vie.
Dans ce scรฉnario, l'adaptateur prendrait notre tableau de contacts et notre vue, et ร partir de lร gรฉnรฉrerait des ViewHolders que RecyclerView est prรชt ร accepter.
Le RecyclerView fournit une interface que nous pouvons รฉtendre pour crรฉer notre adaptateur via la classe RecyclerView.Adapter. ร lโintรฉrieur de cet adaptateur se trouve un moyen de crรฉer la classe ViewHolder avec laquelle RecyclerView souhaite travailler. Nous avons donc la mรชme situation quโavant, mais avec une chose en plus, cโest lโadaptateur.
Nous avons un tableau de contacts, une vue pour afficher un contact OneContactView. Un RecyclerView est une liste de vues qui fournissent des services de recyclage mais qui ne souhaitent accepter que des ViewHolders.
Mais dans ce scรฉnario, nous avons maintenant la classe RecyclerView.Adapter, qui contient une mรฉthode pour crรฉer des ViewHolders.
fun createViewHolder(@NonNull parent: ViewGroup, viewType: Int): ViewHolder
Le RecyclerView.ViewHolder est un absolutracclasse t qui prend notre vue comme argument et la convertit en ViewHolder.
Il utilise le modรจle wrapper utilisรฉ pour รฉtendre les capacitรฉs des classes.
Base #2 โ Modรจle dโemballage
Nous utiliserons un exemple simple pour dรฉmontrer comment faire parler les animaux.
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
Dans lโexemple ci-dessus, nous avons deux animaux. Si par hasard, nous voulions ajouter une mรฉthode pour faire parler, mais que l'auteur de la bibliothรจque n'รฉtait pas amusant, nous pourrions quand mรชme trouver un moyen. Ce dont nous avons besoin serait un wrapper pour notre classe Animal. Nous ferions cela en prenant Animal comme constructeur pour notre classe
class SpeechPoweredAnimalByWrapper(var myAnimal:Animal){
fun sound(){
myAnimal.sound()
}
speak(){
println("Hello, my name is ${myAnimal.name}")
}
}
Nous pouvons maintenant transmettre une instance animale au SpeechPoweredAnimalByWrapper. Appeler la mรฉthode sound() dessus appellerait la mรฉthode animal sound() transmise. Nous avons รฉgalement une mรฉthode speak() supplรฉmentaire, qui compte comme une nouvelle fonctionnalitรฉ que nous ajoutons aux animaux transmis. Nous pouvons l'utiliser comme suit :
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"
Grรขce ร ce modรจle, nous pouvons prendre des classes et y ajouter des fonctionnalitรฉs. Il nous suffit de leur transmettre une instance de classe et les nouvelles mรฉthodes dรฉfinies par notre wrapper.ping classe.
Dans l'exemple ci-dessus, nous avons utilisรฉ une classe concrรจte. Il est รฉgalement possible d'implรฉmenter la mรชme chose dans un Abs.tracclasse t. Il nous suffirait de modifier la classe SpeechPoweredAnimalByWrapper en abstracVoilร , c'est terminรฉ. Nous allons raccourcir le nom de la classe pour le rendre plus lisible.
abstract class SpeechPowered(var myAnimal:Animal){
fun sound(){
myAnimal.sound()
}
speak(){
println("Hello, my name is ${myAnimal.name}")
}
}
C'est la mรชme chose qu'avant, mais cela signifierait autre chose. Dans une classe normale, nous pouvons avoir une instance d'une classe de la mรชme maniรจre que nous avons crรฉรฉ cat1 et dog1. AbstracLes classes t, cependant, ne sont pas destinรฉes ร รชtre instanciรฉes mais ร รฉtendre d'autres classes. Alors, comment utiliserions-nous la nouvelle mรฉthode SpeechPowered(var myAnimal:Animal) ?tracLa classe t. Nous pouvons l'utiliser en crรฉant de nouvelles classes qui l'รฉtendront et qui, par consรฉquent, bรฉnรฉficieront de ses fonctionnalitรฉs.
Dans notre exemple, nous allons crรฉer une classe SpeechPoweredAnimal qui รฉtend la classe
class SpeechPoweredAnimal(var myAnimal:Animal):SpeechPowered(myAnimal)
var cat1 = Cat("Tubby")
var speakingKitty = SpeechPoweredAnimal(cat1)
speakingKitty.speak() //"Hello, my name is Tubby"
Il s'agit du mรชme modรจle utilisรฉ dans le ViewHolder. La classe RecyclerView.ViewHolder est une classe absolue.tracCette classe ajoute des fonctionnalitรฉs ร la vue, un peu comme nous avons ajoutรฉ la mรฉthode `speak` aux animaux. C'est cette fonctionnalitรฉ supplรฉmentaire qui permet son bon fonctionnement avec le RecyclerView.
Voici comment nous crรฉerions un OneContactViewHolder ร partir de 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 dispose d'un adaptateur qui nous permet de connecter notre tableau de contacts au ContactsView avec le RecyclerView
Ajout d'une vue
Le ViewGroup ne redessine pas automatiquement le ViewGroup mais suit un calendrier particulier. Il se peut que votre appareil se redessine toutes les 10 ms ou 100 ms, ou si nous choisissons un nombre absurde, par exemple 1 minute, lorsque nous ajoutons une vue ร un ViewGroup, vous verrez les changements 1 minute plus tard lorsque le ViewGroup ยซ s'actualise ยป.
RecyclerView.Recycler
Base #3. Mise en cache
Lโun des meilleurs exemples dโendroits oรน nous effectuons rรฉguliรจrement des actualisations se trouve dans le navigateur. Imaginons, par exemple, que le site que nous visitons soit statique et n'envoie pas de contenu de maniรจre dynamique, nous aurions besoin de continuer ร l'actualiser pour voir les changements.
Pour cet exemple, imaginons que le site en question soit Twitter. Nous aurions une sรฉrie de tweets statiques rรฉpertoriรฉs et la seule faรงon de voir les nouveaux tweets serait de cliquer sur le bouton d'actualisation pour rรฉcupรฉrer le contenu.
Repeindre tout lโรฉcran est รฉvidemment une chose coรปteuse. Si nous devions imaginer que c'รฉtait le cas, nous avions une bande passante limitรฉe avec notre opรฉrateur tรฉlรฉphonique. Et notre liste de tweets contenait beaucoup d'images et de vidรฉos, il serait coรปteux de retรฉlรฉcharger tout le contenu de la page ร chaque actualisation.
Nous aurions besoin d'un moyen de stocker les Tweets dรฉjร chargรฉs et de garantir que notre prochaine requรชte ait la capacitรฉ d'indiquer les Tweets qu'elle possรจde dรฉjร . Par consรฉquent, il ne tรฉlรฉcharge pas tout ร nouveau et rรฉcupรจre uniquement les nouveaux Tweets dont il dispose et vรฉrifie รฉgalement si certains Tweets enregistrรฉs localement ne sont plus lร afin de pouvoir les supprimer localement. Ce que nous dรฉcrivons s'appelle la mise en cache.
Les informations que nous envoyons au site Web sur les contenus dont nous disposons sont appelรฉes mรฉtadonnรฉes. Donc, dans le vrai sens, nous ne disons pas simplement ยซ nous voulons charger votre site ยป, nous disons ยซ nous voulons charger votre site, et voici une partie du contenu que nous avions dรฉjร enregistrรฉ lors du dernier chargement, veuillez l'utiliser pour envoyez-nous uniquement ce qui n'est pas lร , nous n'utilisons donc pas beaucoup de bande passante car nous n'avons pas beaucoup de ressources.
Appels de mise en page โ La liste de tweets doit รชtre folle
Un exemple d'appel de mise en page est scrollToPosition
Il s'agit d'un exemple courant prรฉsent dans des รฉlรฉments tels que les applications de chat. Si quelqu'un dans un fil de discussion rรฉpond ร une bulle de discussion prรฉcรฉdente, certaines applications de discussion incluent la rรฉponse et un lien vers la bulle de discussion, qui, en cliquant sur vous, vous dirige vers l'endroit oรน se trouvait votre message d'origine.
Dans le cas, nous appelons cette mรฉthode avant d'avoir un LayoutManager ajoutรฉ ร notre RecyclerView et avant d'avoir un RecyclerView.Adapter, le scrollToPosition(n:Int) est simplement ignorรฉ.
Communication entre les composants RecyclerView
Base #4. Rappels
Le RecyclerView, pour faire son travail, comporte de nombreuses piรจces mobiles. Il doit gรฉrer le LayoutManager qui nous indique comment organiser les vues, soit linรฉairement, soit dans une grille. Il doit faire face ร un adaptateur qui fait le travail de convertir nos รฉlรฉments contactList en Views OneContactView puis en ViewHolders OneContactViewHolder que le RecyclerView est prรชt ร travailler selon les mรฉthodes qu'il nous propose.
La matiรจre premiรจre du RecyclerView est nos vues, par exemple OneContactView et la source de donnรฉes.
contactList:Array<Contact>
Nous avons utilisรฉ un scรฉnario simple comme point de dรฉpart pour avoir une idรฉe de ce que RecyclerView tente d'accomplir.
Un cas de base dans lequel nous aurions un tableau statique de 1000 contacts que nous souhaitons montrer ร l'utilisateur est facile ร comprendre.
La machinerie RecyclerView commence vraiment ร prendre vie lorsque la liste n'est plus statique.
Avec une liste dynamique, nous devons penser ร ce qui arrive ร la vue ร l'รฉcran lorsque nous ajoutons un รฉlรฉment ร la liste ou supprimons un รฉlรฉment de la liste.
RecyclerView.LayoutManager
Outre le choix de la disposition des vues (linรฉaire ou en grille), le LayoutManager effectue de nombreuses opรฉrations en arriรจre-plan.ping Les recycleurs savent quand effectuer le recyclage.
Il est responsable de keeping tracLe gestionnaire de mise en page surveille les vues actuellement affichรฉes ร l'รฉcran et transmet cette information au mรฉcanisme de recyclage. Lorsqu'un utilisateur fait dรฉfiler la page vers le bas, le gestionnaire de mise en page informe le systรจme de recyclage des vues qui perdent le focus en haut de la page, afin qu'elles puissent รชtre rรฉutilisรฉes au lieu de rester affichรฉes et de consommer de la mรฉmoire, ou au lieu de les dรฉtruire et de devoir en crรฉer de nouvelles.
Cela signifie que le LayoutManager doit conserver tracLe systรจme dรฉtermine la position de l'utilisateur lorsqu'il fait dรฉfiler la liste. Pour ce faire, il utilise une liste de positions indexรฉes : le premier รฉlรฉment est indexรฉ ร 0 et l'index s'incrรฉmente pour correspondre au nombre d'รฉlรฉments dans la liste.
Si nous pouvons afficher 10 รฉlรฉments sur notre liste de 100, par exemple, au dรฉbut, le LayoutManager est conscient qu'il a le focus sur la vue 0 jusqu'ร la vue 9. Au fur et ร mesure que nous faisons dรฉfiler, le LayoutManager est capable de calculer les vues qui sortent. de concentration.
Le LayoutManager est capable de transmettre ces vues au mรฉcanisme de recyclage afin qu'elles puissent รชtre rรฉutilisรฉes (de nouvelles donnรฉes peuvent y รชtre liรฉes, par exemple les donnรฉes de contact d'une vue peuvent รชtre supprimรฉes et les nouvelles donnรฉes de contact du segment suivant peuvent remplacer les espaces rรฉservรฉs).
Ce serait un cas heureux si la liste que nous avons est statique, mais l'un des cas d'utilisation les plus courants d'utilisation d'un RecyclerView concerne les listes dynamiques oรน les donnรฉes peuvent provenir d'un point de terminaison en ligne ou mรชme peut-รชtre d'un capteur. Non seulement les donnรฉes sont ajoutรฉes, mais les donnรฉes de notre liste sont parfois supprimรฉes ou mises ร jour.
L'รฉtat dynamique de nos donnรฉes peut rendre trรจs difficile le raisonnement sur le LayoutManager. Pour cette raison, le LayoutManager gรจre sa propre liste d'รฉlรฉments et de positions, distincte de la liste utilisรฉe par le composant Recycling. Cela garantit qu'il effectue correctement son travail de mise en page.
Dans le mรชme temps, le LayoutManager de RecyclerView ne veut pas dรฉformer les donnรฉes dont il dispose. Afin de fonctionner correctement, le LayoutManager se synchronise avec le RecyclerView.Adapter ร intervalles donnรฉs (60 ms) en partageant des informations sur nos รฉlรฉments de liste. c'est-ร -dire les รฉlรฉments ajoutรฉs, mis ร jour, supprimรฉs, dรฉplacรฉs d'une position ร une autre). Le LayoutManager, dรจs rรฉception de ces informations, rรฉorganise le contenu ร l'รฉcran pour correspondre aux modifications lorsque cela est nรฉcessaire.
La plupart des opรฉrations de base liรฉes ร RecylerView tournent autour de la communication entre RecyclerView.LayoutManager et RecyclerView.Adapter qui stocke nos listes de donnรฉes parfois statiques ou parfois dynamiques.
De plus, RecyclerView nous prรฉsente des mรฉthodes que nous pouvons utiliser pour รฉcouter des รฉvรฉnements tels que onBindViewHolder lorsque notre RecyclerView.Adapter lie le contenu de notre liste, par exemple un contact ร un ViewHolder afin qu'il soit dรฉsormais utilisรฉ pour afficher les informations ร l'รฉcran.
Un autre est onCreateViewHolder, qui nous indique quand le RecyclerView. L'adaptateur prend une vue normale comme OneContactView et la convertit en un รฉlรฉment ViewHolder avec lequel le RecyclerView peut fonctionner. De notre ViewHolder pour une utilisation par RecyclerView. onViewDรฉtachรฉ
En dehors des mรฉcanismes de base qui permettent le recyclage. Le RecyclerView fournit des moyens de personnaliser le comportement sans affecter le recyclage.
La rรฉutilisation des vues rend difficile les tรขches courantes que nous avons l'habitude de faire avec des vues statiques, telles que la rรฉaction aux รฉvรฉnements onClick.
Comme nous sommes conscients que le RecyclerView.LayoutManager qui prรฉsente les vues ร l'utilisateur peut avoir pendant un moment une liste d'รฉlรฉments diffรฉrente de celle de la RecyclerView.Adapter qui contient la liste que nous avons stockรฉe dans une base de donnรฉes ou diffusรฉe ร partir d'une source. Placer des รฉvรฉnements OnClick directement sur les vues peut entraรฎner un comportement inattendu, tel que la suppression ou la modification du mauvais contact.
Gradle
Si nous voulons utiliser RecyclerView, nous devons l'ajouter en tant que dรฉpendance dans notre fichier de build .gradle.
Dans l'exemple suivant, nous avons utilisรฉ l'implรฉmentation ยซ androidx.recyclerview:recyclerview:1.1.0 ยป qui est la version la plus rรฉcente selon cet article.
Aprรจs avoir ajoutรฉ la dรฉpendance ร notre Gradle fichier, nous serons invitรฉs par Android Studio ร Synchroniser les changements,
C'est ainsi que notre Gradle ressemblera au fichier aprรจs lโajout dโun RecyclerView dans un projet vide avec uniquement les valeurs par dรฉfaut.
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"
}
Nous n'avons qu'un seul fichier de mise en page pour le moment. Nous commencerons par un exemple simple dans lequel nous utiliserons un RecyclerView pour afficher une liste de noms de fruits ร l'รฉcran.
Liste d'objets
Nous allons accรฉder ร notre fichier MainActivity et crรฉer un tableau avec les noms de fruits ร l'intรฉrieur juste avant la mรฉthode onCreate() qui a รฉtรฉ gรฉnรฉrรฉe lors de la configuration.
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)
}
}
Notre prochain objectif sera de prรฉsenter cette liste ร l'รฉcran ร l'aide d'un RecyclerView.
Pour ce faire, nous allons naviguer vers le rรฉpertoire de mise en page qui contient nos mises en page et crรฉer une vue qui sera chargรฉe d'afficher un fruit.
Disposition ร utiliser pour chaque รฉlรฉment de notre liste
<?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>
Dans le TextView ci-dessus, nous avons ajoutรฉ un champ d'identification qui sera utilisรฉ pour identifier une vue.
Il n'est pas gรฉnรฉrรฉ par dรฉfaut. Nous avons dans notre TextView l'identifiant fruitName pour correspondre aux donnรฉes, qui y seront liรฉes.
Ajout de RecyclerView ร la mise en page principale
Dans la mรชme activitรฉ, il y a le fichier de mise en page main_layout.xml qui a รฉtรฉ gรฉnรฉrรฉ pour nous par dรฉfaut.
Si nous choisissons un projet vide. Cela aura gรฉnรฉrรฉ un XML contenant un ConstraintLayout et ร lโintรฉrieur se trouvera un TextView avec le texte ยซ Bonjour ยป.
Nous supprimerons tout le contenu et ferons en sorte que la mise en page contienne uniquement le RecyclerView comme ci-dessous :
<?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" />
Nous avons รฉgalement ajoutรฉ un attribut id pour RecyclerView que nous utiliserons pour le rรฉfรฉrencer dans notre code.
android:id="@+id/fruitRecyclerView"
Nous reviendrons ensuite ร notre fichier MainActivity. En utilisant les identifiants que nous avons crรฉรฉs, nous pourrons rรฉfรฉrencer les vues que nous venons de crรฉer.
Nous commencerons par rรฉfรฉrencer le RecyclerView ร lโaide de la mรฉthode findViewById() fournie par Android. Nous ferons cela dans notre mรฉthode onCreate().
Notre mรฉthode onCreate() ressemblera ร ceci.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var myFruitRecyclerView: RecyclerView = findViewById(R.id.fruitRecyclerView)
}
Crรฉer un ViewHolder
Ensuite, nous allons crรฉer un RecyclerView.ViewHolder qui est chargรฉ de prendre notre vue et de la convertir en ViewHolder, que le RecyclerView utilise pour afficher nos รฉlรฉments.
Nous le ferons juste aprรจs notre mรฉthode amusante onCreate().
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)
}
Crรฉer un RecyclerViewAdapter
Ensuite, nous allons crรฉer une classe FruitArrayAdapter qui รฉtend la classe RecyclerView.Adapter.
Le FruitArrayAdapter que nous crรฉons sera chargรฉ d'effectuer les tรขches suivantes.
Il prendra les noms de fruits de la gamme de fruits. Il crรฉera un ViewHolder en utilisant notre vue one_fruit_view.xml. Il liera ensuite le fruit ร un ViewHolder et liera dynamiquement le contenu ร la vue que nous avons crรฉรฉe one_fruit_view.xml.
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 ajoutera des soulignements rouges sur notre FruitArrayAdapter, nous indiquant que nous devons implรฉmenter une mรฉthode que le RecyclerView peut utiliser pour connecter notre tableau ร un ViewHolder que le RecyclerView peut utiliser.
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) {
}
}
Nous commencerons par le bit le plus simple du code gรฉnรฉrรฉ : la mรฉthode getItemCount(). Nous savons comment obtenir le nombre d'รฉlรฉments de notre tableau en appelant la mรฉthode array.length.
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) {
}
}
Ensuite, nous implรฉmenterons la mรฉthode override fun onCreateViewHolder.
C'est lร que RecyclerView nous demande de l'aider ร construire un FruitHolder pour lui.
Si nous nous rappelons, voici ร quoi ressemblait notre classe FruitViewHolder :
class FruitViewHolder(fruitView: View): RecyclerView.ViewHolder(fruitView)
Cela nรฉcessite notre fruitView que nous avons crรฉรฉ sous forme de fichier XML one_fruit_view.xml
Nous pouvons crรฉer une rรฉfรฉrence ร ce XML et la convertir en vue comme suit.
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) {
}
}
Le bit restant est le remplacement
fun onBindViewHolder(holder: FruitViewHolder, position: Int)
Le RecyclerView.Adapter demande avec un entier de position, que nous utiliserons pour rรฉcupรฉrer un รฉlรฉment de notre liste. Il nous fournit รฉgalement un support afin que nous puissions lier l'รฉlรฉment que nous obtenons du fruitArray ร la vue conservรฉe ร l'intรฉrieur du support de vue.
La vue conservรฉe ร l'intรฉrieur d'un ViewHoder est accessible via le champ ViewHolder.itemView. Une fois que nous obtenons la vue, nous pouvons utiliser l'identifiant fruitName que nous avons crรฉรฉ prรฉcรฉdemment pour dรฉfinir le contenu.
override fun onBindViewHolder(holder: FruitViewHolder, position: Int) {
var ourFruitTextView = holder.itemView.findViewById<TextView>(R.id.fruitName)
var aFruitName = fruitArray.get(position)
ourFruitTextView.setText(aFruitName)
}
Avec cela, notre FruitArrayAdapter est complet et se prรฉsente comme suit.
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)
}
}
Enfin, nous sommes prรชts ร cรขbler les รฉlรฉments restants de notre RecyclerView. Qui crรฉent un LayoutManager, qui indiquera au RecyclerView comment afficher le contenu de la liste. Que ce soit pour afficher de maniรจre linรฉaire ร l'aide de LinearLayoutManager ou dans une grille ร l'aide de GridLayoutManager ou StaggeredGridLayoutManager.
Crรฉer un gestionnaire de mise en page
Nous allons revenir dans notre fonction onCreate et ajouter le 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
}
Accrochez notre adaptateur aux รฉlรฉments et configurez-le sur 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
}
Nous avons รฉgalement crรฉรฉ une instance fruitListAdapter et lui avons fourni le tableau de noms de fruits.
Et en gros, nous avons tous terminรฉ.
Le fichier MainActivity.kt complet se prรฉsente comme suit.
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)
}
}
}
