Android RecyclerView: Mitä on, opi yksinkertaisilla esimerkeillä

⚡ Älykäs yhteenveto

RecyclerView on edistynyt Android widget, joka näyttää suuria, vieritettäviä tietojoukkoja tehokkaasti kierrättämällä rajoitetun määrän näkymiä. Se käyttää sovitinta, asettelun hallintaohjelmaa ja näkymänpitäjää sitoakseen ajonaikana muuttuvat tietokokoelmat.

  • 📱 Mitä se on: RecyclerView on ListView'n ja GridView'n joustava seuraaja suurten tietokokoelmien näyttämiseen.
  • 🧱 Näkymät ja näkymäryhmät: Android piirtää käyttöliittymän View- ja ViewGroup-luokkien avulla, ja LayoutParams ohjaa kokoa ja sijaintia.
  • 🔄 Adapter: Sovitinmalli muuntaa datataulukon ViewHolder-objekteiksi, jotka RecyclerView voi hyväksyä.
  • 📐 Asettelunhallinta: Asettelun hallintaohjelma järjestää kohteet lineaarisesti tai ruudukkoon ja ilmoittaa, milloin näkymiä voidaan kierrättää.
  • ♻️ Kierrätys: Näytön ulkopuolisia näkymiä käytetään uudelleen luomisen sijaan, mikä säästää muistia ja pitää vierityksen sujuvana.
  • 🛠️ Rakenna vaiheet: Lisää Gradle riippuvuus, luo alkion asettelu, ViewHolder, sovitin ja LayoutManager.

Android RecyclerView

Missä RecyclerView on Android?

RecyclerView on GridView'n ja ListView'n joustavampi ja edistyneempi versio. Se on säilö 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 suorituksen 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 abstract ja niitä laajennetaan eri toteutuksiin käyttötapauksen mukaan. Esimerkiksi TextView'n yksinkertainen tarkoitus on näyttää tekstisisältöä näytöllä. EditText ulottuu samasta View-luokasta ja lisää toimintoja, joiden avulla käyttäjä voi syöttää tietoja.

On mahdollista luoda omia mukautettuja näkymiä, mikä lisää joustavuutta kehitysvaiheessa.ping käyttöliittymiä. View-luokka tarjoaa metodeja, joita voimme korvata piirtääksemme näytölle, ja keinon välittää parametreja, kuten leveys, korkeus ja omia mukautettuja attribuutteja, joita haluamme lisätä View-luokkaamme, jotta se toimisi haluamallamme tavalla.

NäytäRyhmät

ViewGroup-luokka on eräänlainen View-luokka, mutta toisin kuin yksinkertainen View-luokka, jonka tehtävänä on vain näyttää, ViewGroup antaa meille mahdollisuuden yhdistää useita näkymiä yhteen näkymään, johon voimme viitata kokonaisuutena. Tässä tapauksessa ylimmällä tasolla luotua View'ta, johon lisäämme muita yksinkertaisia ​​näkymiä, kutsutaan "päänäkymäksi", ja sen sisälle lisättyjä näkymiä kutsutaan "alinäkymäksi".

Voimme kuvitella näkymän (View) taulukkona (Array) ja näkymäryhmän (ViewGroup) taulukoiden joukosta (Array) koostuvana taulukkona. Koska taulukoiden joukosta koostuva taulukko on itsekin taulukko, voimme nähdä, kuinka näkymäryhmää (ViewGroup) voidaan käsitellä näkymän (View) tavoin.

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-ryhmän avulla voimme myös määrittää, miten alinäkymän elementit on järjestetty näkymän sisällä, esimerkiksi ovatko ne pystysuunnassa vai vaakasuunnassa. Voimme käyttää erilaisia ​​sääntöjä näkymän sisäiselle vuorovaikutukselle. Esimerkiksi toisiaan seuraavien TextView-elementtien etäisyyden tulisi olla 12 dp, kun taas TextView-elementin ja sitä seuraavan ImageView-elementin etäisyyden tulisi olla 5 dp.

Näin olisi, jos olisimme kehittyneetping oma ViewGroup alusta alkaen. Näiden määritysten helpottamiseksi Android tarjoaa luokan nimeltä LayoutParams, jota voimme käyttää näiden kokoonpanojen syöttämiseen.

Android dokumentointi tarjoaa joitakin oletusparametreja, joita käyttäisimme oman ViewGroup-arvomme määrittämisessä. Joitakin yleisiä parametreja ovat leveys, korkeus ja reunus. Oletusarvoisesti näillä kokoonpanoilla on rakenne android:layout_height korkeudelle ja android:layout_width leveydelle. Tässä suhteessa, kun luot ViewGroup-arvosi, voit luoda LayoutParams-arvoja, jotka ovat erityisiä ViewGroup-arvojesi toiminnan mukaan.

Android sisältää oletusarvoiset näkymät ja näkymäryhmät, joita voimme käyttää monien yleisten tehtävien suorittamiseen. Yksi mainitsemamme esimerkki on TextView. Se on yksinkertainen näkymä, jossa on konfiguroitavia ominaisuuksia, kuten korkeus, leveys ja tekstin koko. Meillä on myös ImageView kuvien näyttämiseen ja, kuten mainitsimme, EditText, monien muiden ohella. Android on myös mukautettuja ViewGroups-ryhmiä, joihin voimme lisätä näkymämme ja saada odotetun toiminnan.

Lineaarinen asettelu

LinearLayout-asetukselle voidaan lisätä View-kohteita. LinearLayout-asetuksella on orientation-attribuutti, joka määrää, miten se asetetaan näytöllä. Siinä on myös LinearLayout.LayoutParams-attribuutti, joka sanelee säännöt sen sisällä oleville näkymille. 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 kuvia keskittämisen ymmärtämiseksi. Oletamme tämän olevan yksinkertainen TextView 200 x 200 pikselin kokoisessa tilassa; keskitysattribuutit saisivat sen toimimaan seuraavasti.

android:center_horizontal

Vaakasuoraan keskitetty sisältö

Vaakasuoraan keskitetty sisältö

android:center_vertical

Pystysuuntaisesti keskitetty sisältö

Pystysuunnassa keskitetty sisältö

android:center

Keskitetty sisältö

Keskitetty sisältö

RecyclerView:n ydinkomponentit

RecyclerView:n ydinkomponentit

RecyclerView:n ydinkomponentit

Seuraavat ovat RecyclerView:n tärkeät osat:

RecyclerView.Adapter

Pohjatyö nro 1 – sovitinkuvio

Sovitin on laite, joka muuttaa yhden järjestelmän tai laitteen ominaisuudet muuten yhteensopimattoman laitteen tai järjestelmän ominaisuuksiksi. Jotkut sovittimet muokkaavat signaali- tai teho-ominaisuuksia, kun taas toiset yksinkertaisesti mukauttavat yhden liittimen fyysisen muodon toiseen.

Yksinkertainen esimerkki sovittimesta on tilanne, jossa laitteita on yhdistettävä toisiinsa, mutta niiden liitäntäportit eivät sovi yhteen. Näin voi olla esimerkiksi silloin, kun vierailet toisessa maassa, jossa käytetään erilaisia ​​pistorasioita. Jos kannat mukanasi puhelimen tai kannettavan tietokoneen laturia, sitä on mahdotonta kytkeä pistorasiaan. Et kuitenkaan luovuta, vaan hankit sovittimen, joka asetetaan pistorasian ja laturin väliin ja mahdollistaa latauksen.

Näin on ohjelmoinnissa, kun haluamme yhdistää kaksi tietorakennetta toisiinsa 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 chargingPower = 5
}

Tämän jälkeen luomme kaksi laitetta.

class AmericanDevice()
class BritishDevice()

Esimerkkinä voimme sitten luoda laitteista joitain esiintymiä pelaamista varten.

var myAmericanPhone = new AmericanDevice()
var myBritishPhone = new BritishDevice()

Seuraavaksi esittelemme molempien laitteiden latauksen käsitteen lisäämällä laitteisiin metodin nimeltä charge(). Metodi ottaa vastaan ​​oman laturin tiedot 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 tarvitsisimme jostain syystä brittiläisen laturin käyttäessämme amerikkalaista laturia, 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 seuraamme analogiaa, meidän on mentävä elektroniikkaliikkeeseen ja ostettava sovitin, jonka avulla voimme ladata amerikkalaisia ​​laitteita, kunhan meillä on brittiläisiä latureita. Ohjelmoinnin näkökulmasta olemme sovittimen valmistaja.

Teemme sovittimen, joka vastaa täsmälleen samaa mallia, jota tarvitsisimme toisen luomiseen. Toteutamme sen luokkana seuraavasti. Sen ei välttämättä tarvitse olla luokka, ja se voi olla funktio, joka korostaa sovittimen yleistä toimintaa. Käytämme luokkaa, koska se vastaa useimpia käyttötarkoituksia 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
    }
}

Ohjelmoinnin maailmassa pistorasioiden ero on analoginen latausmenetelmien erojen kanssa. Latureiden erilaiset latausmenetelmät tekisivät niiden käytön mahdottomaksi.

var myBritishDevice = new BritishDevice()
var americanChargerIFound = new AmericanCharger()

charge()-metodin kutsuminen myBritishDevice-laitteessa americanChargerIFound-metodilla ei toimi, koska AmericanDevice hyväksyy vain AmericanCharger-latauslaitteen. Joten tätä on mahdotonta tehdä:

var myBritishDevice = new BritishDevice()
var americanChargerIFound = new AmericanCharger()
myBritishDevice.charge(americanChargerIFound)

Tässä skenaariossa luomastamme sovittimesta, AmericanToBritishChargerAdapterista, voi nyt olla hyötyä. Voimme käyttää returnNewCharger()-metodia luodaksemme uuden BritishChargerin, jota voimme käyttää lataamiseen. Meidän tarvitsee vain luoda sovittimestamme instanssi ja syöttää siihen AmericanCharger-lataussignaali, jolloin 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(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

Kun käsitellään ViewGroupia (näkymäryhmää), sen sisällä on näkymiä (views). LayoutManagerin (asettelunhallinnan) tehtävänä on kuvata, miten näkymät (views) on aseteltu ryhmän sisällä.

Vertailun vuoksi, kun työskentelemme LinearLayout ViewGroupin kanssa, haluamme mahdollisuuden sijoittaa kohteet joko pystysuunnassa tai vaakasuunnassa. Tämä on helppo toteuttaa lisäämällä orientation-attribuutti, joka kertoo meille, miten LinearLayout 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ä GridLayout. Sen käyttötapaus on, kun haluamme sijoittaa näkymiä suorakaiteen muotoiseen ruudukkorakenteeseen. Tämä voi olla tarpeen esimerkiksi siksi, että sovelluksen käyttäjälle esittämämme datan käyttö olisi helppoa. GridLayout on suunniteltu mahdollistamaan konfiguraatioita, jotka auttavat saavuttamaan tämän tavoitteen määrittämällä ruudukon mitat; meillä voi esimerkiksi olla 4×4-ruudukko tai 3×2-ruudukko.

RecyclerView.ViewHolder

ViewHolder on vatsalihastract-luokka, jota laajennamme myös RecyclerView'sta. ViewHolder tarjoaa meille yleisiä metodeja, joiden avulla voimme viitata RecyclerView'hun sijoittamaamme näkymään, vaikka RecyclerView'n kierrätysmekanismi olisi muuttanut useita viittauksia, joista emme tiedä.

Suuret listat

RecyclerViews-kohteita käytetään, kun haluamme esitellä käyttäjälle todella suuren joukon Views-kohteita, mutta samalla ei tyhjentää RAM laitteellamme jokaiselle luodun näkymän esiintymälle.

Jos otamme esimerkiksi yhteystietoluettelon, meillä olisi yleinen käsitys siitä, miltä yksi yhteystieto luettelossa näyttäisi. Tämän jälkeen loisimme mallipohjan, joka on itse asiassa näkymä, jossa on paikkoja, joihin yhteystietoluettelomme tiedot täytetään. Seuraava on pseudokoodi, joka selittää koko tarkoituksen:

//OneContactView
<OneContact>
<TextView>{{PlaceHolderForName}}</TextView>
<TextView>{{PlaceHolderForAddress}}</TextView>
<ImageView>{{PlaceHolderForProfilePicture}}</ImageView>
<TextView>{{PlaceHolderForPhoneNumber}}</TextView>
</OneContact>

Meillä olisi sitten tämänkaltainen yhteystietoluettelo:

<ContactList>
</ContactList>

Jos koodaisimme sisällön kovakoodauksella, meillä ei olisi ohjelmallista tapaa lisätä uutta sisältöä listaan ​​ilman sovelluksen uudelleenkirjoittamista. Onneksi meille View-ryhmän lisäämistä tukee addView(view:View) menetelmä. Silti se ei ole tapa, jolla RecyclerView lisää lapsinäkymiä siihen.

Käyttötapauksessamme meillä olisi pitkä luettelo yhteystiedoista. Jokaista luettelossa olevaa yhteystietoa varten meidän pitäisi luoda OneContactView ja täyttää View-näkymän tiedot vastaamaan Contact-luokkamme kenttiä. Kun näkymä on valmis, meidän pitäisi lisätä se RecyclerView-näkymään 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 yhteystietotaulukko. OneContactView sisältää paikkoja, joihin voi ottaa Contact-luokan sisällön ja näyttää sen. RecyclerView'ssa meidän on lisättävä siihen Views-objekteja, jotta se voi auttaa meitä kierrätyskyvyssään.

RecyclerView ei oikeastaan ​​salli näkymän lisäämistä, mutta se mahdollistaa ViewHolderin lisäämisen. Tässä skenaariossa meillä on kaksi osaa, jotka haluamme yhdistää, mutta jotka eivät sovi yhteen. Tässä kohtaa sovitin tulee mukaan kuvaan. RecyclerView tarjoaa meille sovittimen, aivan kuten AmericanToBritishChargerAdapter() aiemmasta, mikä mahdollisti sen, että pystyimme muuttamaan AmericanCharger-laturimme, jota ei voinut käyttää BritishDevice-laitteemme kanssa, käyttökelpoiseksi, käytännössä virtalähteen kaltaiseksi.

Tässä skenaariossa sovitin ottaisi yhteystietotaulukomme ja View-näkymämme ja luoisi niistä ViewHolder-objektit, jotka RecyclerView on valmis hyväksymään.

RecyclerView tarjoaa rajapinnan, jota voimme laajentaa luomaan oman sovittimen RecyclerView.Adapter-luokan avulla. Tämän sovittimen sisällä on tapa luoda ViewHolder-luokka, jonka kanssa RecyclerView haluaa työskennellä. Tilanne on siis sama kuin aiemmin, mutta yhdellä lisäjutulla, sovittimella.

Meillä on yhteystietotaulukko, näkymä yhden yhteystiedon näyttämiseksi (OneContactView) ja RecyclerView, joka on luettelo näkymistä, jotka tarjoavat kierrätyspalveluita, mutta on halukas ottamaan vastaan ​​vain ViewHolder-objekteja. Tässä skenaariossa meillä on nyt RecyclerView.Adapter-luokka, jonka sisällä on metodi ViewHolder-objektien luomiseksi.

fun createViewHolder(@NonNull parent: ViewGroup, viewType: Int): ViewHolder

RecyclerView.ViewHolder on vatsalihas.tract-luokka, joka ottaa View-arvomme argumenttina ja muuntaa sen ViewHolder-objektiksi. Se käyttää käärekuviota, jota käytetään luokkien ominaisuuksien 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 haluaisimme lisätä metodin, joka saa ne puhumaan, mutta kirjaston tekijä ei olisi hauska, voisimme silti löytää keinon. Tarvitsisimme kääreen Animal-luokallemme. Tekisimme tämän ottamalla Animalin luokan rakentajaksi.

class SpeechPoweredAnimalByWrapper(var myAnimal:Animal){

    fun sound(){
        myAnimal.sound()
    }

    fun speak(){
        println("Hello, my name is ${myAnimal.name}")
    }
}

Nyt voimme välittää eläininstanssin SpeechPoweredAnimalByWrapperille. Sen sound()-metodin kutsuminen kutsuisi jo annettua animal sound()-metodia. Meillä on myös ylimääräinen speak()-metodi, joka lasketaan uudeksi toiminnallisuudeksi, jonka lisäämme annettuihin 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ä niihin toiminnallisuutta. Tarvitsemme vain luokkainstanssin ja uudet wrappimme määrittelemät metodit.ping luokka.

Yllä olevassa tapauksessa käytimme betoniluokkaa. Saman voi toteuttaa myös abs-funktiossatract-luokka. Meidän pitäisi muuttaa SpeechPoweredAnimalByWrapper-luokka muotoon abstract, ja olemme valmiita. Muutamme luokan nimen lyhyemmäksi, jotta se olisi luettavampi.

abstract class SpeechPowered(var myAnimal:Animal){

    fun sound(){
        myAnimal.sound()
    }

    fun speak(){
        println("Hello, my name is ${myAnimal.name}")
    }
}

Se on sama kuin ennen, mutta se merkitsisi jotain muuta. Normaalissa luokassa meillä voi olla luokan instanssi samalla tavalla kuin loimme cat1:n ja dog1:n. Abstract-luokkia ei kuitenkaan ole tarkoitettu instanssien luomiseen, vaan niiden tarkoituksena on laajentaa muita luokkia. Joten miten käyttäisimme uutta SpeechPowered(var myAnimal:Animal) abs -funktiota?tract-luokka? Voimme käyttää sitä luomalla uusia luokkia, jotka laajentavat sitä ja saavat samalla sen toiminnallisuuden.

Esimerkissämme luomme SpeechPoweredAnimal-luokan, 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 kaava, jota käytetään ViewHolderissa. RecyclerView.ViewHolder-luokka on abs-luokka.tract-luokka, joka lisää View'hun toimintoja, aivan kuten lisäsimme Animals-metodille speak-metodin. Lisätty toiminnallisuus tekee siitä toimivan RecyclerView'n kanssa.

Näin loisimme OneContactViewHolder-objektin OneContactView'sta:

//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ää yhteystietomatriisin ContactsView'hun RecyclerView'n avulla.

Näkymän lisääminen

ViewGroup ei piirrä uudelleen automaattisesti, vaan noudattaa tiettyä aikataulua. Laitteellasi se voi piirtää uudelleen 10 ms:n tai 100 ms:n välein; tai jos valitsemme järjettömän luvun, esimerkiksi minuutin, kun lisäämme näkymän ViewGroupiin, näet muutokset minuutin kuluttua, kun ViewGroup "päivittyy".

RecyclerView.Recycler

Pohjatyö #3 – Välimuisti

Yksi parhaista esimerkeistä säännöllisistä päivityksistä on selain. Kuvitellaan esimerkiksi, että vierailemamme sivusto on staattinen eikä lähetä sisältöä dynaamisesti; meidän pitäisi päivittää sivua jatkuvasti nähdäksemme muutokset.

Kuvitellaanpa tässä esimerkissä, että kyseinen sivusto on Twitter. Meillä olisi listattu joukko staattisia twiittejä, ja ainoa tapa nähdä uudet twiitit olisi napsauttaa päivityspainiketta sisällön hakemiseksi uudelleen.

Koko näytön uudelleenmaalaaminen on tietenkin kallista. Kuvittele, että meillä olisi rajoitettu kaistanleveys puhelinoperaattorimme kanssa ja twiittilistassamme olisi paljon kuvia ja videoita; sivun koko sisällön lataaminen uudelleen jokaisella päivityksellä olisi kallista.

Tarvitsisimme tavan tallentaa jo ladatut twiitit ja varmistaa, että seuraava pyyntömme pystyy kertomaan, mitkä twiitit sillä jo on. Näin ollen se ei lataa kaikkea uudelleen, vaan hakee vain uudet twiitit ja tarkistaa myös, onko jokin paikallisesti tallennettu twiitti poistunut, jotta se voi poistaa sen paikallisesti. Tätä kutsutaan välimuistiksi.

Tietoja, joita lähetämme verkkosivustolle sisällöstämme, kutsutaan metatiedoksi. Emme siis käytännössä sano vain "haluamme ladata sivustosi", vaan "haluamme ladata sivustosi, ja tässä on osa sisällöstä, jonka olemme jo tallentaneet viimeisimmästä latauskerrasta lähtien. Käytä sitä vain sellaisen sisällön lähettämiseen, jota siellä ei ole, jotta emme käytä paljon kaistanleveyttä".

Ulkoasukutsut – Tweet-luettelon täytyy olla hullu

Esimerkki asettelukutsuista on scrollToPosition. Tämä on yleinen esimerkki esimerkiksi keskustelusovelluksissa. Jos joku keskusteluketjussa vastaa aiempaan keskustelukuplaan, jotkut keskustelusovellukset lisäävät vastauksen ja linkin keskustelukuplaan, jota napsauttamalla pääset takaisin alkuperäiseen viestiisi.

Siinä tapauksessa, että kutsumme tätä metodia ennen kuin LayoutManager on lisätty RecyclerView-objektiin ja ennen kuin meillä on RecyclerView.Adapter-objekti, scrollToPosition(n:Int) yksinkertaisesti jätetään huomiotta.

Viestintä RecyclerView-komponenttien välillä

Pohjatyö #4 – Takaisinkutsut

RecyclerView'lla on työssään paljon liikkuvia osia. Sen on käsiteltävä LayoutManageria, joka kertoo meille, miten näkymät järjestetään joko lineaarisesti tai ruudukkoon. Sen on käsiteltävä sovitinta, joka muuntaa alkiot (contactList) näkymiksi (OneContactView) ja sitten näkymänpitäjiksi (OneContactViewHolder), joiden kanssa RecyclerView on valmis työskentelemään tarjoamiensa metodien avulla.

RecyclerView'n raaka-aineena toimivat View'mme, esim. OneContactView, ja datalähde.

contactList:Array<Contact>

Olemme käyttäneet lähtökohtana yksinkertaista skenaariota saadaksemme käsityksen siitä, mitä RecyclerView pyrkii saavuttamaan. Perustapaus, jossa meillä on staattinen 1000 yhteystiedon taulukko, jonka haluamme näyttää käyttäjälle, on helppo ymmärtää. RecyclerView'n koneisto alkaa todella toimia, kun lista ei ole enää staattinen. Dynaamisen listan kanssa meidän on mietittävä, mitä näytöllä olevalle näkymälle tapahtuu, kun lisäämme kohteen listaan ​​tai poistamme kohteen listasta.

RecyclerView.LayoutManager

Sen lisäksi, että LayoutManager päättää, miten näkymät asetetaan, joko lineaarisesti tai ruudukkona, se tekee paljon työtä konepellin alla.ping Kierrättäjä tietää, milloin kierrätys on tehtävä.

Se on vastuussa keestäping track näytöllä kulloinkin näkyvistä näkymistä ja välittää nämä tiedot kierrätysmekanismille. Kun käyttäjä vierittää alaspäin, LayoutManager vastaa kierrätysjärjestelmän tiedottamisesta ylhäällä tarkentuvista näkymistä, jotta niitä voidaan käyttää uudelleen sen sijaan, että ne jäisivät sinne ja kuluttaisivat muistia tai tuhoutuisivat vain luodakseen uusia.

Tämä tarkoittaa, että LayoutManagerin on pidettävä track siitä, missä käyttäjä on, kun hän vierittää listaamme. Se tekee tämän käyttämällä indeksipohjaista listaa sijainneista, eli ensimmäinen kohde alkaa nollasta ja kasvaa vastaamaan listan kohteiden määrää.

Jos voimme tarkastella 10 kohdetta listallamme, jossa on esimerkiksi 100 kohdetta, LayoutManager tietää alussa, että sillä on tarkennuspisteenä View-0 ja View-9. Vierittäessämme LayoutManager pystyy laskemaan näkymät, jotka menevät epätarkasti.

LayoutManager pystyy vapauttamaan nämä näkymät kierrätysmekanismiin, jotta niitä voidaan käyttää uudelleen (niihin voidaan sitoa uutta dataa; esim. näkymän yhteystiedot voidaan poistaa ja seuraavan segmentin uudet yhteystiedot voivat korvata paikkamerkit).

Tämä olisi hyvä tapaus, jos listamme on staattinen, mutta yksi yleisimmistä RecyclerView'n käyttötapauksista on dynaamiset listat, joissa data voi tulla online-päätepisteestä tai jopa anturista. Dataa ei ainoastaan ​​lisätä, vaan listallamme olevaa dataa voidaan joskus poistaa tai päivittää.

Datamme dynaaminen tila voi tehdä LayoutManagerista päättelyn erittäin vaikeaksi. Tästä syystä LayoutManager ylläpitää omaa listaansa kohteista ja positioista erillään kierrätyskomponentin käyttämästä listasta. Tämä varmistaa, että se suorittaa asettelutehtävänsä oikein.

Samaan aikaan RecyclerView'n LayoutManager ei halua vääristellä hallussaan olevaa dataa. Jotta LayoutManager toimisi oikein, se synkronoituu RecyclerView.Adapterin kanssa tietyin väliajoin (60 ms) ja jakaa tietoa listakohteistamme, eli lisätyistä, päivitetyistä, poistetuista tai paikasta toiseen siirretyistä kohteista. Saatuaan nämä tiedot LayoutManager järjestää näytön sisällön uudelleen vastaamaan muutoksia tarvittaessa.

Monet RecyclerView'n ydintoiminnoista liittyvät RecyclerView.LayoutManager- ja RecyclerView.Adapter-tiedostojen väliseen kommunikointiin. Adapter tallentaa luettelomme joskus staattisista tai joskus dynaamisista tiedoista.

Lisäksi RecyclerView tarjoaa meille metodeja, joita voimme käyttää tapahtumien kuuntelemiseen, kuten onBindViewHolder, kun RecyclerView.Adapter sitoo listamme sisällön (esim. yhteystiedon) ViewHolderiin, jotta sitä käytetään tietojen näyttämiseen näytöllä.

Toinen on onCreateViewHolder, joka kertoo meille, milloin RecyclerView.Adapter ottaa tavallisen View-näkymän, kuten OneContactView-näkymän, ja muuntaa sen ViewHolder-kohteeksi, jonka kanssa RecyclerView voi työskennellä.

Kierrätyksen mahdollistavien ydinmekanismien lisäksi RecyclerView tarjoaa tapoja mukauttaa toimintaa vaikuttamatta kierrätykseen. Näkymien uudelleenkäyttö vaikeuttaa yleisiä asioita, joihin olemme tottuneet staattisilla näkymillä, kuten reagointia onClick-tapahtumiin.

Kuten tiedämme, RecyclerView.LayoutManager joka esittelee näkymät käyttäjälle, saattaa hetkeksi olla eri luettelo kohteista kuin RecyclerView.Adapter , jonka luettelo on tallennettuna tietokantaan tai suoratoistettuna lähteestä. OnClick-tapahtumien lisääminen suoraan Views-objektiin voi johtaa odottamattomaan toimintaan, kuten väärän yhteystiedon poistamiseen.

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 mukaan uusin versio.

Lisättyään riippuvuuden meidän Gradle tiedosto, saamme kehotteen Android Studio synkronoidaksesi muutokset. Näin meidän Gradle tiedosto huolehtii RecyclerView-näkymän lisäämisestä tyhjään projektiin käyttäen vain oletusarvoja.

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 hedelmien nimien luettelon näytöllä.

Luettelo tuotteista

Siirrymme MainActivity-tiedostoomme ja luomme hedelmien nimiä sisältävän taulukon juuri ennen asennuksen aikana luotua onCreate()-metodia.

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ä lista ruudulla RecyclerView'n avulla. Tätä varten siirrymme asettelujamme sisältävään layout-hakemistoon 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'hun olemme lisänneet id-kentän, jota käytetään näkymän tunnistamiseen. Sitä ei luoda oletusarvoisesti. Olemme antaneet TextView'lle id-fruitName-tunnuksen, joka vastaa siihen sidottavaa dataa.

RecyclerView'n lisääminen pääasetteluun

Samassa aktiviteetissa on main_layout.xml-asettelutiedosto, joka luotiin meille oletuksena. Jos valitsisimme tyhjän projektin, se olisi luonut XML sisältää ConstraintLayout-näkymän ja sen sisällä on TextView-näkymä, jossa on teksti ”Hello”. Poistamme kaiken sisällön ja annamme asettelun sisältää vain RecyclerView-näkymän seuraavasti:

<?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-objektille id-attribuutin, jota käytämme siihen viittaamiseen koodissamme.

android:id="@+id/fruitRecyclerView"

Palaamme sitten MainActivity-tiedostoomme. Luomiemme tunnisteiden avulla voimme viitata juuri luomiimme näkymiin. Aloitamme viittaamalla RecyclerView-näkymään käyttämällä findViewById()-metodia, jonka tarjoaa AndroidTeemme tämän onCreate()-metodillamme. OnCreate()-metodimme 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.ViewHolder-metodin, joka muuntaa View-objektimme ViewHolder-objektiksi, jota RecyclerView käyttää objektiemme näyttämiseen. Teemme tämän heti onCreate()-metodin 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 vastaa seuraavista asioista: se ottaa hedelmien nimet hedelmätaulukosta, luo ViewHolder-objektin käyttämällä one_fruit_view.xml-näkymäämme, sitoo sitten hedelmän ViewHolder-objektiin ja sitoo sisällön dynaamisesti luomaamme View-objektiin.

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 ​​viivoja FruitArrayAdapter-objektiimme, mikä kertoo, että meidän on toteutettava metodit, joita RecyclerView voi käyttää taulukon yhdistämiseen ViewHolder-objektiin.

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 osasta, getItemCount()-metodista. Tiedämme, miten taulukon alkioiden lukumäärä saadaan kutsumalla taulukon koko -ominaisuutta.

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 toteutamme onCreateViewHolder-metodin. Tässä RecyclerView pyytää meitä auttamaan FruitViewHolder-luokituksen luomisessa. Jos muistamme, FruitViewHolder-luokkamme näytti tältä:

class FruitViewHolder(fruitView: View): RecyclerView.ViewHolder(fruitView)

Se vaatii fruitView-näkymän, jonka loimme XML-tiedostona one_fruit_view.xml. Voimme luoda viittauksen tähän XML-tiedostoon ja muuntaa sen View-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äljelle jäävä bitti on onBindViewHolder-ohituksen arvo.

fun onBindViewHolder(holder: FruitViewHolder, position: Int)

RecyclerView.Adapter pyytää position kokonaislukua, jota käytämme hakemaan alkion listaltamme. Se antaa meille myös pidikkeen, jotta voimme sitoa fruitArray-luokasta saamamme alkion näkymäpidikkeen sisällä olevaan näkymään (View). ViewHolder-elementin sisällä olevaan näkymään pääsee käsiksi ViewHolder.itemView-kentän kautta. Kun olemme saaneet näkymän, voimme käyttää aiemmin luomaamme fruitName-tunnusta 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)
      }

Tämän jälkeen FruitArrayAdapter 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 kytkemään RecyclerView'n jäljellä olevat osat. Ne luovat LayoutManagerin, joka kertoo RecyclerView'lle, miten listan sisältö näytetään: lineaarisesti LinearLayoutManagerilla vai ruudukossa GridLayoutManagerilla tai StaggeredGridLayoutManagerilla.

Luo Layout Manager

Palaamme takaisin onCreate-funktioomme 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-instanssin ja syöttäneet siihen hedelmien nimitaulukon. Ja periaatteessa kaikki on valmista. Koko 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)
        }
    }
}

Lataa projekti

UKK

RecyclerView on ListView'n joustavampi ja tehokkaampi seuraaja. Se käyttää ViewHolder-kuviota, tukee lineaarisia, ruudukkoisia ja porrastettuja asetteluja asettelunhallinnan kautta ja lisää sisäänrakennettuja animaatioita, kun taas ListView ei tarjoa mitään näistä oletuksena.

GridView näyttää kohteet vain kiinteässä ruudukossa. RecyclerView voi luoda ruudukon GridLayoutManagerin avulla, mutta se tukee myös lineaarisia ja porrastettuja asetteluja, näkymien kierrätystä ja animaatioita, mikä tekee siitä paljon monipuolisemman kuin GridView.

Tämä RecyclerView-esimerkki on kirjoitettu Kotlinilla, kielellä, jota virallisesti suosittelee Google varten Android kehityksessä. Samat RecyclerView-, sovitin- ja asettelunhallinta-APIt toimivat myös Java.

Liitä klikkauskuuntelija ViewHolderin tai onBindViewHolderin sisään ja paljasta se sitten rajapinnan takaisinkutsulla aktiviteettiin tai fragmenttiin. ViewHolder-sijainnin käyttäminen estää klikkausten sitomisen väärään kierrätettyyn kohteeseen.

DiffUtil on apuohjelmaluokka, joka laskee kahden listan välisen eron ja lähettää sovittimelle mahdollisimman pienen joukon päivityksiä. Se tehostaa dynaamisia listamuutoksia ja animoi lisäyksiä, poistoja ja siirtoja automaattisesti.

Jetpack Compose käyttää LazyColumnia ja LazyRow'ta RecyclerView'n sijaan uusissa näytöissä. RecyclerView'ta käytetään kuitenkin edelleen laajalti olemassa olevissa View-pohjaisissa projekteissa, ja se voidaan upottaa Composeen yhteentoimivuusrajapintojen kautta.

Kyllä. Tekoälyavustajat, kuten GitHub Copilot ja GPT-pohjaiset työkalut, voivat lyhyellä kehotteella luoda RecyclerView-sovittimen, ViewHolderin ja onBindViewHolder-mallin. Kehittäjien tulisi silti tarkistaa luodun koodin oikeellisuus ja suorituskyky.

Koneen oppiminen Android Sovellukset käyttävät RecyclerView'ta dynaamisten tulosteiden, kuten suositusten, hakutulosten sijoitusten tai kuvien luokittelujen, renderöintiin. Sovitin sitoo mallin tulokset luettelokohtiin, jotta suuret, usein muuttuvat tietojoukot vierivät sujuvasti.

Tiivistä tämä viesti seuraavasti: