Android RecyclerView: mis on, õppige lihtsate näidetega
⚡ Nutikas kokkuvõte
RecyclerView on täiustatud Android Vidin, mis kuvab suuri ja keritavaid andmekogumeid tõhusalt piiratud arvu vaateid taaskasutades. See tugineb adapterile, küljendushaldurile ja vaatehoidjale, et siduda käitusajal muutuvad andmekogumid.

Milles on RecyclerView Android?
. RecyclerView on GridView ja ListView paindlikum ja täiustatud versioon. See on konteiner suurte andmekogumite kuvamiseks, mida saab tõhusalt kerida piiratud arvu vaadete abil. RecyclerView vidinat saate kasutada andmekogumite puhul, mille elemendid muutuvad käitusajal olenevalt võrgusündmusest või kasutaja toimingust.
views
. Android platvorm kasutab ekraanile üksuste joonistamiseks klasse View ja ViewGroup. Need klassid on absoluutselttract ja neid laiendatakse erinevatele rakendustele vastavalt kasutusjuhule. Näiteks TextView'l on lihtne eesmärk kuvada ekraanil tekstisisu. EditText laieneb samast View klassist ja lisab rohkem funktsionaalsust, mis võimaldab kasutajal andmeid sisestada.
Arendusel suurema paindlikkuse saavutamiseks on võimalik luua oma kohandatud vaateid.ping kasutajaliidesed. Klass View pakub meetodeid, mida saame ekraanile joonistamiseks ümber kirjutada, ning vahendeid parameetrite (nt laius, kõrgus ja meie enda kohandatud atribuudid) edastamiseks, mida soovime oma vaatele lisada, et see käituks vastavalt oma soovidele.
ViewGroups
Klass ViewGroup on omamoodi vaade (View), aga erinevalt lihtsast vaateklassist (View), mille ülesanne on lihtsalt kuvada, annab ViewGroup meile võimaluse koondada mitu vaadet ühte vaatesse, millele saame tervikuna viidata. Sellisel juhul nimetatakse ülemisel tasemel loodud vaadet, millele lisame teisi lihtsaid vaateid, „eelvaateks“ ja selle sisse lisatud vaateid „tütarvaateks“.
Me võime ette kujutada vaadet (View) massiivina ja vaategruppi (ViewGroup) massiivide massiivina. Arvestades, et massiivide massiiv on ise massiiv, näeme, kuidas vaategruppi (ViewGroup) saab käsitleda nagu vaadet (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
ViewGroup võimaldab meil määratleda ka seda, kuidas alamelemendid vaates on korraldatud, näiteks kas need on paigutatud vertikaalselt või horisontaalselt. Vaate sees suhtlemiseks võivad olla erinevad reeglid. Näiteks peaks üksteisele järgnevate TextView-de vahekaugus olema 12 dp, samas kui ImageView-i ja sellele järgneva TextView-i vahekaugus peaks olema 5 dp.
See oleks nii, kui me oleksime arendanudping meie enda ViewGroupi nullist. Nende konfiguratsioonide lihtsustamiseks Android pakub klassi nimega LayoutParams, mida saame kasutada nende konfiguratsioonide sisestamiseks.
. Android dokumentatsioon pakub mõningaid vaikeparameetreid, mida me oma ViewGroupi konfigureerimisel rakendaksime. Mõned levinumad parameetrid on seotud laiuse, kõrguse ja veerisega. Vaikimisi on nende konfiguratsioonide struktuur android:layout_height kõrguse jaoks ja android:layout_width laiuse jaoks. Sellega seoses saate ViewGroupi loomisel luua ka LayoutParams'e, mis on spetsiifilised teie soovitud ViewGroupi käitumise järgi.
Android on varustatud vaikevaadete ja vaategruppidega, mida saame kasutada paljude vajalike toimingute tegemiseks. Üks näide, mida oleme maininud, on tekstivaade (TextView). See on lihtne vaade, millel on konfigureeritavad aspektid, nagu kõrgus, laius ja teksti suurus. Nagu mainisime, on meil ka piltide kuvamiseks pildivaade (ImageView) ja EditText, lisaks paljudele teistele. Android sisaldab ka kohandatud vaaterühmi, kuhu saame oma vaated lisada ja soovitud käitumist saavutada.
Lineaarne paigutus
LinearLayout võimaldab meil lisada vaateelemente (View). LinearLayoutil on orientatsiooni atribuut, mis määrab, kuidas see ekraanil paigutust määratakse. Sellel on ka LinearLayout.LayoutParams, mis määrab vaadete reeglid. Näiteks atribuut android:center_horizontal tsentreeriks vaated horisontaalteljele, samas kui android:center_vertical tsentreeriks vaate sisu vertikaalteljele.
Siin on mõned pildid, mis aitavad tsentreerimist mõista. Meie arvates on see lihtne TextView 200x200 pikslises ruumis; tsentreerimise atribuudid paneksid selle käituma järgmiselt.
android: center_horizontal
Horisontaalselt tsentreeritud sisu
android: center_vertical
Vertikaalselt tsentreeritud sisu
android:center
Tsentreeritud sisu
RecyclerView põhikomponendid
RecyclerView põhikomponendid
Järgmised on RecyclerView olulised komponendid.
RecyclerView.Adapter
Alustöö nr 1 – adapteri muster
Adapter on seade, mis muudab ühe süsteemi või seadme atribuudid muidu ühildumatu seadme või süsteemi atribuutideks. Mõned adapterid muudavad signaali või toite atribuute, teised aga lihtsalt kohandavad ühe pistiku füüsilist vormi teisega.
Lihtne näide adapterist reaalsest elust on olukord, kus peame seadmeid omavahel ühendama, kuid nende ühenduspesad ei sobi kokku. See võib juhtuda siis, kui külastate teist riiki, kus kasutatakse erinevat tüüpi pistikupesasid. Kui kannate kaasas telefoni või sülearvuti laadijat, on seda võimatu pistikupesadega ühendada. Siiski ei tohiks te alla anda, vaid ostate lihtsalt adapteri, mis asetatakse pistikupesa ja laadija vahele ning võimaldab laadimist.
See on programmeerimisel nii, kui tahame ülesande täitmiseks ühendada kaks andmestruktuuri, kuid nende vaikeportidel puudub võimalus omavahel suhelda.
Kasutame lihtsat näidet seadmest ja laadijast. Meil on kaks laadijat, Ameerika ja Briti oma.
class AmericanCharger() { var chargingPower = 10 } class BritishCharger(){ var chargingPower = 5 }
Seejärel loome kaks seadet.
class AmericanDevice() class BritishDevice()
Näitena saame seejärel luua mõned seadmete eksemplarid kaasamängimiseks.
var myAmericanPhone = new AmericanDevice() var myBritishPhone = new BritishDevice()
Seejärel tutvustame mõlema seadme laadimise kontseptsiooni, lisades seadmetesse meetodi nimega charge(). Meetod võtab oma vastava laadija ja teostab laadimise selle põhjal.
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 } }
Sellisel juhul, meie analoogia põhjal, oleks meil ühel või teisel põhjusel vaja kasutada BritishChargerit, kui me kasutame AmericanDevice'i, või vastupidi.
Programmeerimismaailmas on see tavaliselt sama funktsiooni pakkuvate teekide segamisel (meie kontekstis on meie jagatud funktsionaalsus tasuline). Peaksime leidma viisi selle võimaldamiseks.
Analoogia järgides peame minema elektroonikapoodi ja ostma adapteri, mis võimaldab meil laadida Ameerika seadmeid, arvestades Briti laadijaid. Programmeerimise seisukohast oleme meie adapteri tootjad.
Me loome adapteri, mis vastab täpselt mustrile, mida vajaksime teise loomiseks. Me rakendame selle klassina järgmiselt. See ei pea tingimata olema klass ja see võib olla funktsioon, mis toob esile, mida adapteri muster üldiselt teeb. Me kasutame klassi, kuna see sobib enamiku kasutustega 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 } }
Programmeerimismaailmas on pistikupesade erinevus analoogne laadimismeetodite erinevusega. Erinevate meetoditega laadijad muudaksid laadijate kasutamise võimatuks.
var myBritishDevice = new BritishDevice() var americanChargerIFound = new AmericanCharger()
charge() meetodi kutsumine myBritishDevice'is americanChargerIFound'iga ei toimi, kuna AmericanDevice aktsepteerib ainult AmericanCharger'i. Seega on seda võimatu teha:
var myBritishDevice = new BritishDevice() var americanChargerIFound = new AmericanCharger() myBritishDevice.charge(americanChargerIFound)
Selles stsenaariumis võib meie loodud adapter AmericanToBritishChargerAdapter nüüd kasuks tulla. Meetodi returnNewCharger() abil saame luua uue BritishChargeri, mida saame laadimiseks kasutada. Peame vaid looma oma adapteri eksemplari ja sisestama sinna olemasoleva AmericanChargeri, mis loob BritishChargeri, mida saame kasutada.
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
Vaategrupiga (ViewGroup) tegeledes paigutatakse selle sisse vaated (Views). LayoutManageri (LayoutManager) ülesanne on kirjeldada, kuidas vaated (Views) grupeeringu sees paiknevad.
Võrdluseks, LinearLayout ViewGroupiga töötades on meie soovitud kasutusjuhtum võimalus paigutada elemente kas vertikaalselt või horisontaalselt. Seda saab hõlpsalt rakendada orientation atribuudi lisamisega, mis näitab, kuidas LinearLayout ekraanile paigutatakse. Saame seda teha, kasutades android:orientation=VERTICAL|HORIZONTAL atribuut.
Meil on ka teine ViewGroup nimega GridLayout. Selle kasutusjuhtum on siis, kui soovime vaateid paigutada ristkülikukujulise ruudustiku struktuuri. See võib olla tingitud näiteks sellest, et rakenduse kasutajale esitatavaid andmeid oleks lihtne tarbida. GridLayout on loodud konfiguratsioonide abil, mis aitavad teil seda eesmärki saavutada, määratledes ruudustiku mõõtmed; näiteks võib meil olla 4×4 või 3×2 ruudustik.
RecyclerView.ViewHolder
ViewHolder on kõhulihastract-klass, mida me samuti RecyclerView'st laiendame. ViewHolder pakub meile üldmeetodeid, mis aitavad meil viidata RecyclerView'sse paigutatud vaatele (View), isegi pärast seda, kui RecyclerView'i taaskasutusmehhanism on muutnud mitmesuguseid viiteid, millest me ei tea.
Suured nimekirjad
RecyclerView'sid kasutatakse siis, kui tahame kasutajale esitada tõeliselt suure hulga vaateid, ilma et see ammendaks RAM meie seadmes iga loodud vaate eksemplari jaoks.
Kui võtame näiteks kontaktide loendi, siis meil oleks üldine ettekujutus sellest, kuidas üks kontakt loendis välja näeks. Seejärel looksime mallipaigutuse, mis on tegelikult vaade (View), kus on pesad, kuhu täidetakse meie kontaktide loendi erinevad andmed. Järgnev pseudokood selgitab kogu eesmärki:
//OneContactView
<OneContact>
<TextView>{{PlaceHolderForName}}</TextView>
<TextView>{{PlaceHolderForAddress}}</TextView>
<ImageView>{{PlaceHolderForProfilePicture}}</ImageView>
<TextView>{{PlaceHolderForPhoneNumber}}</TextView>
</OneContact>
Siis oleks meil selline kontaktide loend:
<ContactList> </ContactList>
Kui me sisu kõvakodeeriksime, poleks meil programmilist võimalust uut sisu loendisse lisada ilma rakendust ümber kirjutamata. Õnneks toetab View'i lisamist ViewGroupile funktsioon addView(view:View) meetod. Sellegipoolest ei lisa RecyclerView alamvaateid sel viisil.
Meie kasutusjuhul oleks meil pikk kontaktide loend. Iga loendis oleva kontakti jaoks peaksime looma OneContactView'i ja täitma vaates olevad andmed, et need vastaksid meie kontaktiklassi väljadele. Seejärel, kui vaade on loodud, peaksime selle loendi kuvamiseks lisama RecyclerView'i.
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)
Meil on kontaktide massiiv. OneContactView sisaldab pesasid, kuhu saab Contact klassi sisu võtta ja seda kuvada. RecyclerView's peame sinna lisama View'd, et see saaks meid taaskasutusvõimalustega aidata.
RecyclerView ei võimalda meil vaadet lisada, kuid võimaldab meil lisada ViewHolderi. Seega on meil selles stsenaariumis kaks osa, mida me tahame ühendada, kuid mis ei sobi kokku. Siin tulebki mängu adapter. RecyclerView pakub meile adapterit, sarnaselt meie AmericanToBritishChargerAdapter() varasemast, mis võimaldas meil muuta oma AmericanChargeri, mis oli meie BritishDevice'iga kasutuskõlbmatu, millekski kasutatavaks, mis sarnanes päriselus toiteadapteriga.
Selle stsenaariumi korral võtaks adapter meie kontaktide massiivi ja meie vaate ning genereeriks sealt vaatehoidjad, mida RecyclerView on nõus aktsepteerima.
RecyclerView pakub liidest, mida saame laiendada oma adapteri loomiseks RecyclerView.Adapter klassi kaudu. Selle adapteri sees on viis ViewHolder klassi loomiseks, millega RecyclerView soovib töötada. Seega on meil sama olukord mis varem, aga ühe lisaasjaga – adapteriga.
Meil on kontaktide massiiv, vaade ühe kontakti kuvamiseks (OneContactView) ja RecyclerView, mis on vaadete loend, mis pakub taaskasutusteenuseid, kuid on nõus vastu võtma ainult ViewHoldersi. Selles stsenaariumis on meil nüüd klass RecyclerView.Adapter, millel on meetod ViewHoldersi loomiseks.
fun createViewHolder(@NonNull parent: ViewGroup, viewType: Int): ViewHolder
RecyclerView.ViewHolder on kõhulihas.tract-klass, mis võtab argumendina meie vaate (View) ja teisendab selle vaatehoidjaks (ViewHolder). See kasutab ümbrismustrit, mida kasutatakse klasside võimaluste laiendamiseks.
Alustöö nr 2 – ümbrismuster
Toome lihtsa näite, et demonstreerida, kuidas panna loomad rääkima.
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
Ülaltoodud näites on meil kaks looma. Kui peaksime juhuslikult tahtma lisada meetodi, mis paneks nad rääkima, aga teegi autor poleks meeldiv, võiksime ikkagi leida lahenduse. Meil oleks vaja oma Looma-klassile wrapperit. Selleks võtaksime Looma oma klassi konstruktoriks.
class SpeechPoweredAnimalByWrapper(var myAnimal:Animal){ fun sound(){ myAnimal.sound() } fun speak(){ println("Hello, my name is ${myAnimal.name}") } }
Nüüd saame edastada looma eksemplari SpeechPoweredAnimalByWrapperile. Selle jaoks meetodi sound() kutsumine kutsuks välja edastatud looma sound() meetodi. Meil on ka täiendav meetod speak(), mis loetakse uueks funktsionaalsuseks, mille lisame edastatud loomadele. Saame seda kasutada järgmiselt:
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"
Selle mustri abil saame võtta klasse ja lisada funktsionaalsust. Me vajame vaid klassi eksemplari ja meie wrapi poolt defineeritud uusi meetodeid.ping klass.
Ülaltoodud juhul kasutasime betooniklassi. Sama on võimalik rakendada ka abs-süsteemis.tract-klass. Peaksime SpeechPoweredAnimalByWrapper klassi muutma abs-klassiks.tract ja olemegi valmis. Muudame klassi nime lühemaks, et see oleks loetavam.
abstract class SpeechPowered(var myAnimal:Animal){ fun sound(){ myAnimal.sound() } fun speak(){ println("Hello, my name is ${myAnimal.name}") } }
See on sama mis enne, aga see tähendaks midagi muud. Tavalises klassis saab meil olla klassi eksemplar samamoodi nagu me lõime cat1 ja dog1. Abstract-klassid ei ole aga mõeldud eksemplaride loomiseks, vaid teiste klasside laiendamiseks. Kuidas me siis kasutaksime uut SpeechPowered(var myAnimal:Animal) abs funktsiooni?tract-klass? Me saame seda kasutada uute klasside loomisel, mis seda laiendavad ja omakorda selle funktsionaalsust suurendavad.
Meie näites loome klassi SpeechPoweredAnimal, mis laiendab klassi.
class SpeechPoweredAnimal(var myAnimal:Animal):SpeechPowered(myAnimal)
var cat1 = Cat("Tubby") var speakingKitty = SpeechPoweredAnimal(cat1) speakingKitty.speak() //"Hello, my name is Tubby"
See on sama muster, mida kasutatakse ViewHolderis. Klass RecyclerView.ViewHolder on abs.tract-klass, mis lisab vaatele funktsionaalsust, sarnaselt sellele, kuidas me lisasime loomadele meetodi „speak“. Lisatud funktsionaalsus paneb selle RecyclerView'ga tegelemisel toimima.
Nii looksime OneContactViewHolderi OneContactView'st:
//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'l on adapter, mis võimaldab meil RecyclerView abil oma kontaktide massiivi ContactsView'ga ühendada.
Vaate lisamine
Vaategrupp ei joonista uuesti automaatselt, vaid järgib kindlat ajakava. Teie seadmes võib juhtuda, et see joonistab uuesti iga 10 ms või 100 ms järel; või kui valime absurdse numbri, näiteks 1 minuti, siis kui lisame vaate vaategruppi, näete muudatusi 1 minut hiljem, kui vaategrupp "värskendub".
RecyclerView.Recycler
Eeltöö nr 3 – vahemällu salvestamine
Üks parimaid näiteid regulaarsest värskendamisest on brauser. Kujutagem näiteks ette, et külastatav sait on staatiline ega saada sisu dünaamiliselt; muudatuste nägemiseks peaksime lehte pidevalt värskendama.
Kujutagem selle näite jaoks ette, et kõnealune sait on Twitter. Meil oleks loetletud rida staatilisi säutse ja uusi säutse näeksime ainult siis, kui klõpsaksime sisu uuesti laadimiseks värskendamisnupul.
Kogu ekraani uuesti värvimine on ilmselgelt kulukas ettevõtmine. Kujutage ette, et meie telefonipakkuja ribalaius on piiratud ja meie säutsude nimekirjas on palju pilte ja videoid; kogu lehe sisu iga värskendamise korral uuesti allalaadimine oleks kulukas.
Vajame viisi juba laaditud säutsude salvestamiseks ja tagamaks, et meie järgmisel päringul on võimalik öelda, millised säutsud tal juba on. Seega ei laadita kõike uuesti alla, vaid hangitakse ainult uued säutsud ning kontrollitakse ka seda, kas mõnda lokaalselt salvestatud säutsu enam pole, et saaks selle lokaalselt kustutada. Seda, mida me kirjeldame, nimetatakse vahemällu salvestamiseks.
Veebisaidile meie sisu kohta saadetavat teavet nimetatakse metaandmeteks. Seega tegelikult ei ütle me lihtsalt „me tahame teie saiti laadida“, vaid ütleme „me tahame teie saiti laadida ja siin on osa sisust, mille me juba viimasest laadimisest salvestasime; palun kasutage seda ainult selle saatmiseks, mida seal pole, et me ei kasutaks palju ribalaiust“.
Paigutuskõned – säutsuloend peab olema hull
Üks näide paigutuskäsust on scrollToPosition. See on levinud näide näiteks vestlusrakendustes. Kui keegi vestluses osaleja vastab varasemale vestlusmullile, lisavad mõned vestlusrakendused vastuse ja lingi vestlusmullile, millel klõpsamisel suunatakse teid algse sõnumi asukohta.
Juhul kui me kutsume seda meetodit välja enne, kui meil on RecyclerView'le lisatud LayoutManager ja enne, kui meil on RecyclerView.Adapter, siis scrollToPosition(n:Int) lihtsalt ignoreeritakse.
RecyclerView komponentide vaheline suhtlus
Eeltöö nr 4 – Tagasihelistamised
Oma töös on RecyclerView'l palju liikuvaid osi. See peab tegelema LayoutManageriga, mis ütleb meile, kuidas vaateid (View) lineaarselt või ruudustikuna korraldada. See peab tegelema adapteriga, mis teisendab meie elemendid (contactList) vaadeteks (OneContactView) ja seejärel vaatehoidjateks (OneContactViewHolder), millega RecyclerView on nõus töötama, kasutades selle pakutavaid meetodeid.
RecyclerView toormaterjaliks on meie vaated, nt OneContactView, ja andmeallikas.
contactList:Array<Contact>
Oleme lähtepunktina kasutanud lihtsat stsenaariumi, et saada aimu, mida RecyclerView püüab saavutada. Baasjuhtum, kus meil on staatiline 1000 kontakti massiiv, mida tahame kasutajale näidata, on kergesti mõistetav. RecyclerView masinad hakkavad tõeliselt ellu minema siis, kui loend pole enam staatiline. Dünaamilise loendi puhul peame mõtlema, mis juhtub ekraanil oleva vaatega, kui lisame loendisse üksuse või eemaldame loendist üksuse.
RecyclerView.LayoutManager
Lisaks sellele, et otsustame, kuidas meie vaated on paigutatud – kas lineaarselt või ruudustikuna –, teeb LayoutManager palju tööd kapoti all.ping Taaskasutusspetsialist teab, millal taaskasutada.
See vastutab kee eestping track hetkel ekraanil nähtavatest vaadetest ja edastab selle teabe taaskasutusmehhanismile. Kui kasutaja kerib allapoole, teavitab LayoutManager taaskasutussüsteemi ülaosas fookusest väljas olevatest vaadetest, et neid saaks uuesti kasutada, selle asemel, et need sinna jääksid ja mälu tarbiksid või hävitataks ainult uute loomiseks.
See tähendab, et LayoutManager peab säilitama track, kus kasutaja meie nimekirja kerimisel asub. See saavutatakse indeksil põhinevate positsioonide loendi abil, st esimene element algab 0-st ja suureneb vastavalt meie nimekirjas olevate elementide arvule.
Kui meie loendis, mis koosneb näiteks 100 üksusest, on 10 üksust, siis alguses teab LayoutManager, et fookuses on vaade 0 kuni vaade 9. Kerimise käigus saab LayoutManager arvutada, millised vaated fookusest välja lähevad.
LayoutManager saab need vaated taaskasutusmehhanismi vabastada, et neid saaks taaskasutada (nendega saab siduda uusi andmeid; nt vaate kontaktandmed saab eemaldada ja järgmise segmendi uued kontaktandmed saavad asendada kohatäited).
See oleks hea juhtum, kui meie loend on staatiline, kuid üks levinumaid RecyclerView kasutusjuhtumeid on dünaamilised loendid, kus andmed võivad pärineda võrguühendusega lõpp-punktist või isegi andurilt. Lisaks andmete lisamisele ja mõnikord ka loendis olevate andmete eemaldamisele või värskendamisele.
Meie andmete dünaamiline olek võib muuta paigutusehalduri kohta arutlemise väga keeruliseks. Sel põhjusel haldab paigutusehaldur oma üksuste ja positsioonide loendit, mis on eraldi ringlussevõtu komponendi kasutatavast loendist. See tagab paigutuse korrektse teostamise.
Samal ajal ei taha RecyclerView'i LayoutManager oma andmeid valesti esitada. Korrektseks tööks sünkroonib LayoutManager end RecyclerView.Adapteriga etteantud intervallide (60 ms) järel, jagades teavet meie loendiüksuste kohta, st lisatud, värskendatud, eemaldatud või ühest positsioonist teise liigutatud üksuste kohta. Pärast selle teabe saamist korraldab LayoutManager ekraanil oleva sisu ümber, et see vajadusel muudatustega vastaks.
Paljud RecyclerView'ga seotud põhitoimingud keerlevad RecyclerView.LayoutManageri ja RecyclerView.Adapteri vahelise suhtluse ümber, mis salvestab meie vahel staatiliste või vahel dünaamiliste andmete loendeid.
Lisaks pakub RecyclerView meile meetodeid sündmuste pealtkuulamiseks, näiteks onBindViewHolder, kui meie RecyclerView.Adapter seob meie loendi sisu (nt kontakti) ViewHolderiga, et seda kasutataks teabe kuvamiseks ekraanil.
Teine on onCreateViewHolder, mis annab meile teada, millal RecyclerView.Adapter võtab tavalise vaate (nt OneContactView) ja teisendab selle ViewHolderi elemendiks, millega RecyclerView saab töötada.
Lisaks põhimehhanismidele, mis võimaldavad taaskasutamist, pakub RecyclerView võimalusi käitumise kohandamiseks ilma taaskasutamist mõjutamata. Vaadete taaskasutamine raskendab tavaliste toimingute tegemist, mida oleme staatiliste vaadetega harjunud tegema, näiteks onClick-sündmustele reageerimist.
Nagu me teame, siis RecyclerView.LayoutManager mis esitleb kasutajale vaateid, võib hetkeks olla üksuste loendist erinev RecyclerView.Adapter , mille loend on andmebaasis talletatud või voogesitatakse allikast. OnClick-sündmuste otse vaadetele paigutamine võib põhjustada ootamatuid probleeme, näiteks vale kontakti kustutamise.
Gradle
Kui tahame RecyclerView'd kasutada, peame selle lisama sõltuvusena oma build.gradle faili. Järgmises näites oleme kasutanud implementatsiooni „androidx.recyclerview:recyclerview:1.1.0”, mis on selle artikli kohaselt uusim versioon.
Pärast sõltuvuse lisamist meie Gradle faili, küsib meilt Android Studio muudatuste sünkroonimiseks. Nii toimib meie Gradle fail hoolitseb pärast RecyclerView lisamist tühja projekti ainult vaikesätetega.
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" }
Meil on hetkel ainult üks küljendusfail. Alustame lihtsa näitega, kus kasutame RecyclerView'd puuviljanimede loendi kuvamiseks ekraanil.
Üksuste loend
Navigeerime oma MainActivity faili ja loome puuviljade nimedega massiivi, vahetult enne seadistamise ajal genereeritud onCreate() meetodit.
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) } }
Meie järgmine eesmärk on see loend ekraanil kuvada, kasutades RecyclerView'd. Selleks navigeerime paigutuste kataloogi, mis sisaldab meie paigutusi, ja loome vaate, mis vastutab ühe puuvilja kuvamise eest.
Paigutus, mida kasutatakse meie loendi iga üksuse jaoks
<?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>
Ülaltoodud TextView'sse lisasime ID-välja, mida kasutatakse vaate tuvastamiseks. Vaikimisi seda ei genereerita. Oleme andnud oma TextView'le ID fruitName, et see sobiks andmetega, mis sellega seotakse.
RecyclerView lisamine põhipaigutusele
Samas tegevuses on olemas ka main_layout.xml küljendusfail, mis genereeriti meile vaikimisi. Kui valisime tühja projekti, genereeris see XML mis sisaldab ConstraintLayout'i ja mille sees on TextView tekstiga „Hello“. Kustutame kogu sisu ja paigutus sisaldab ainult RecyclerView'i, nagu allpool näidatud:
<?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" />
Oleme lisanud ka RecyclerView jaoks id-atribuudi, mida me oma koodis sellele viitamiseks kasutame.
android:id="@+id/fruitRecyclerView"
Seejärel navigeerime tagasi oma MainActivity faili. Kasutades loodud ID-sid, saame viidata äsja loodud vaadetele. Alustame RecyclerView'le viitamisest, kasutades meetodit findViewById(), mille pakub AndroidMe teeme seda oma onCreate() meetodis. Meie onCreate() meetod näeb välja järgmine.
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) var myFruitRecyclerView: RecyclerView = findViewById(R.id.fruitRecyclerView) }
Loo ViewHolder
Järgmisena loome RecyclerView.ViewHolderi, mis võtab meie vaate (View) ja teisendab selle ViewHolderiks (ViewHolderiks), mida RecyclerView kasutab meie üksuste kuvamiseks. Teeme seda kohe pärast meie onCreate() meetodit.
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) }
Looge RecyclerView Adapter
Järgmisena loome klassi FruitArrayAdapter, mis laiendab klassi RecyclerView.Adapter. Meie loodud FruitArrayAdapter vastutab järgmise eest: see võtab puuviljamassiivist puuviljanimed, loob meie vaate one_fruit_view.xml abil ViewHolderi, seejärel seob puuvilja ViewHolderiga ja seob sisu dünaamiliselt loodud vaatega (View).
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 lisab meie FruitArrayAdapterile punased kriipsud, mis näitavad, et peame rakendama meetodeid, mida RecyclerView saab kasutada meie massiivi ühendamiseks ViewHolderiga.
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) { } }
Alustame genereeritud koodi kõige lihtsamast osast, meetodist getItemCount(). Me teame, kuidas massiivi elementide arvu teada saada, kutsudes välja massiivi suuruse omaduse.
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) { } }
Seejärel rakendame meetodi onCreateViewHolder. Siin palub RecyclerView meil aidata tal luua FruitViewHolder. Meie FruitViewHolderi klass nägi meie mäletamist mööda välja selline:
class FruitViewHolder(fruitView: View): RecyclerView.ViewHolder(fruitView)
See nõuab meie fruitView'd, mille lõime XML-failina one_fruit_view.xml. Saame sellele XML-ile viite luua ja selle View'ks teisendada järgmiselt.
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) { } }
Ülejäänud bitt on onBindViewHolderi ülekirjutus.
fun onBindViewHolder(holder: FruitViewHolder, position: Int)
RecyclerView.Adapter küsib positsiooni täisarvuga, mida kasutame loendist elemendi toomiseks. See annab meile ka hoidja, et saaksime fruitArray'st saadud elemendi siduda vaatehoidja sees oleva vaatega (View). ViewHolderis olevale vaatele (ViewHolder.itemView) pääseb ligi välja ViewHolder.itemView kaudu. Kui oleme vaate kätte saanud, saame sisu määramiseks kasutada varem loodud ID-d fruitName.
override fun onBindViewHolder(holder: FruitViewHolder, position: Int) { var ourFruitTextView = holder.itemView.findViewById<TextView>(R.id.fruitName) var aFruitName = fruitArray.get(position) ourFruitTextView.setText(aFruitName) }
Sellega on meie FruitArrayAdapter valmis ja näeb välja järgmine.
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) } }
Lõpuks oleme valmis ühendama meie RecyclerView ülejäänud osad, mis loovad LayoutManageri, mis ütleb RecyclerView'le, kuidas loendi sisu kuvada – kas lineaarselt LinearLayoutManageri abil või ruudustikus GridLayoutManageri või StaggeredGridLayoutManageri abil.
Loo paigutushaldur
Läheme tagasi oma onCreate funktsiooni juurde ja lisame LayoutManageri.
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 }
Ühendage meie adapter esemete külge ja seadke see RecyclerView'sse
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 }
Samuti oleme loonud fruitListAdapteri eksemplari ja sisestanud sinna puuviljade nimede massiivi. Ja põhimõtteliselt ongi kõik tehtud. Täielik MainActivity.kt fail näeb välja järgmine.
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) } } }




