Android RecyclerView: Mitä on, opi yksinkertaisilla esimerkeillä
Missä RecyclerView on Android?
- RecyclerView on widget, joka on joustavampi ja edistyneempi versio GridView- ja ListView-ohjelmista. Se on kontti suurten tietojoukkojen näyttämiseen, joita voidaan vierittää tehokkaasti ylläpitämällä rajoitettu määrä näkymiä. Voit käyttää RecyclerView-widgetiä, kun sinulla on tietokokoelmia, joiden elementit muuttuvat ajon aikana verkkotapahtuman tai käyttäjän toiminnan mukaan.
Näyttökerrat
- Android alusta käyttää View- ja ViewGroup-luokkia kohteiden piirtämiseen näytölle. Nämä luokat ovat abstrakteja ja niitä on laajennettu erilaisiin toteutuksiin käyttötapauksen mukaan. Esimerkiksi TextView-sovelluksella on yksinkertainen tarkoitus näyttää tekstisisältöä näytöllä. EditText ulottuu samasta View-luokasta ja lisää toimintoja, jotta käyttäjä voi syöttää tietoja.
On mahdollista luoda omia mukautettuja näkymiä, jotta voimme saavuttaa enemmän joustavuutta käyttöliittymien kehittämisessä. View-luokka tarjoaa menetelmiä, joita voimme ohittaa piirtämään näytölle, ja tavan siirtää parametreja, kuten leveys, korkeus ja omat mukautetut attribuuttimme, jotka haluamme lisätä näkymäämme saadakseen sen toimimaan haluamallamme tavalla.
NäytäRyhmät
ViewGroup-luokka on eräänlainen View, mutta toisin kuin yksinkertainen View-luokka, jonka tehtävänä on pelkkä näyttäminen, ViewGroup antaa meille mahdollisuuden laittaa useita näkymiä yhteen näkymään, johon voimme viitata kokonaisuutena. Tässä tapauksessa ylätasolla luotua näkymää, johon lisäämme muita yksinkertaisia näkymiä (voimme myös lisätä näkymäryhmiä), kutsutaan "vanhemmiksi", ja sisälle lisätyt näkymät ovat "lapsia".
Voimme kuvata näkymän taulukkona ja näkymäryhmän taulukkona. Ottaen huomioon, että Array of Array on itse matriisi, voimme nähdä, kuinka ViewGroupia voidaan käsitellä näkymänä.
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
ViewGroupin avulla voimme myös määritellä, miten lapset järjestetään näkymän sisällä, esimerkiksi asetetaan pysty- tai vaakasuoraan. Meillä voi olla erilaiset säännöt vuorovaikutukselle näkymän sisällä. Esimerkiksi toisiaan seuraavien TextView-kuvien etäisyyden tulisi olla 12 dp, kun taas TextView:n seuraavien ImageView-näkymien etäisyyden tulisi olla 5 dp.
Näin olisi, jos kehittäisimme omaa ViewGroupia tyhjästä. Helpottaaksesi näitä määrityksiä, Android tarjoaa luokan nimeltä LayoutParams, jota voimme käyttää näiden kokoonpanojen syöttämiseen.
Android dokumentointi tarjoaa joitain oletusparametreja, joita ottaisimme käyttöön määrittäessämme omaa ViewGroupia. Joitakin yleisiä parametreja ovat leveys, korkeus ja marginaali. Oletuksena näillä määrityksillä on rakenne android:layout_height korkeutta varten, esimerkiksi android:layout_width. Kun luot ViewGroupin, voit lisäksi luoda LayoutParams-parametreja sen mukaan, miten haluat ViewGroupin käyttäytyvän.
Android mukana tulee oletusnäkymät ja ViewGroups, joita voimme käyttää useiden yleisten tehtävien suorittamiseen. Yksi mainitsemamme esimerkki on TextView. Tämä on yksinkertainen näkymä, jossa on konfiguroitavia ominaisuuksia, kuten korkeus, leveys, tekstikoko ja vastaavat. Meillä on ImageView kuvien ja EditTextin näyttämiseen, kuten olimme maininneet muun muassa. Android on myös mukautettuja ViewGroups-ryhmiä, joihin voimme lisätä näkymämme ja saada odotetun toiminnan.
Lineaarinen asettelu
LinearLayoutin avulla voimme lisätä siihen Näytä-kohteita. LinearLayout on suuntamäärite, joka määrää, kuinka se asetetaan näytölle. Siinä on myös LinearLayout.LayoutParams, jotka sanelevat sisäisten näkymien säännöt. Esimerkiksi attribuutti android:center_horizontal keskittäisi näkymät vaaka-akselille, kun taas `android:center_vertical keskittäisi näkymän sisällön pystyakselille.
Tässä on joitain kuvia keskittämisen ymmärtämiseksi. Ottaisimme tämän yksinkertaiseksi TextView-näkymäksi 200 x 200 pikselin tilassa, attribuuttien keskittäminen saisi sen toimimaan seuraavasti.
android:center_horizontal

android:center_vertical

android:center

RecyclerView:n ydinkomponentit

Seuraavat ovat RecyclerView:n tärkeät osat:
RecyclerView.Adapter
Pohjatyö nro 1 – sovitinkuvio
Sovitin on laite, joka muuttaa järjestelmän tai laitteen attribuutit muuten yhteensopimattoman laitteen tai järjestelmän määritteiksi. Jotkut niistä muokkaavat signaalin tai tehon ominaisuuksia, kun taas toiset yksinkertaisesti mukauttavat liittimen fyysistä muotoa toiseen.
Yksinkertainen esimerkki, jonka löydämme tosielämästä selittämään sovittimen, on se, kun meidän on kytkettävä laitteet yhteen, mutta niissä on liitäntäportit, jotka eivät vastaa toisiaan. Näin voi käydä, kun vierailet toisessa maassa, jossa he käyttävät erilaisia pistorasioita. Jos kannat puhelimen tai kannettavan tietokoneen laturia, sen kytkeminen pistorasiaan on mahdotonta. Et kuitenkaan antaisi periksi, vaan hankit vain sovittimen, joka tulee virtalähteen ja laturin väliin ja mahdollistaa latauksen.
Näin on ohjelmoinnissa, kun halutaan yhdistää kaksi tietorakennetta tehtävän suorittamiseksi, mutta niiden oletusportit eivät pysty kommunikoimaan keskenään.
Käytämme yksinkertaista esimerkkiä laitteesta ja laturista. Meillä on kaksi laturia. Amerikkalainen ja brittiläinen
class AmericanCharger() { var chargingPower = 10 } class BritishCharger(){ var charginPower = 5 }
Luomme sitten kaksi laitetta
class AmericanDevice() class BritishDevice()
Esimerkkinä voimme sitten luoda laitteista joitain esiintymiä pelaamista varten.
var myAmericanPhone = new AmericanDevice() var myBritishPhone = new BritishDevice()
Esittelemme sitten molempien laitteiden latauksen käsitteen lisäämällä laitteisiin menetelmän nimeltä charge() .
Menetelmä ottaa sisääntulona sen vastaavan laturin ja suorittaa latauksen sen perusteella.
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 } }
Tässä tapauksessa analogiamme perusteella joutuisimme syystä tai toisesta käyttämään BritishChargeria käytettäessä AmericanDevicea tai päinvastoin.
Ohjelmointimaailmassa tämä on yleensä silloin, kun sekoitetaan yhteen kirjastoja, jotka tarjoavat saman toiminnallisuuden (meidän kontekstissamme jaettu toiminnallisuus on veloitus). Meidän on löydettävä keino tämän mahdollistamiseksi.
Jos noudatamme analogiaa, meidän on mentävä elektroniikkaliikkeeseen ja ostettava sovitin, jonka avulla voimme ladata AmericanDevicesia BritishChargers-latureilla. Ohjelmoinnin näkökulmasta me olemme ne, jotka ovat sovittimen valmistaja.
Teemme yhdelle sovittimen, joka vastaa täsmälleen sitä mallia, jota tarvitsemme toisen luomiseen. Toteutamme sen luokkana seuraavasti. Sen ei välttämättä tarvitse olla luokka, ja se voi olla funktio, joka korostaa sovittimen kuvion yleensä. Käytämme luokkaa, joka sopii useimpiin käyttötarkoituksiin 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 } }
Ohjelmointimaailmassa pistorasian ero on samanlainen kuin sisällä olevien lataustapojen ero. Eri menetelmiä käyttävät laturit tekisivät mahdottomaksi laturien käytön.
var myBritishDevice = new BritishDevice() var americanChargerIFound = new AmericanCharger()
Yritetään kutsua charge()-menetelmää myBritishDevicessa americanChargerIFoundilla ei toimisi, koska AmericanDevice hyväksyy vain AmericanChargerin
Joten tämä on mahdotonta tehdä
var myBritishDevice = new BritishDevice() var americanChargerIFound = new AmericanCharger() myBritishDevice.charge(americanChargerIFound)
Tässä skenaariossa luomamme sovitin
AmericanToBritishChargerAdapter voi nyt olla hyödyllinen. Voimme käyttää returnNewCharger()-menetelmää uuden BritishChargerin luomiseen, jota voimme käyttää lataamiseen. Meidän tarvitsee vain luoda sovittimemme esiintymä ja syöttää se AmericanChargerilla, joka meillä on, niin se luo BritishChargerin, jota voimme käyttää.
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
Käsitellessämme ViewGroupia meillä olisi näkymät sen sisään. LayoutManagerin tehtävänä on kuvata, kuinka näkymät on sijoitettu sisällä.
Vertailun vuoksi Linearlayout ViewGroupin kanssa työskennellessämme haluamamme käyttötapaus on mahdollisuus sijoittaa kohteet joko pysty- tai vaakasuoraan. Tämä on helppo toteuttaa lisäämällä orientaatioattribuutti, joka kertoo kuinka lineaarinen asettelu sijoitetaan näytölle. Voimme tehdä tämän käyttämällä android:orientation=VERTICAL|HORIZONTAL
määrite.
Meillä on myös toinen ViewGroup nimeltään GridLayout, sen käyttötapaus on, kun haluamme sijoittaa näkymät suorakaiteen muotoiseen Grid-rakenteeseen. Tämä voi johtua esimerkiksi siitä, että sovelluksen käyttäjälle esittämämme tiedot ovat helppokäyttöisiä. Suunnittelultaan GridLayout mahdollistaa konfiguraatiot, jotka auttavat sinua saavuttamaan tämän tavoitteen, koska meillä on konfiguraatioita, joissa voimme määritellä ruudukon mitat, esimerkiksi meillä voi olla 4 × 4 ruudukko, 3 × 2 ruudukko.
RecyclerView.ViewHolder
ViewHolder on abstrakti luokka, jonka laajennamme myös RecyclerView'sta. ViewHolder tarjoaa meille yleisiä menetelmiä, joiden avulla voimme viitata näkymään, jonka olemme sijoittaneet RecyclerView'hun, vaikka RecyclerView'n kierrätyskoneisto on muuttanut useita viitteitä, joista emme tiedä.
Suuret listat
RecyclerViewia käytetään, kun haluamme esittää käyttäjälle todella suuren joukon näkymiä, mutta emme kuitenkaan kuluta RAM laitteellamme jokaiselle luodun näkymän esiintymälle.
Jos ottaisimme yhteystietoluettelon, meillä olisi yleinen käsitys siitä, miltä yksi yhteyshenkilö näyttäisi luettelossa. Sitten tekisimme malliasettelun – joka on itse asiassa näkymä – jossa on paikkoja, joihin yhteystietoluettelomme tiedot täyttyvät. Seuraava on pseudokoodi, joka selittää koko tarkoituksen:
//OneContactView <OneContact> <TextView>{{PlaceHolderForName}}</TextView> <TextView>{{PlaceHolderForAddress}}</TextView> <ImageView>{{PlaceHolderForProfilePicture}}</ImageView> <TextView>{{PlaceHolderForPhoneNumber}}</TextView> </OneContact>
Sitten meillä olisi tällainen yhteystietolista
<ContactList> </ContactList>
Jos näin on, koodasimme sisällön, meillä ei olisi ohjelmallista tapaa lisätä uutta sisältöä luetteloon kirjoittamatta sovellusta uudelleen. Meidän onneksi. Näkymän lisäämistä ViewGroupiin tukee addView(view:View)
menetelmällä.
Vaikka näin olisikin, RecyclerView ei lisää lasten näkymiä siihen.
Meidän käyttötapauksessa meillä olisi pitkä yhteyshenkilöluettelo. Jokaiselle luettelossa olevalle yhteyshenkilölle meidän on luotava OneContactView ja täytettävä tiedot näkymän sisällä vastaamaan Yhteystietoluokkamme kenttiä. Sitten kun meillä on näkymä, meidän on lisättävä se RecyclerView'hun luettelon näyttämiseksi.
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)
Meillä on joukko yhteystietoja nimeltä OneContactView. Se sisältää paikkoja Yhteystiedot-luokan sisällön ottamiseksi ja näyttämiseksi. RecyclerView'ssa meidän on lisättävä siihen Views, jotta se voi auttaa meitä kierrätyskyvyssä.
RecyclerView ei todellakaan salli näkymän lisäämistä, mutta sen avulla voimme lisätä ViewHolderin. Joten tässä skenaariossa meillä on kaksi asiaa, jotka haluamme yhdistää, mutta jotka eivät täsmää. Tässä adapterimme tulee käyttöön. RecyclerView tarjoaa meille paljon samanlaisen sovittimen AmericanToBritishChargerAdapter()
aiemmasta, minkä ansiosta pystyimme muuttamaan AmericanChargerimme, joka ei ollut käyttökelvollinen BritishDevicemme kanssa, käyttökelpoiseksi, joka muistuttaa virtalähdettä tosielämässä.
Tässä skenaariossa sovitin ottaisi yhteystietojemme ja näkymämme ja luo sieltä ViewHolders, jotka RecyclerView on valmis hyväksymään.
RecyclerView tarjoaa käyttöliittymän, jota voimme laajentaa luodaksemme sovittimemme RecyclerView.Adapter-luokan kautta. Tämän sovittimen sisällä on tapa luoda ViewHolder-luokka, jonka kanssa RecyclerView haluaa työskennellä. Meillä on siis sama tilanne kuin ennenkin, mutta yhden ylimääräisen asian kanssa, se on sovitin.
Meillä on joukko yhteystietoja, näkymä yhden yhteystiedon näyttämiseen OneContactView. RecyclerView on luettelo näkymistä, jotka tarjoavat kierrätyspalveluita, mutta jotka ovat valmiita ottamaan vastaan vain ViewHolders
Mutta tässä skenaariossa meillä on nyt RecyclerView.Adapter-luokka, jonka sisällä on menetelmä ViewHolderien luomiseen.
fun createViewHolder(@NonNull parent: ViewGroup, viewType: Int): ViewHolder
RecyclerView.ViewHolder on abstrakti luokka, joka ottaa näkymämme argumenttina ja muuntaa sen ViewHolderiksi.
Se käyttää käärekuviota, jota käytetään luokkien kykyjen laajentamiseen.
Pohjatyö #2 – käärekuvio
Käytämme yksinkertaista esimerkkiä osoittaaksemme, kuinka voimme saada eläimet puhumaan.
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
Yllä olevassa esimerkissä meillä on kaksi eläintä. Jos sattumalta, halusimme lisätä menetelmän puhua, mutta kirjaston kirjoittaja ei ollut hauska, voisimme silti löytää tavan. Tarvitsemme kääreen Animal-luokkaamme. Tekisimme tämän ottamalla Animalin luokallemme rakentajaksi
class SpeechPoweredAnimalByWrapper(var myAnimal:Animal){ fun sound(){ myAnimal.sound() } speak(){ println("Hello, my name is ${myAnimal.name}") } }
Nyt voimme välittää eläininstanssin SpeechPoweredAnimalByWrapperille. Sound()-metodin kutsuminen siihen kutsuisi passin in animal sound() -menetelmää. Meillä on myös ylimääräinen speak()-menetelmä, joka lasketaan uudeksi toiminnalliseksi, jonka lisäämme sisään siirrettyihin eläimiin. Voimme käyttää sitä seuraavasti:
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"
Tämän mallin avulla voimme ottaa luokkia ja lisätä toimintoja. Tarvitsemme vain luokan ilmentymän ja kääreluokkamme määrittelemät uudet menetelmät.
Yllä olevassa tapauksessamme käytimme betoniluokkaa. Sama on mahdollista toteuttaa myös abstraktissa luokassa. Meidän pitäisi vain lisätä SpeechPoweredAnimalByWrapper-luokka abstraktiksi, ja se on valmis. Muutamme luokan nimen lyhyemmäksi, jotta se olisi helpompi lukea.
abstract class SpeechPowered(var myAnimal:Animal){ fun sound(){ myAnimal.sound() } speak(){ println("Hello, my name is ${myAnimal.name}") } }
Se on sama kuin ennen, mutta se tarkoittaisi jotain muuta. Normaalissa luokassa meillä voi olla luokan esiintymä samalla tavalla kuin loimme kissa1 ja koira1. Abstrakteja luokkia ei kuitenkaan ole tarkoitettu instantoitaviksi, vaan niiden tarkoituksena on laajentaa muita luokkia. Joten miten käyttäisimme uutta SpeechPowered(var myAnimal:Animal) abstraktia luokkaa. Voimme käyttää sitä luomalla uusia luokkia, jotka laajentavat sitä ja puolestaan saavat sen toimivuuden.
Esimerkissämme luomme luokan SpeechPoweredAnimal, joka laajentaa luokkaa
class SpeechPoweredAnimal(var myAnimal:Animal):SpeechPowered(myAnimal)
var cat1 = Cat("Tubby") var speakingKitty = SpeechPoweredAnimal(cat1) speakingKitty.speak() //"Hello, my name is Tubby"
Tämä on sama kuvio, jota käytetään ViewHolderissa. Luokka RecyclerView.ViewHolder on abstrakti luokka, joka lisää View-näkymään toimintoja, aivan kuten olemme lisänneet puhumismenetelmän eläimille. Lisätty toiminnallisuus saa sen toimimaan RecyclerView'n käsittelyssä.
Näin luomme OneContactViewHolderin OneContactView-sovelluksesta
//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:ssa on sovitin, jonka avulla voimme yhdistää yhteystietomatriisi ContactsView:hen RecyclerView:n avulla.
Näkymän lisääminen
ViewGroup ei piirrä ViewGroupia automaattisesti uudelleen, vaan noudattaa tiettyä aikataulua. Laitteessasi voi olla niin, että se piirtää uudelleen 10 ms tai 100 ms välein, tai jos valitsemme absurdin luvun, esimerkiksi 1 minuutin, kun lisäämme näkymän ViewGroupiin, näet muutokset minuutin kuluttua, kun ViewGroup "päivittyy".
RecyclerView.Recycler
Pohjatyö nro 3. Välimuisti
Yksi parhaista esimerkeistä siitä, missä teemme säännöllisesti päivityksiä, on selain. Oletetaan esimerkiksi, että vierailemamme sivusto on staattinen eikä lähetä sisältöä dynaamisesti. Meidän on päivitettävä päivitystä nähdäksemme muutokset.
Tässä esimerkissä oletetaan, että kyseessä oleva sivusto on Twitter. Meillä olisi luettelossa staattisia twiittejä, ja ainoa tapa nähdä uusia twiittejä on napsauttaa päivityspainiketta sisällön hakemiseksi.
Koko näytön uudelleenmaalaaminen on tietysti kallista. Jos kuvittelemme, että näin olisi, meillä oli rajoitettu kaistanleveys puhelinpalveluntarjoajamme kanssa. Ja twiittilistallamme oli paljon kuvia ja videoita, olisi kallista ladata koko sivun sisältö uudelleen jokaisen päivityksen yhteydessä.
Tarvitsemme tavan tallentaa jo ladatut twiitit ja varmistaa, että seuraava pyyntömme pystyy sanomaan jo olemassa olevat twiitit. Siksi se ei lataa kaikkea uudelleen, vaan saa vain uudet twiitit, jotka sillä on, ja myös tarkistaa, ettei jokin paikallisesti tallennettu twiitti ole enää siellä, jotta se voi poistaa sen paikallisesti. Se, mitä kuvaamme, on nimeltään välimuisti.
Tietoa, jonka lähetämme verkkosivustolle sisällöstämme, kutsutaan metatiedoksi. Joten todellisessa mielessä emme vain sano "haluamme ladata sivustosi", sanomme "haluamme ladata sivustosi, ja tässä on joitain sisältöjä, jotka olimme jo tallentaneet edellisen latauskerran jälkeen. Käytä sitä Lähetä meille vain se, mitä siellä ei ole, joten emme käytä paljon kaistanleveyttä, koska meillä ei ole paljon resursseja."
Ulkoasukutsut – Tweet-luettelon täytyy olla hullu
Esimerkki asettelukutsuista on scrollToPosition
Tämä on yleinen esimerkki, jota esiintyy esimerkiksi chat-sovelluksissa. Jos joku keskusteluketjussa vastaa aikaisempaan chat-kuplaan, jotkin chat-sovellukset sisältävät vastauksen ja linkin chat-kuplaan, jota napsauttamalla siirryt alkuperäisen viestisi sijaintiin.
Siinä tapauksessa kutsumme tätä menetelmää ennen kuin LayoutManager on lisätty RecyclerView-näkymään ja ennen kuin meillä on RecyclerView.Adapter, scrollToPosition(n:Int) jätetään huomiotta.
Viestintä RecyclerView-komponenttien välillä
Pohjatyö nro 4. Takaisinsoittoja
RecyclerView'ssa on työssään paljon liikkuvia osia. Sen on käsiteltävä LayoutManageria, joka kertoo meille, kuinka näkymät järjestetään joko lineaarisesti tai ruudukossa. Sen on toimittava sovittimen kanssa, joka muuntaa kohteemme contactList Views OneContactView -näkymäksi ja sitten ViewHolders OneContactViewHolder -sovellukseksi, jota RecyclerView on valmis työskentelemään meille tarjoamillaan menetelmillä.
RecyclerView:n raaka-aineena ovat Views-näkymämme, esim. OneContactView ja tietolähde.
contactList:Array<Contact>
Olemme käyttäneet yksinkertaista skenaariota lähtökohtana saadaksemme käsityksen siitä, mitä RecyclerView yrittää saavuttaa.
Perustapaus, jossa meillä olisi 1000 kontaktin staattinen joukko, jonka haluamme näyttää käyttäjälle, on helppo ymmärtää.
RecyclerView-koneisto alkaa todella elää, kun luettelo ei ole enää staattinen.
Dynaamisen luettelon avulla meidän on mietittävä, mitä tapahtuu näytöllä näkyvälle näkymälle, kun lisäämme kohteen luetteloon tai poistamme kohteen luettelosta.
RecyclerView.LayoutManager
Sen lisäksi, että päätetään, kuinka näkemyksemme asetetaan joko lineaarisesti tai ruudukossa. LayoutManager tekee paljon työtä konepellin alla auttaakseen kierrättäjää tietämään, milloin kierrätys tulee tehdä.
Se vastaa näytöllä tällä hetkellä näkyvien näkymien seuraamisesta ja näiden tietojen välittämisestä kierrätysmekanismille. Kun käyttäjä rullaa alaspäin, asettelupäällikkö on vastuussa siitä, että se ilmoittaa kierrätysjärjestelmälle näkymistä, jotka menevät epätarkkoihin ylhäällä, jotta niitä voidaan käyttää uudelleen sen sijaan, että ne jäävät sinne ja kuluttaisivat muistia tai tuhoaisivat ne ja joutuisivat luomaan. uudet.
Tämä tarkoittaa, että LayoutManagerin on seurattava, missä käyttäjä on, kun hän vierittää luetteloamme. Se tekee tämän luomalla luettelon sijoituksista, jotka ovat indeksipohjaisia, eli ensimmäinen kohta on aloitettava nollasta ja kasvatettava vastaamaan luettelossamme olevien kohteiden määrää.
Jos voimme tarkastella 10 kohdetta esimerkiksi 100:n luettelossamme, LayoutManager tietää alussa, että sillä on tarkentunut näkymä-0 aina näkymä-9:ään asti. Kun vieriämme, LayoutManager pystyy laskemaan ulos tulevat näkymät. keskittymistä.
LayoutManager voi vapauttaa nämä näkymät Kierrätysmekanismille, jotta niitä voidaan käyttää uudelleen (niihin voidaan sitoa uusia tietoja, esim. näkymän yhteystiedot voidaan poistaa ja uudet yhteystiedot seuraavasta segmentistä voivat korvata paikkamerkit).
Tämä olisi onnellinen tapaus, jos meillä oleva luettelo on staattinen, mutta yksi yleisimmistä RecyclerView-käyttötapauksista on dynaamiset luettelot, joissa tiedot voivat tulla online-päätepisteestä tai jopa kenties anturista. Tietoja ei vain lisätä, vaan myös luettelomme tietoja joskus poistetaan tai päivitetään.
Tietojemme dynaaminen tila voi vaikeuttaa LayoutManagerin perustelemista. Tästä syystä LayoutManager ylläpitää omaa luetteloa kohteista ja paikoista, joka on erillinen Kierrätyskomponentin käyttämästä luettelosta. Tämä varmistaa, että se tekee asettelutyönsä oikein.
Samaan aikaan RecyclerView'n LayoutManager ei halua antaa vääriä tietoja sillä on. Toimiakseen oikein LayoutManager synkronoituu RecyclerView.Adapterin kanssa tietyin väliajoin (60 ms) jakaa tietoja luettelokohteistamme. eli kohteet lisätty, päivitetty, poistettu, siirretty paikasta toiseen). Saatuaan nämä tiedot LayoutManager järjestää uudelleen näytön sisällön vastaamaan muutoksia tarvittaessa.
Monet RecylerView'ta käsittelevistä ydintoiminnoista kiertävät RecyclerView.LayoutManagerin ja RecyclerView.Adapterin välistä viestintää, joka tallentaa luettelomme joskus staattisista tai joskus dynaamisista tiedoista.
Lisäksi RecyclerView esittelee meille menetelmiä, joilla voimme kuunnella tapahtumia, kuten onBindViewHolder, kun RecyclerView.Adaptermme sitoo sisältöä luettelostamme, esim. yhteyshenkilön ViewHolderiin, jotta se nyt tottuu näyttämään tiedot näytöllä.
Toinen on onCreateViewHolder, joka kertoo meille, milloin RecyclerView.Sovitin ottaa tavallisen näkymän, kuten OneContactView, ja muuntaa sen ViewHolder-kohteeksi, jonka kanssa RecyclerView voi toimia. ViewHolderistamme RecyclerView'n käyttöön. onViewDetached
Kierrätyksen mahdollistavien ydinmekanismien lisäksi. RecyclerView tarjoaa tapoja mukauttaa käyttäytymistä vaikuttamatta kierrätykseen.
Näkymien uudelleenkäyttö vaikeuttaa yleisten asioiden tekemistä, joita olemme tottuneet tekemään staattisilla näkymillä, kuten reagoimista onClick-tapahtumiin.
Kuten tiedämme, että RecyclerView.LayoutManager
joka esittelee näkymät käyttäjälle, saattaa hetkeksi olla eri luettelo kohteista kuin RecyclerView.Adapter
joka sisältää luettelon, jonka olemme tallentaneet tietokantaan tai suoratoistona lähteestä. OnClick-tapahtumien lisääminen suoraan Viewsiin voi johtaa odottamattomaan toimintaan, kuten väärän yhteystiedon poistamiseen tai vaihtamiseen.
Gradle
Jos haluamme käyttää RecyclerView'ta, meidän on lisättävä se riippuvuutena build .gradle -tiedostoomme.
Seuraavassa esimerkissä olemme käyttäneet toteutusta "androidx.recyclerview:recyclerview:1.1.0", joka on tämän artikkelin uusin versio.
Lisättyään riippuvuuden meidän Gradle tiedosto, saamme kehotteen Android Studioon Synchronisoida muutokset,
Näin on meidän Gradle tiedosto näyttää siltä, kun RecyclerView on lisätty tyhjään projektiin vain oletusarvoilla.
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" }
Meillä on tällä hetkellä vain yksi asettelutiedosto. Aloitamme yksinkertaisella esimerkillä, jossa käytämme RecyclerView'ta näyttääksemme luettelon hedelmien nimistä näytöllä.
Luettelo tuotteista
Siirrymme MainActivity-tiedostoomme ja luomme taulukon, jossa on hedelmien nimet juuri ennen asennuksen aikana luotua onCreate()-menetelmää.
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) } }
Seuraava tavoitteemme on esittää tämä luettelo näytöllä RecyclerView'n avulla.
Tätä varten siirrymme asetteluhakemistoon, joka sisältää asettelumme, ja luomme näkymän, joka vastaa yhden hedelmän näyttämisestä.
Asettelu, jota käytetään luettelossamme jokaiselle kohteelle
<?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>
Yllä olevaan TextView-näkymään olemme lisänneet id-kentän, jota käytetään näkymän tunnistamiseen.
Sitä ei luoda oletuksena. Meillä on TextView id fruitName vastaamaan tietoja, jotka sidotaan siihen.
RecyclerView'n lisääminen pääasetteluun
Samassa toiminnassa on main_layout.xml-asettelutiedosto, joka on luotu meille oletuksena.
Jos valitsimme tyhjän projektin. Se on luonut XML sisältää ConstraintLayout ja sen sisällä on TextView, jossa on "Hei"-teksti.
Poistamme kaiken sisällön ja asettelussa on vain RecyclerView, kuten alla:
<?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" />
Olemme myös lisänneet RecyclerView:lle id-attribuutin, jota käytämme viittaamaan siihen koodissamme.
android:id="@+id/fruitRecyclerView"
Siirrymme sitten takaisin MainActivity-tiedostoomme. Käyttämällä luomiamme tunnuksia voimme viitata juuri luomiimme näkymiin.
Aloitamme viittaamalla RecyclerView'n tarjoamaan findViewById()-menetelmään Android. Teemme tämän onCreate()-menetelmällämme.
OnCreate()-menetelmämme näyttää seuraavalta.
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) var myFruitRecyclerView: RecyclerView = findViewById(R.id.fruitRecyclerView) }
Luo ViewHolder
Seuraavaksi luomme RecyclerView.ViewHolderin, joka vastaa näkymämme ottamisesta ja sen muuntamisesta ViewHolderiksi, jota RecyclerView käyttää tuotteidemme näyttämiseen.
Teemme tämän heti hauskan onCreate()-menetelmämme jälkeen.
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) }
Luo RecyclerViewAdapter
Seuraavaksi luomme FruitArrayAdapter-luokan, joka laajentaa RecyclerView.Adapter-luokkaa.
Luomamme FruitArrayAdapter on vastuussa seuraavista toimista.
Se ottaa hedelmien nimet hedelmäjoukosta. Se luo ViewHolderin käyttämällä näkymäämme one_fruit_view.xml Se sitoo sitten hedelmät ViewHolderiin ja sitoo sisällön dynaamisesti luomaan one_fruit_view.xml-näkymään.
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 lisää punaisia värejä FruitArrayAdapteriimme, mikä kertoo meille, että meidän on otettava käyttöön menetelmä, jolla RecyclerView voi yhdistää taulukomme ViewHolderiin, jota RecyclerView voi käyttää.
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) { } }
Aloitamme luodun koodin helpoimmasta bitistä getItemCount()-menetelmä. Tiedämme kuinka saada taulukossa olevien kohteiden määrä kutsumalla array.length-menetelmää.
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) { } }
Sitten otamme käyttöön menetelmän ohitus hauskan CreateViewHolderissa.
Tässä RecyclerView pyytää meitä auttamaan sitä rakentamaan sille FruitHolderin.
Jos muistamme, tältä FruitViewHolder-luokkamme näytti:
class FruitViewHolder(fruitView: View): RecyclerView.ViewHolder(fruitView)
Se vaatii hedelmänäkymämme, jonka loimme xml-tiedostona one_fruit_view.xml
Voimme luoda viittauksen tähän xml-tiedostoon ja muuntaa sen näkymäksi seuraavasti.
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) { } }
Jäljellä oleva bitti on ohitus
fun onBindViewHolder(holder: FruitViewHolder, position: Int)
RecyclerView.Adapter kysyy sijaintikokonaisluvulla, jota käytämme kohteen hakemiseen luettelostamme. Se tarjoaa meille myös pidikkeen, jotta voimme sitoa hedelmätaulukosta saamamme kohteen näkymäpitimen sisällä olevaan näkymään.
ViewHoderin sisällä olevaan näkymään pääsee ViewHolder.itemView-kentän kautta. Kun saamme näkymän, voimme käyttää aiemmin luomaamme id fruitName sisällön asettamiseen.
override fun onBindViewHolder(holder: FruitViewHolder, position: Int) { var ourFruitTextView = holder.itemView.findViewById<TextView>(R.id.fruitName) var aFruitName = fruitArray.get(position) ourFruitTextView.setText(aFruitName) }
FruitArrayAdapterimme on valmis ja näyttää seuraavalta.
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) } }
Lopuksi olemme valmiita yhdistämään jäljellä olevat RecyclerView-osat. Jotka luovat LayoutManagerin, joka kertoo RecyclerViewille, kuinka luettelon sisältö näytetään. Näytetäänkö lineaarisesti LinearLayoutManagerin avulla tai ruudukossa GridLayoutManagerin tai StaggeredGridLayoutManagerin avulla.
Luo Layout Manager
Palaamme onCreate-toimintoomme ja lisäämme LayoutManagerin
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 }
Kiinnitä sovitin esineisiin ja aseta se RecyclerView'lle
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 }
Olemme myös luoneet fruitListAdapter-esiintymän ja syöttäneet sille joukon hedelmien nimiä.
Ja periaatteessa olemme kaikki valmiita.
Täydellinen MainActivity.kt-tiedosto näyttää seuraavalta.
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) } } }