Android RecyclerView: Mi az, tanuljon egyszerű példákkal

⚡ Okos összefoglaló

A RecyclerView egy fejlett Android Egy olyan widget, amely nagyméretű, görgethető adathalmazokat jelenít meg hatékonyan korlátozott számú nézet újrahasznosításával. Egy adapterre, egy elrendezéskezelőre és egy nézettárolóra támaszkodik a futásidőben változó adatgyűjtemények összekapcsolásához.

  • 📱 Ami: A RecyclerView a ListView és a GridView rugalmas utódja nagy adatgyűjtemények megjelenítéséhez.
  • 🧱 Nézetek és nézetcsoportok: Android A felületet a View és a ViewGroup osztályok segítségével rajzolja meg, a LayoutParams pedig a méretet és a pozíciót szabályozza.
  • 🔄 Adapter: Az adapterminta egy adattömböt ViewHolderekké alakít át, amelyeket a RecyclerView el tud fogadni.
  • 📐 Elrendezéskezelő: Az elrendezéskezelő lineárisan vagy rácsban rendezi el az elemeket, és jelzi, ha a nézetek újrahasznosíthatók.
  • ♻️ Újrafeldolgozás: A képernyőn kívüli nézeteket újra felhasználja a rendszer ahelyett, hogy újra létrehozná, ami memóriát takarít meg és a görgetést simán biztosítja.
  • 🇧🇷 Építési lépések: Adjuk hozzá a Gradle függőség, hozzon létre egy elemelrendezést, egy ViewHolder-t, egy adaptert és egy LayoutManager-t.

Android RecyclerView

Miben található a RecyclerView Android?

Az RecyclerView a GridView és a ListView rugalmasabb és fejlettebb változata. Ez egy nagy adathalmazok megjelenítésére szolgáló tároló, amely hatékonyan görgethető a korlátozott számú nézet fenntartásával. A RecyclerView widgetet akkor használhatod, ha olyan adatgyűjteményeid vannak, amelyek elemei futásidőben változnak egy hálózati esemény vagy felhasználói művelet függvényében.

Nézetek

Az Android A platform a View és a ViewGroup osztályokat használja az elemek képernyőn való megjelenítéséhez. Ezek az osztályok abszolúttracés kiterjeszthetők különböző implementációkra, hogy megfeleljenek egy használati esetnek. A TextView például egyszerűen a szöveges tartalom megjelenítését végzi a képernyőn. Az EditText ugyanebből a View osztályból származik, és további funkciókat kínál, amelyek lehetővé teszik a felhasználó számára az adatok bevitelét.

Lehetőség van saját egyéni nézetek létrehozására a nagyobb rugalmasság érdekében a fejlesztés során.ping felhasználói felületek. A View osztály metódusokat biztosít, amelyeket felülbírálhatunk a képernyőre való rajzoláshoz, valamint egy eszközt paraméterek, például szélesség, magasság és saját egyéni attribútumok átadására, amelyeket hozzáadhatunk a View osztályhoz, hogy az a kívánt módon viselkedjen.

ViewGroups

A ViewGroup osztály egyfajta View osztály, de az egyszerű View osztállyal ellentétben, amelynek feladata pusztán a megjelenítés, a ViewGroup lehetővé teszi számunkra, hogy több nézetet egyetlen nézetbe helyezzünk, amelyre egészként hivatkozhatunk. Ebben az esetben a legfelső szinten létrehozott View-t, amelyhez más egyszerű nézeteket adunk hozzá, „szülőnek” nevezzük, a benne hozzáadott nézetek pedig „gyermekeknek”.

Elképzelhetünk egy nézetet (View) tömbként (Array), és egy nézetcsoportot (ViewGroup) tömbök tömbjeként (Array). Tekintettel arra, hogy egy tömbök tömbje maga is egy tömb, láthatjuk, hogyan kezelhető egy nézetcsoport (ViewGroup) nézetként (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

A ViewGroup lehetővé teszi számunkra, hogy meghatározzuk a gyermekek nézeten belüli elrendezését, például, hogy függőlegesen vagy vízszintesen vannak-e elrendezve. Különböző szabályokat alkalmazhatunk a nézeten belüli interakcióra. Például az egymást követő TextView elemeknek 12 dp távolsággal kell rendelkezniük egymástól, míg egy TextView által követett ImageView távolságának 5 dp-nek kell lennie.

Ez lenne a helyzet, ha fejlődnénkping saját ViewGroup-ot a nulláról. A konfigurációk megkönnyítése érdekében Android biztosít egy LayoutParams nevű osztályt, amelyet ezen konfigurációk bevitelére használhatunk.

Az Android dokumentáció néhány alapértelmezett paramétert biztosít, amelyeket a saját ViewGroup konfigurálásakor alkalmaznánk. Néhány gyakori paraméter a szélességhez, magassághoz és margóhoz kapcsolódik. Alapértelmezés szerint ezek a konfigurációk az android:layout_height struktúrával rendelkeznek a magassághoz és az android:layout_width struktúrával a szélességhez. Ebben a tekintetben a ViewGroup létrehozásakor további LayoutParams paramétereket hozhat létre, amelyek a ViewGroup kívánt viselkedéséhez igazodnak.

Android alapértelmezett nézetekkel és nézetcsoportokkal rendelkezik, amelyekkel számos gyakori feladatot elvégezhetünk. Egy példa erre a TextView. Ez egy egyszerű nézet, amely konfigurálható tulajdonságokkal rendelkezik, mint például a magasság, a szélesség és a szövegméret. Van egy ImageView is a képek megjelenítéséhez, és ahogy említettük, az EditText is, sok más mellett. Android egyedi ViewGroupokkal is rendelkezik, amelyekhez hozzáadhatjuk nézeteinket, és elérhetjük a várt viselkedést.

Lineáris elrendezés

A LinearLayout lehetővé teszi számunkra, hogy View elemeket adjunk hozzá. A LinearLayout rendelkezik egy orientation attribútummal, amely meghatározza, hogyan legyen elrendezve a képernyőn. Emellett rendelkezik LinearLayout.LayoutParams attribútummal, amely a benne lévő nézetekre vonatkozó szabályokat határozza meg. Például az android:center_horizontal attribútum a nézeteket a vízszintes tengely mentén középre igazítaná, míg az android:center_vertical a nézet tartalmát a függőleges tengely mentén középre igazítaná.

Íme néhány kép, amelyek segítenek megérteni a középre igazítást. Ezt egy egyszerű TextView-nek tekintjük egy 200 x 200 képpontos területen belül; a középre igazítás attribútumok a következőképpen viselkednének.

android:center_horizontal

Vízszintesen középre helyezett tartalom

Vízszintesen középre helyezett tartalom

android:center_vertical

Függőlegesen középre igazított tartalom

Függőlegesen középre igazított tartalom

android:center

Középre helyezett tartalom

Középre helyezett tartalom

A RecyclerView fő összetevői

A RecyclerView fő összetevői

A RecyclerView fő összetevői

A RecyclerView fontos összetevői a következők:

RecyclerView.Adapter

1. alapvetés – Adapterminta

Az adapter egy olyan eszköz, amely egy rendszer vagy eszköz tulajdonságait egy egyébként inkompatibilis eszköz vagy rendszer tulajdonságaivá alakítja. Egyes adapterek módosítják a jel- vagy tápellátási tulajdonságokat, míg mások egyszerűen csak egy csatlakozó fizikai formáját adaptálják a másikhoz.

Egy egyszerű, valós példa az adapter használatára az, amikor eszközöket kell összekapcsolnunk, de a csatlakozóik nem illenek egymáshoz. Ez előfordulhat, amikor egy másik országban járunk, ahol más típusú aljzatokat használnak. Ha magunkkal visszük a telefonunk vagy laptopunk töltőjét, lehetetlen lenne csatlakoztatni a konnektorhoz. Azonban nem adnánk fel, hanem egyszerűen vennénk egy adaptert, amely a konnektor és a töltő közé kerül, és lehetővé teszi a töltést.

Ez a helyzet áll fenn a programozásban, amikor két adatszerkezetet össze szeretnénk kapcsolni egy feladat végrehajtásához, de az alapértelmezett portjaik nem tudnak kommunikálni egymással.

Egy egyszerű eszköz és egy töltő példáját fogjuk használni. Két töltőt fogunk használni, egy amerikait és egy britet.

class AmericanCharger() {
    var chargingPower = 10
}
class BritishCharger(){
    var chargingPower = 5
}

Ezután két eszközt fogunk létrehozni.

class AmericanDevice()
class BritishDevice()

Példaként ezután létrehozhatunk néhány példányt az eszközökből, hogy együtt játszhassunk.

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

Ezután bevezetjük a töltés koncepcióját mindkét eszköz esetében egy charge() nevű metódus hozzáadásával az eszközökhöz. A metódus fogadja a megfelelő töltőt, és annak alapján végzi el a töltést.

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
    }
}

Ebben az esetben, analógiánk alapján, valamilyen okból szükségünk lenne egy BritishCharger töltő használatára, amikor egy AmericanDevice-t használunk, vagy fordítva.

A programozási világban ez általában olyan könyvtárak keverésekor történik, amelyek ugyanazt a funkcionalitást kínálják (a mi kontextusunkban a megosztott funkciónk töltés). Meg kell találnunk a módját ennek lehetővé tételére.

Ha követjük az analógiát, akkor el kell mennünk egy elektronikai boltba, és veszünk egy adaptert, amely lehetővé teszi számunkra, hogy amerikai eszközöket töltsünk, ha van brit töltőnk. Programozási szempontból mi leszünk az adapter gyártója.

Létrehozunk egy adaptert, amely pontosan megegyezik azzal a mintával, amelyre a másik létrehozásához szükségünk lenne. Az alábbiak szerint osztályként fogjuk megvalósítani. Nem feltétlenül kell osztálynak lennie, lehet egy függvény, amely kiemeli, hogy mit csinál az adapter minta általában. Az osztályt azért használjuk, mert az a legtöbb használathoz illeszkedik a következőn: 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
    }
}

A programozás világában a foglalatok közötti különbség analóg a töltési módszerek közötti különbséggel. A különböző módszerekkel rendelkező töltők lehetetlenné tennék a töltők használatát.

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

A charge() metódus meghívása a myBritishDevice-en az americanChargerIFound függvénnyel nem működik, mivel az AmericanDevice csak AmericanCharger azonosítót fogad el. Tehát ezt lehetetlen megtenni:

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

Ebben a forgatókönyvben az általunk létrehozott adapter, az AmericanToBritishChargerAdapter, most jól jöhet. A returnNewCharger() metódussal létrehozhatunk egy új BritishCharger adaptert, amelyet töltésre használhatunk. Csak létre kell hoznunk az adapterünk egy példányát, és meg kell adnunk neki a meglévő AmericanCharger adaptert, ami létrehoz egy BritishCharger adaptert, amit használhatunk.

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

Amikor egy ViewGroup-pal foglalkozunk, a View-ket (nézeteket) helyezzük el benne. A LayoutManager (elrendezéskezelő) feladata, hogy leírja, hogyan helyezkednek el a nézetek belül.

Összehasonlításképpen, amikor egy LinearLayout ViewGroup-pal dolgozunk, a kívánt használati eset az elemek függőleges vagy vízszintes elhelyezésének lehetősége. Ez könnyen megvalósítható egy orientation attribútum hozzáadásával, amely megmondja, hogyan kerüljön elhelyezésre a LinearLayout a képernyőn. Ezt a következővel tehetjük meg: android:orientation=VERTICAL|HORIZONTAL tulajdonság.

Létezik egy másik ViewGroup is, a GridLayout. Használati esete az, amikor nézeteket szeretnénk elhelyezni egy téglalap alakú rácsszerkezetben. Ennek oka lehet például az, hogy az alkalmazásfelhasználónak megjelenített adatokat könnyen feldolgozhatóvá tegyük. A GridLayout kialakításából adódóan olyan konfigurációkat tesz lehetővé, amelyek segítenek elérni ezt a célt a rács méreteinek meghatározásával; például lehet egy 4×4-es vagy egy 3×2-es rácsunk.

RecyclerView.ViewHolder

A ViewHolder egy hasizmot ábrázoltract osztály, amelyet szintén a RecyclerView-ból bővítünk ki. A ViewHolder általános metódusokat biztosít, amelyek segítenek hivatkozni egy RecyclerView-ba helyezett nézetre, még azután is, hogy a RecyclerView újrahasznosító mechanizmusa számos olyan hivatkozást megváltoztatott, amelyekről nem tudunk.

Nagy listák

A RecyclerView-kat akkor használjuk, ha egy igazán nagyszámú nézetet szeretnénk megjeleníteni a felhasználó számára, miközben nem merítjük ki a rendelkezésre álló lehetőségeket. RAM eszközünkön a létrehozott nézet minden egyes példányához.

Ha egy névjegyzék példáját vesszük, akkor általános elképzelésünk lenne arról, hogy egy névjegy hogyan nézne ki a listában. Ezután létrehoznánk egy sablonelrendezést, ami valójában egy nézet, olyan résekkel, amelyeket a névjegyzékünkből származó különféle adatok töltenek ki. A következő pszeudo kód elmagyarázza a teljes célt:

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

Ezután egy ilyen jellegű kapcsolati listánk lenne:

<ContactList>
</ContactList>

Ha fixen kódolnánk a tartalmat, akkor nem lenne programozott módunk új tartalom hozzáadására a listához az alkalmazás átírása nélkül. Szerencsére számunkra egy nézet hozzáadását egy nézetcsoporthoz egy addView(view:View) metódus. Ennek ellenére a RecyclerView nem így kapja meg a gyermeknézeteket.

A mi esetünkben egy hosszú névjegylistánk lenne. A listában szereplő minden egyes névjegyhez létre kellene hoznunk egy OneContactView nézetet, és a View nézetben lévő adatokat úgy kellene kitöltenünk, hogy azok megfeleljenek a Contact osztályunk mezőinek. Ezután, ha elkészült a nézet, hozzá kell adnunk a RecyclerView nézethez a lista megjelenítéséhez.

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)

Van egy névjegyekből álló tömbünk. A OneContactView olyan mezőket tartalmaz, amelyek a Contact osztály tartalmának megjelenítésére szolgálnak. A RecyclerView-ban View-kat kell hozzáadnunk, hogy segítsen az újrahasznosítási képességében.

A RecyclerView valójában nem teszi lehetővé nézet hozzáadását, de lehetővé teszi egy ViewHolder hozzáadását. Tehát ebben a forgatókönyvben két darabot szeretnénk összekapcsolni, de ezek nem egyeznek. Itt jön képbe az adapterünk. A RecyclerView egy adaptert biztosít számunkra, hasonlóan a miénkhez. AmericanToBritishChargerAdapter() a korábbiakból, ami lehetővé tette számunkra, hogy az AmericanCharger töltőnket, amely használhatatlan volt a BritishDevice-ünkkel, valami használhatóvá alakítsuk, hasonlóan egy valós életben használt hálózati adapterhez.

Ebben a forgatókönyvben az illesztő a névjegyek tömbjét és a nézetünket venné, és onnan generálna olyan ViewHoldereket, amelyeket a RecyclerView hajlandó elfogadni.

A RecyclerView egy olyan felületet biztosít, amelyet a RecyclerView.Adapter osztályon keresztül bővíthetünk ki az adapter létrehozásához. Ezen az adapteren belül létrehozhatjuk a ViewHolder osztályt, amellyel a RecyclerView dolgozni szeretne. Tehát ugyanaz a helyzet áll fenn, mint korábban, de egy plusz dologgal, az adapterrel.

Van egy névjegyekből álló tömbünk, egy nézettel, amely egyetlen névjegyet jelenít meg (OneContactView), és egy RecyclerView-val, amely egy olyan nézetlista, amely újrahasznosítási szolgáltatásokat nyújt, de csak ViewHoldereket hajlandó fogadni. Ebben a forgatókönyvben most van egy RecyclerView.Adapter osztályunk, amelynek van egy metódusa ViewHolderek létrehozására.

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

A RecyclerView.ViewHolder egy hasizmot tartalmaz.tracEgy t osztály, ami argumentumként a View objektet veszi, és ViewHolder objektummá alakítja. Az osztályok képességeinek kiterjesztésére használt wrapper mintát használja.

2. alapvetés – Burkolatminta

Egy egyszerű példával fogjuk bemutatni, hogyan tudjuk megszólaltatni az állatokat.

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

A fenti példában két állatunk van. Ha véletlenül hozzá szeretnénk adni egy metódust, amivel megszólaltathatjuk őket, de a könyvtár szerzője nem lenne jó, akkor is találhatnánk rá megoldást. Amire szükségünk lenne, az egy wrapper az Animal osztályunkhoz. Ezt úgy tehetjük meg, hogy az Animal-t konstruktorként használjuk az osztályunk számára.

class SpeechPoweredAnimalByWrapper(var myAnimal:Animal){

    fun sound(){
        myAnimal.sound()
    }

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

Most átadhatunk egy állatpéldányt a SpeechPoweredAnimalByWrappernek. A sound() metódus meghívása meghívná az átadott állat sound() metódust. Van egy további speak() metódusunk is, amely új funkciónak számít, amelyet az átadott állatokhoz adunk hozzá. A következőképpen használhatjuk:

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"

Ezzel a mintával osztályokat vehetünk fel és funkciókat adhatunk hozzájuk. Csak egy osztálypéldányt és a wrap által definiált új metódusokat kell átadnunk.ping osztály.

A fenti esetben egy konkrét osztályt használtunk. Ugyanez megvalósítható egy ABS-ben is.tract osztály. A SpeechPoweredAnimalByWrapper osztályt abs-ra kellene cserélnünk.tract, és készen is vagyunk. Rövidebbre fogjuk változtatni az osztály nevét, hogy olvashatóbb legyen.

abstract class SpeechPowered(var myAnimal:Animal){

    fun sound(){
        myAnimal.sound()
    }

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

Ugyanaz, mint azelőtt, de valami mást jelentene. Egy normál osztályban ugyanúgy létrehozhatunk egy osztálypéldányt, mint ahogyan létrehoztuk a cat1-et és a dog1-et. AbstracA t osztályok azonban nem példányosításra, hanem más osztályok kiterjesztésére szolgálnak. Hogyan használnánk tehát az új SpeechPowered(var myAnimal:Animal) abs függvényt?tract osztály? Használhatjuk új osztályok létrehozásával, amelyek kiterjesztik, és ezáltal elnyerik a funkcionalitását.

A példánkban létrehozunk egy SpeechPoweredAnimal osztályt, amely kiterjeszti az osztályt.

class SpeechPoweredAnimal(var myAnimal:Animal):SpeechPowered(myAnimal)
var cat1 =  Cat("Tubby")
var speakingKitty = SpeechPoweredAnimal(cat1)
speakingKitty.speak() //"Hello, my name is Tubby"

Ez ugyanaz a minta, amit a ViewHolderben használunk. A RecyclerView.ViewHolder osztály egy absz.tract osztály, amely funkciókat ad a View-hoz, hasonlóan ahhoz, ahogyan a speak metódust hozzáadtuk az állatokhoz. Ez a hozzáadott funkcionalitás teszi működőképessé a RecyclerView-val való kezelés során.

Így hozhatunk létre egy OneContactViewHolder objektumot a OneContactView objektumból:

//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)

A RecyclerView rendelkezik egy adapterrel, amely lehetővé teszi számunkra, hogy a névjegytömbünket a RecyclerView segítségével összekapcsoljuk a ContactsView-val.

Nézet hozzáadása

A ViewGroup nem rajzol újra automatikusan, hanem egy adott ütemtervet követ. Előfordulhat, hogy az eszközödön 10 vagy 100 ms-ként rajzol újra; vagy ha egy abszurd számot választunk, mondjuk 1 percet, akkor amikor hozzáadunk egy ViewGroup-hoz egy ViewGroup-ot, a változásokat 1 perccel később, a ViewGroup „frissülése”kor fogod látni.

RecyclerView.Recycler

3. alapozó – Gyorsítótárazás

Az egyik legjobb példa arra, hogy hol végezünk rendszeres frissítéseket, a böngésző. Képzeljük el például, hogy a meglátogatott oldal statikus, és nem küld dinamikusan tartalmat; folyamatosan frissítenünk kellene, hogy lássuk a változásokat.

Példaként képzeljük el, hogy a szóban forgó oldal a Twitter. Lenne egy sor statikus tweetünk listázva, és az új tweeteket csak úgy tudnánk látni, ha a frissítés gombra kattintunk a tartalom újbóli letöltéséhez.

A teljes képernyő újrafestése nyilvánvalóan költséges dolog. Képzeljük el, hogy korlátozott sávszélességünk lenne a telefonszolgáltatónkkal, és a tweetlistánk sok kép és videó lenne; költséges lenne minden frissítéskor újra letölteni az oldal teljes tartalmát.

Szükségünk lenne egy módszerre, amellyel tárolhatjuk a már betöltött tweeteket, és biztosíthatjuk, hogy a következő kérésünk képes legyen megmondani, hogy mely tweetek vannak már benne. Ezért nem tölt le mindent újra, csak az új tweeteket kapja meg, és azt is ellenőrzi, hogy van-e már helyben mentett tweet, hogy helyben törölhesse azt. Amit most leírunk, azt gyorsítótárazásnak nevezzük.

Az információkat, amelyeket a weboldalnak küldünk a tartalmainkról, metaadatoknak nevezzük. Tehát a valóságban nem csak azt mondjuk, hogy „be akarjuk tölteni az oldaladat”, hanem azt, hogy „be akarjuk tölteni az oldaladat, és itt van egy része a tartalomnak, amit a legutóbbi betöltéskor mentettünk el; kérjük, csak azt küldd el, ami nincs benne, így nem használunk sok sávszélességet”.

Elrendezési hívások – A tweetlista biztosan őrült

Egy példa az elrendezés hívásaira a scrollToPosition. Ez egy gyakori példa, amely például a csevegőalkalmazásokban található. Ha valaki egy csevegőszálban válaszol egy korábbi csevegőbuborékra, egyes csevegőalkalmazások belefoglalják a választ és egy linket a csevegőbuborékhoz, amelyre kattintva oda navigálhatsz, ahol az eredeti üzeneted volt.

Abban az esetben, ha ezt a metódust még azelőtt hívjuk meg, hogy hozzáadnánk egy LayoutManager objektumot a RecyclerView nézethez, és mielőtt létrehoznánk egy RecyclerView.Adapter objektumot, a scrollToPosition(n:Int) egyszerűen figyelmen kívül marad.

Kommunikáció a RecyclerView komponensek között

4. alapozó – Visszahívások

A RecyclerView a munkája során számos mozgó alkatrészből áll. Meg kell küzdenie a LayoutManagerrel, amely megmondja, hogyan rendezzük a nézeteket (View), akár lineárisan, akár rácsban. Meg kell küzdenie egy adapterrel, amely elvégzi az elemeink (contactList) nézetté (OneContactView), majd nézettartókká (OneContactViewHolder) konvertálását, amelyekkel a RecyclerView hajlandó együttműködni a biztosított metódusok segítségével.

A RecyclerView nyersanyagát a nézeteink, pl. a OneContactView, és egy adatforrás alkotja.

contactList:Array<Contact>

Egy egyszerű forgatókönyvet használtunk kiindulópontként, hogy megértsük, mit is próbál elérni a RecyclerView. Egy könnyen érthető alapeset, amikor egy 1000 névjegyből álló statikus tömböt szeretnénk megmutatni a felhasználónak. A RecyclerView gépezete igazán akkor kezd el működni, amikor a lista már nem statikus. Egy dinamikus lista esetén át kell gondolnunk, mi történik a képernyőn megjelenő nézettel, amikor hozzáadunk egy elemet a listához, vagy eltávolítunk egy elemet a listából.

RecyclerView.LayoutManager

Amellett, hogy eldönti, hogyan rendeződnek el a nézetek – lineárisan vagy rácsban –, a LayoutManager sok munkát végez a motorháztető alatt.ping Az újrahasznosító tudja, mikor kell újrahasznosítani.

Felelős a kee-értping track a képernyőn aktuálisan látható nézetek közül, és ezt az információt továbbítja az újrahasznosító mechanizmusnak. Ahogy a felhasználó lefelé görget, a LayoutManager felelős azért, hogy tájékoztassa az újrahasznosító rendszert azokról a nézetekről, amelyek felül elmosódnak, hogy újra felhasználhatók legyenek, ahelyett, hogy ott maradnának és memóriát fogyasztanának, vagy csak azért semmisülnének meg, hogy újakat hozhassanak létre.

Ez azt jelenti, hogy a LayoutManagernek meg kell őriznie track, amely azt mutatja, hogy a felhasználó hol tartózkodik, miközben görgeti a listánkat. Ezt úgy éri el, hogy a pozíciók listája index alapú, azaz az első elem 0-ról indul, és a listánkban szereplő elemek számával megegyezően növekszik.

Ha a mondjuk 100 elemből álló listánkon 10 elemet tudunk megtekinteni, akkor a LayoutManager a kezdetekkor tudja, hogy a View-0 egészen a View-9-ig fókuszban van. Görgetés közben a LayoutManager képes kiszámítani, hogy mely nézetek kerülnek fókuszba.

A LayoutManager képes ezeket a nézeteket újrahasznosító mechanizmusnak átadni, hogy azok újra felhasználhatók legyenek (új adatok köthetők hozzájuk; pl. egy nézet elérhetőségi adatai eltávolíthatók, és a következő szegmensből származó új elérhetőségi adatok helyettesíthetik a helyőrzőket).

Ez egy szerencsés eset lenne, ha a listánk statikus, de a RecyclerView használatának egyik leggyakoribb esete a dinamikus listák, ahol az adatok egy online végpontról vagy akár egy érzékelőről származhatnak. Nemcsak hozzáadunk adatokat, hanem a listánkon lévő adatokat néha eltávolítjuk vagy frissítjük is.

Adataink dinamikus állapota miatt nagyon nehéz lehet a LayoutManagerrel kapcsolatos következtetéseket levonni. Emiatt a LayoutManager saját listát vezet az elemekről és pozíciókról, elkülönítve az újrahasznosító komponens által használt listától. Ez biztosítja, hogy az elrendezési feladatát helyesen végezze.

Ugyanakkor a RecyclerView LayoutManager-je nem akarja, hogy a birtokában lévő adatok tévesen jelenjenek meg. A megfelelő működés érdekében a LayoutManager megadott időközönként (60 ms) szinkronizálódik a RecyclerView.Adapter-rel, megosztva az információkat a listaelemeinkről, azaz a hozzáadott, frissített, eltávolított vagy egyik pozícióból a másikba áthelyezett elemekről. A LayoutManager, miután megkapta ezt az információt, szükség esetén átszervezi a képernyőn megjelenő tartalmat a változásoknak megfelelően.

A RecyclerView-val kapcsolatos alapvető műveletek közül sok a RecyclerView.LayoutManager és a RecyclerView.Adapter közötti kommunikációra összpontosít, amely a hol statikus, hol dinamikus adatlistáinkat tárolja.

Ezenkívül a RecyclerView olyan metódusokat is kínál, amelyekkel eseményeket figyelhetünk, például az onBindViewHolder metódust, amikor a RecyclerView.Adapter a listánk tartalmát (pl. egy névjegyet) egy ViewHolderhez köti, hogy az megjelenítse az információt a képernyőn.

Egy másik az onCreateViewHolder, ami megmutatja, hogy a RecyclerView.Adapter mikor konvertál egy hagyományos View elemet, például a OneContactView-ot egy olyan ViewHolder elemmé, amellyel a RecyclerView dolgozni tud.

Az újrahasznosítást lehetővé tevő alapvető mechanizmusokon kívül a RecyclerView lehetőséget kínál a viselkedés testreszabására az újrahasznosítás befolyásolása nélkül. A nézetek újrafelhasználása megnehezíti a statikus nézetekkel megszokott szokásos dolgokat, például az onClick eseményekre való reagálást.

Mint tudjuk, a RecyclerView.LayoutManager amely bemutatja a Nézeteket a felhasználónak, egy pillanatra eltérhet az elemek listája a RecyclerView.Adapter amelynek a listája egy adatbázisban van tárolva, vagy egy forrásból adatfolyamként érkezik. Az OnClick események közvetlen Views-be helyezése váratlan viselkedéshez vezethet, például a rossz kapcsolattartó törléséhez.

Gradle

Ha a RecyclerView-t szeretnénk használni, akkor függőségként kell hozzáadnunk a build.gradle fájlunkhoz. A következő példában az „androidx.recyclerview:recyclerview:1.1.0” implementációt használtuk, amely a cikkben leírtak szerint a legfrissebb verzió.

Miután hozzáadtuk a függőséget a mi Gradle fájlt, akkor a rendszer rákérdez Android Studio a változások szinkronizálásához. Így működik a mi Gradle A fájl egy RecyclerView hozzáadása után egy üres projektben fog gondoskodni az alapértelmezett beállításokkal.

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"
}

Jelenleg csak egy layout fájlunk van. Egy egyszerű példával kezdünk, ahol egy RecyclerView-t használunk gyümölcsnevek listájának megjelenítéséhez a képernyőn.

Tételek listája

Navigáljunk a MainActivity fájlunkhoz, és hozzunk létre egy tömböt, amelyben gyümölcsnevek szerepelnek, közvetlenül az onCreate() metódus előtt, amelyet a beállítás során generáltunk.

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)
    }
}

A következő célunk az lesz, hogy ezt a listát egy RecyclerView segítségével megjelenítsük a képernyőn. Ehhez navigáljunk a layout-okat tartalmazó layout könyvtárba, és hozzunk létre egy View-t, amely egy gyümölcs megjelenítéséért lesz felelős.

A listánk minden eleméhez használandó elrendezés

<?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>

A fenti TextView nézetben hozzáadtunk egy id mezőt, amely a View nézet azonosítására szolgál. Alapértelmezés szerint nem generálódik. A TextView nézetnek a fruitName id-t adtuk meg az adatoknak megfelelően, amelyek hozzá lesznek kötve.

A RecyclerView hozzáadása a fő elrendezéshez

Ugyanebben a tevékenységben van egy main_layout.xml elrendezési fájl, amelyet alapértelmezés szerint generáltak nekünk. Ha egy üres projektet választottunk, akkor az egy generált XML tartalmaz egy ConstraintLayout elemet, és belül egy TextView lesz „Hello” szöveggel. Töröljük az összes tartalmat, és az elrendezés csak a RecyclerView elemet fogja tartalmazni az alábbiak szerint:

<?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" />

Hozzáadtunk egy id attribútumot is a RecyclerView-hoz, amelyet a kódunkban fogunk rá hivatkozni.

android:id="@+id/fruitRecyclerView"

Ezután visszatérünk a MainActivity fájlunkhoz. A létrehozott azonosítók segítségével hivatkozni tudunk majd az imént létrehozott nézetekre. Először a RecyclerView-ra fogunk hivatkozni a findViewById() metódus segítségével, amelyet a következő biztosít. AndroidEzt az onCreate() metódusunkban fogjuk megtenni. Az onCreate() metódusunk a következőképpen fog kinézni.

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    var myFruitRecyclerView: RecyclerView = findViewById(R.id.fruitRecyclerView)
}

Hozzon létre egy ViewHolder-t

Következő lépésként létrehozunk egy RecyclerView.ViewHolder metódust, amely a View objektumot ViewHolderré alakítja, amelyet a RecyclerView az elemeink megjelenítésére használ. Ezt közvetlenül az onCreate() metódus után fogjuk megtenni.

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)

}

Hozzon létre egy RecyclerViewAdaptert

Következő lépésként létrehozunk egy FruitArrayAdapter osztályt, amely kiterjeszti a RecyclerView.Adapter osztályt. Az általunk létrehozott FruitArrayAdapter a következőkért lesz felelős: kiveszi a gyümölcsneveket a gyümölcstömbből, létrehoz egy ViewHolder-t a one_fruit_view.xml nézetünk segítségével, majd a gyümölcsöt egy ViewHolder-hez köti, és dinamikusan köti a tartalmát a létrehozott View-hoz.

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 piros hullámokat fog hozzáadni a FruitArrayAdapterünkhöz, jelezve, hogy olyan metódusokat kell implementálnunk, amelyeket a RecyclerView használhat a tömbünk ViewHolderhez való csatlakoztatásához.

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) {

     }
 }

A generált kód legegyszerűbb részével, a getItemCount() metódussal kezdjük. Tudjuk, hogyan kérdezhetjük meg a tömbünkben lévő elemek számát a tömbméret tulajdonság meghívásával.

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) {

        }
    }

Ezután implementáljuk az onCreateViewHolder metódust. Itt a RecyclerView arra kér minket, hogy segítsünk neki létrehozni egy FruitViewHolder osztályt. Ha jól emlékszem, így nézett ki a FruitViewHolder osztályunk:

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

Szükséges hozzá a fruitView nézetünk, amelyet egy one_fruit_view.xml XML fájlként hoztunk létre. Létrehozhatunk egy hivatkozást erre az XML-re, és a következőképpen konvertálhatjuk View-vá.

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) {

        }
    }

A fennmaradó bit az onBindViewHolder felülbírálása.

fun onBindViewHolder(holder: FruitViewHolder, position: Int)

A RecyclerView.Adapter egy pozíció-egész számmal kérdez rá, amelyet egy elem listánkból való kiolvasására használunk. Emellett egy tárolót is biztosít számunkra, így a fruitArray-ből kapott elemet a nézettárolóban tárolt View-hoz köthetjük. A ViewHolder-en belül tárolt View a ViewHolder.itemView mezőn keresztül érhető el. Miután megkaptuk a nézetet, a korábban létrehozott fruitName azonosítót használhatjuk a tartalom beállításához.

override fun onBindViewHolder(holder: FruitViewHolder, position: Int) {

          var ourFruitTextView = holder.itemView.findViewById<TextView>(R.id.fruitName)

          var aFruitName = fruitArray.get(position)

          ourFruitTextView.setText(aFruitName)
      }

Ezzel a FruitArrayAdapterünk elkészült, és a következőképpen néz ki.

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)
        }
    }

Végül készen állunk arra, hogy bekössük a RecyclerView fennmaradó részeit, amelyek egy LayoutManager-t hoznak létre, amely megmondja a RecyclerView-nak, hogyan jelenítse meg a lista tartalmát, hogy lineárisan jelenítse meg a LinearLayoutManager segítségével, vagy rácsban a GridLayoutManager vagy a StaggeredGridLayoutManager segítségével.

Elrendezéskezelő létrehozása

Visszatérünk az onCreate függvényünkhöz, és hozzáadjuk a LayoutManager-t.

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

   }

Csatlakoztassa adapterünket az elemekhez, és állítsa be a RecyclerView-ba

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
    }

Létrehoztunk egy fruitListAdapter példányt is, és betöltöttük a gyümölcsnevek tömbjével. És alapvetően készen is vagyunk. A teljes MainActivity.kt fájl a következőképpen néz ki.

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)
        }
    }
}

Töltse le a Projektet

GYIK

A RecyclerView a ListView rugalmasabb és hatékonyabb utódja. Kikényszeríti a ViewHolder mintát, támogatja a lineáris, rácsos és lépcsőzetes elrendezéseket az elrendezéskezelőkön keresztül, és beépített elemanimációkat ad hozzá, míg a ListView alapértelmezés szerint ezek egyikét sem kínálja.

A GridView csak rögzített rácsban jeleníti meg az elemeket. A RecyclerView a GridLayoutManager segítségével képes reprodukálni a rácsot, de támogatja a lineáris és lépcsőzetes elrendezéseket, a nézetújrahasznosítást és az animációkat is, így sokkal sokoldalúbb, mint a GridView.

Ez a RecyclerView példa Kotlinban íródott, a hivatalosan ajánlott nyelven. Google mert Android fejlesztés. Ugyanazok a RecyclerView, adapter és elrendezéskezelő API-k működnek a Java.

Csatolj egy kattintásfigyelőt a ViewHolder vagy az onBindViewHolder elemhez, majd egy interfész visszahíváson keresztül hozd nyilvánosságra az aktivitáshoz vagy töredékhez. A ViewHolder pozíció használatával elkerülhető a kattintások rossz újrahasznosított elemhez való kötése.

A DiffUtil egy segédprogramosztály, amely kiszámítja két lista közötti különbséget, és a minimális frissítéskészletet küldi az adapternek. Hatékonysá teszi a dinamikus listamódosításokat, és automatikusan animálja a hozzáadásokat, eltávolításokat és áthelyezéseket.

A Jetpack Compose az új képernyőkhöz a RecyclerView helyett a LazyColumn és a LazyRow modulokat használja. A RecyclerView azonban továbbra is széles körben használatos a meglévő View-alapú projektekben, és interoperabilitási API-kon keresztül beágyazható a Compose-ba.

Igen. Az olyan mesterséges intelligencia asszisztensek, mint a GitHub Copilot és a GPT-alapú eszközök, egy rövid promptból képesek RecyclerView adaptert, ViewHoldert és onBindViewHolder sablont létrehozni. A fejlesztőknek továbbra is ellenőrizniük kell a generált kód helyességét és teljesítményét.

Gépi tanulás Android Az alkalmazások a RecyclerView segítségével jelenítenek meg dinamikus kimeneteket, például ajánlásokat, keresési rangsorokat vagy képbesorolásokat. Az adapter a modell eredményeit listaelemekhez köti, így a nagy, gyakran változó adathalmazok görgetése simán megtörténik.

Foglald össze ezt a bejegyzést a következőképpen: