Android RecyclerView: Vad är, lär dig med enkla exempel
⚡ Smart sammanfattning
RecyclerView är ett avancerat Android en widget som visar stora, rullningsbara datamängder effektivt genom att återanvända ett begränsat antal vyer. Den förlitar sig på en adapter, en layouthanterare och en vyhållare för att binda datasamlingar som ändras vid körning.

Vad är RecyclerView i Android?
Ocuco-landskapet RecyclerView är en mer flexibel och avancerad version av GridView och ListView. Det är en behållare för att visa stora datamängder som kan rullas effektivt genom att bibehålla ett begränsat antal vyer. Du kan använda RecyclerView-widgeten när du har datasamlingar vars element ändras vid körning beroende på en nätverkshändelse eller användaråtgärd.
Visningar
Ocuco-landskapet Android Plattformen använder klasserna View och ViewGroup för att rita objekt på skärmen. Dessa klasser är abstract och utökas till olika implementeringar för att passa ett användningsfall. TextView, till exempel, har det enkla syftet att visa textinnehåll på skärmen. EditText utökar från samma View-klass och lägger till mer funktionalitet för att göra det möjligt för användaren att mata in data.
Det är möjligt att skapa våra egna anpassade vyer för att uppnå större flexibilitet vid utveckling.ping användargränssnitt. Klassen View tillhandahåller metoder som vi kan åsidosätta för att rita på skärmen och ett sätt att skicka in parametrar som bredd, höjd och våra egna anpassade attribut som vi vill lägga till i vår View för att få den att bete sig som vi önskar.
Visa grupper
ViewGroup-klassen är en typ av View, men till skillnad från den enkla View-klassen vars ansvar enbart är att visa, ger ViewGroup oss möjligheten att lägga flera vyer i en vy, som vi kan referera till som en helhet. I det här fallet kallas View som skapas på den översta nivån och där vi lägger till andra enkla vyer kallas "förälder", och de vyer som läggs till inuti kallas "barn".
Vi kan föreställa oss en vy som en array och en vygrupp som en array av arrayer. Givet att en array av arrayer i sig är en array, kan vi se hur en vygrupp kan behandlas som en vy.
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 låter oss också definiera hur barnvyerna är organiserade inuti vyn, till exempel om de är placerade vertikalt eller horisontellt. Vi kan ha olika regler för interaktion inuti vyn. Till exempel bör textvyer som följer på varandra ha ett avstånd på 12 dp, medan en bildvy som följs av en textvy bör ha ett avstånd på 5 dp.
Detta skulle vara fallet om vi utveckladeping vår egen ViewGroup från grunden. För att göra dessa konfigurationer enklare, Android tillhandahåller en klass som heter LayoutParams, som vi kan använda för att mata in dessa konfigurationer.
Ocuco-landskapet Android dokumentation innehåller några standardparametrar som vi skulle implementera när vi konfigurerar vår egen ViewGroup. Några vanliga parametrar avser width, height och margin. Som standard har dessa konfigurationer strukturen android:layout_height för height och android:layout_width för width. I detta avseende kan du, när du skapar din ViewGroup, ytterligare skapa LayoutParams specifika för hur du vill att din ViewGroup ska bete sig.
Android levereras med standardvyer och vygrupper som vi kan använda för att utföra många av de vanliga uppgifter vi behöver. Ett exempel vi har nämnt är en textvy. Det är en enkel vy som kommer med konfigurerbara aspekter som höjd, bredd och textstorlek. Vi har också en bildvy för att visa bilder och EditText, som vi nämnde, bland många andra. Android har också anpassade ViewGroups som vi kan lägga till våra Views till och få det förväntade beteendet.
Linjär layout
LinearLayout låter oss lägga till vyobjekt i den. LinearLayout har ett orientation-attribut som anger hur den ska visas på skärmen. Den har också LinearLayout.LayoutParams som dikterar reglerna för vyerna inuti. Till exempel skulle attributet android:center_horizontal centrera vyerna längs den horisontella axeln, medan android:center_vertical skulle centrera innehållet i vyn längs den vertikala axeln.
Här är några bilder som hjälper till att förstå centrering. Vi antar att detta är en enkel TextView i ett utrymme på 200x200px; centreringsattributen skulle få den att bete sig enligt följande.
android:center_horizontal
Horisontellt centrerat innehåll
android:center_vertical
Vertikalt centrerat innehåll
android:center
Centrerat innehåll
Kärnkomponenter i RecyclerView
Kärnkomponenter i RecyclerView
Följande är de viktiga komponenterna i RecyclerView:
RecyclerView.Adapter
Grundarbete #1 – Adaptermönster
En adapter är en enhet som omvandlar attributen hos ett system eller en enhet till de hos en annars inkompatibel enhet eller ett system. Vissa adaptrar modifierar signal- eller strömförsörjningsattribut, medan andra helt enkelt anpassar den fysiska formen hos en kontakt till en annan.
Ett enkelt exempel från verkligheten som förklarar en adapter är när vi behöver ansluta enheter tillsammans, men de har anslutningsportar som inte matchar varandra. Detta kan vara fallet om du besöker ett annat land som använder andra typer av uttag. Om du har med dig din telefon- eller laptopladdare skulle det vara omöjligt att ansluta den till eluttagen. Du skulle dock inte ge upp utan helt enkelt skaffa en adapter som placeras mellan eluttaget och din laddare och möjliggör laddning.
Detta är fallet i programmering när vi vill koppla samman två datastrukturer för att utföra en uppgift, men deras standardportar inte har något sätt att kommunicera med varandra.
Vi kommer att använda det enkla exemplet med en enhet och en laddare. Vi kommer att ha två typer av laddare, en amerikansk och en brittisk.
class AmericanCharger() { var chargingPower = 10 } class BritishCharger(){ var chargingPower = 5 }
Sedan skapar vi två enheter.
class AmericanDevice() class BritishDevice()
Som ett exempel kan vi sedan skapa några instanser av enheterna för att spela med.
var myAmericanPhone = new AmericanDevice() var myBritishPhone = new BritishDevice()
Vi kommer sedan att introducera konceptet med laddning för båda enheterna genom att lägga till en metod i enheterna som heter charge(). Metoden tar emot respektive laddare och utför laddning baserat på den.
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 } }
I det här fallet, baserat på vår analogi, skulle vi av en eller annan anledning behöva använda en brittisk laddare när vi använder en amerikansk enhet, eller vice versa.
I programmeringsvärlden är det oftast när man blandar ihop bibliotek som erbjuder samma funktionalitet (i vårt sammanhang är vår delade funktionalitet laddar). Vi skulle behöva hitta ett sätt att möjliggöra detta.
Om vi följer analogin måste vi gå till en elektronikbutik och köpa en adapter som gör att vi kan ladda amerikanska enheter med BritishChargers. Ur programmeringsperspektiv kommer vi att vara tillverkaren av adaptern.
Vi kommer att skapa en adapter som matchar exakt det mönster vi skulle behöva för att skapa den andra. Vi kommer att implementera den som en klass enligt följande. Det behöver inte nödvändigtvis vara en klass och kan vara en funktion som lyfter fram vad adaptermönstret generellt gör. Vi använder en klass eftersom den matchar de flesta användningsområden på 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 } }
I programmeringsvärlden är skillnaden i uttagen analog med skillnaden i metoderna som används för laddning. Om laddarna har olika metoder skulle det vara omöjligt att använda laddarna.
var myBritishDevice = new BritishDevice() var americanChargerIFound = new AmericanCharger()
Att försöka anropa charge()-metoden på myBritishDevice med americanChargerIFound skulle inte fungera, eftersom AmericanDevice bara accepterar en AmericanCharger. Så det är omöjligt att göra detta:
var myBritishDevice = new BritishDevice() var americanChargerIFound = new AmericanCharger() myBritishDevice.charge(americanChargerIFound)
I det här scenariot kan adaptern vi skapade, AmericanToBritishChargerAdapter, nu vara praktisk. Vi kan använda returnNewCharger()-metoden för att skapa en ny BritishCharger, som vi kan använda för att ladda. Allt vi behöver göra är att skapa en instans av vår adapter och mata den med den AmericanCharger vi har, och det kommer att skapa en BritishCharger som vi kan använda.
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
När vi har att göra med en ViewGroup har vi vyer placerade inuti den. LayoutManager har till uppgift att beskriva hur vyerna är upplagda inuti.
Som jämförelse, när vi arbetar med en LinearLayout ViewGroup, är det användningsfall vi vill ha möjligheten att placera objekten antingen vertikalt eller horisontellt. Detta implementeras enkelt genom att lägga till ett orientation-attribut, vilket anger hur LinearLayout kommer att placeras på skärmen. Vi kan göra detta genom att använda android:orientation=VERTICAL|HORIZONTAL attribut.
Vi har också en annan ViewGroup som heter GridLayout. Dess användningsfall är när vi vill placera Views i en rektangulär rutnätsstruktur. Detta kan till exempel bero på att göra den data vi presenterar för appanvändaren lätt att använda. GridLayout är utformad för att möjliggöra konfigurationer som hjälper dig att nå detta mål genom att definiera rutnätets dimensioner; till exempel kan vi ha ett 4×4-rutnät eller ett 3×2-rutnät.
RecyclerView.ViewHolder
ViewHolder är en magmuskeltract-klassen som vi också utökar från RecyclerView. ViewHolder ger oss vanliga metoder som hjälper oss att referera till en vy som vi har placerat på RecyclerView, även efter att återvinningsmaskineriet i RecyclerView har ändrat olika referenser som vi inte känner till.
Stora listor
RecyclerViews används när vi vill presentera en riktigt stor uppsättning vyer för användaren, samtidigt som vi inte uttömmer RAM på vår enhet för varje instans av vyn som skapas.
Om vi tar fallet med en kontaktlista, skulle vi ha en allmän uppfattning om hur en kontakt skulle se ut i listan. Vad vi sedan skulle göra är att skapa en malllayout, som egentligen är en vy, med platser där olika data från vår kontaktlista kommer att fyllas. Följande är pseudokod som förklarar hela syftet:
//OneContactView
<OneContact>
<TextView>{{PlaceHolderForName}}</TextView>
<TextView>{{PlaceHolderForAddress}}</TextView>
<ImageView>{{PlaceHolderForProfilePicture}}</ImageView>
<TextView>{{PlaceHolderForPhoneNumber}}</TextView>
</OneContact>
Vi skulle då ha en kontaktlista av detta slag:
<ContactList> </ContactList>
Om vi hårdkodade innehållet skulle vi inte ha ett programmatiskt sätt att lägga till nytt innehåll i listan utan att skriva om appen. Som tur är för oss stöds det av en addView(view:View) metod. Ändå är det inte så RecyclerView får undervyer tillagda till sig.
I vårt användningsfall skulle vi ha en lång lista med kontakter. För varje kontakt i listan skulle vi behöva skapa en OneContactView och fylla i informationen i vyn så att den matchar fälten i vår Contact-klass. När vi har vyn skulle vi behöva lägga till den i RecyclerView för att visa listan.
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)
Vi har en array med kontakter. OneContactView innehåller platser för att hämta innehåll från Contact-klassen och visa det. I RecyclerView måste vi lägga till Views så att den kan hjälpa oss med dess återvinningsförmåga.
RecyclerView tillåter oss egentligen inte att lägga till en vy men gör det möjligt för oss att lägga till en ViewHolder. Så i det här scenariot har vi två delar som vi vill ansluta men som inte matchar. Det är här vår adapter kommer in i bilden. RecyclerView ger oss en adapter, ungefär som vår AmericanToBritishChargerAdapter() från tidigare som gjorde det möjligt för oss att konvertera vår AmericanCharger, som var oanvändbar med vår BritishDevice, till något användbart, ungefär som en nätadapter i verkligheten.
I det här scenariot skulle adaptern ta vår kontaktmatris och vår vy, och därifrån generera ViewHolders som RecyclerView är villig att acceptera.
RecyclerView tillhandahåller ett gränssnitt som vi kan utöka för att skapa vår adapter genom klassen RecyclerView.Adapter. Inuti denna adapter finns ett sätt att skapa ViewHolder-klassen som RecyclerView vill arbeta med. Så, vad vi har är samma situation som tidigare, men med en extra sak, adaptern.
Vi har en array med kontakter, en vy för att visa en kontakt (OneContactView) och en RecyclerView, som är en lista med vyer som tillhandahåller återvinningstjänster men bara är villiga att ta emot ViewHolders. I det här scenariot har vi nu en RecyclerView.Adapter-klass, som har en metod för att skapa ViewHolders inuti.
fun createViewHolder(@NonNull parent: ViewGroup, viewType: Int): ViewHolder
RecyclerView.ViewHolder är en abstract-klass som tar vår View som ett argument och konverterar den till en ViewHolder. Den använder wrapper-mönstret som används för att utöka klassernas möjligheter.
Grundarbete #2 – Omslagsmönster
Vi ska använda ett enkelt exempel för att visa hur vi kan få djur att tala.
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
I exemplet ovan har vi två djur. Om vi av en slump skulle vilja lägga till en metod för att få dem att tala, men biblioteksförfattaren inte är rolig, skulle vi fortfarande kunna hitta ett sätt. Det vi skulle behöva är en wrapper för vår Animal-klass. Vi skulle göra detta genom att ta in Animal som en konstruktor för vår klass.
class SpeechPoweredAnimalByWrapper(var myAnimal:Animal){ fun sound(){ myAnimal.sound() } fun speak(){ println("Hello, my name is ${myAnimal.name}") } }
Nu kan vi skicka in en djurinstans till SpeechPoweredAnimalByWrapper. Om vi anropar sound()-metoden på den, anropas den skickade djurmetoden sound(). Vi har också en ytterligare speak()-metod, som räknas som ny funktionalitet som vi lägger till de skickade djuren. Vi kan använda den enligt följande:
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"
Med hjälp av detta mönster kan vi ta klasser och lägga till funktionalitet. Allt vi behöver göra är att skicka en klassinstans och nya metoder som definieras av vår wrap.ping klass.
I fallet ovan använde vi en betongklass. Det är också möjligt att implementera samma sak i en abstract-klassen. Vi skulle behöva ändra SpeechPoweredAnimalByWrapper-klassen till abstract, och vi är klara. Vi kommer att ändra klassnamnet till något kortare för att göra det mer läsbart.
abstract class SpeechPowered(var myAnimal:Animal){ fun sound(){ myAnimal.sound() } fun speak(){ println("Hello, my name is ${myAnimal.name}") } }
Det är samma sak som tidigare, men det skulle betyda något annat. I en vanlig klass kan vi ha en instans av en klass på samma sätt som vi skapade cat1 och dog1. Abstract-klasser är emellertid inte avsedda att instansieras utan är avsedda att utöka andra klasser. Så hur skulle vi använda den nya SpeechPowered(var myAnimal:Animal) abstract-klassen? Vi kan använda den genom att skapa nya klasser som utökar den och i sin tur får dess funktionalitet.
I vårt exempel skapar vi en klass SpeechPoweredAnimal som utökar klassen.
class SpeechPoweredAnimal(var myAnimal:Animal):SpeechPowered(myAnimal)
var cat1 = Cat("Tubby") var speakingKitty = SpeechPoweredAnimal(cat1) speakingKitty.speak() //"Hello, my name is Tubby"
Detta är samma mönster som används i ViewHolder. Klassen RecyclerView.ViewHolder är en abstract-klassen som lägger till funktionalitet till vyn, ungefär som vi lade till speak-metoden till djuren. Den extra funktionaliteten är det som gör att den fungerar när den hanterar RecyclerView.
Så här skulle vi skapa en OneContactViewHolder från OneContactView:
//The View argument we pass is converted to a ViewHolder which uses the View to give it more abilities and in turn work with the RecyclerView class OneContactViewHolder(ourContactView: View) : RecyclerView.ViewHolder(ourContactView)
RecyclerView har en adapter som låter oss ansluta vår kontaktmatris till ContactsView med RecyclerView.
Lägga till en vy
Vygruppen ritar inte om automatiskt utan följer ett visst schema. Det kan vara så på din enhet att den ritar om var 10:e eller 100:e ms; eller, om vi väljer ett absurt antal, säg 1 minut, när vi lägger till en vy i en vygrupp, kommer du att se ändringarna 1 minut senare när vygruppen "uppdateras".
RecyclerView.Recycler
Grundarbete #3 – Cachning
Ett av de bästa exemplen på var vi regelbundet uppdaterar är i webbläsaren. Låt oss till exempel föreställa oss att webbplatsen vi besöker är statisk och inte skickar innehåll dynamiskt; vi skulle behöva uppdatera kontinuerligt för att se ändringarna.
I det här exemplet kan vi föreställa oss att webbplatsen i fråga är Twitter. Vi skulle ha en serie statiska tweets listade, och det enda sättet vi kan se nya tweets skulle vara genom att klicka på uppdateringsknappen för att hämta innehållet på nytt.
Att måla om hela skärmen är uppenbarligen en kostsam sak. Tänk om vi hade begränsad bandbredd hos vår telefonleverantör och vår tweetlista hade många bilder och videor; det skulle vara kostsamt att ladda ner allt innehåll på sidan vid varje uppdatering.
Vi skulle behöva ett sätt att lagra de tweets som redan laddats ner och säkerställa att vår nästa förfrågan har möjlighet att säga vilka tweets den redan har. Därför laddar den inte ner allt igen och hämtar bara de nya tweetsna, och kontrollerar även om någon tweet som sparats lokalt inte längre finns där så att den kan radera den lokalt. Det vi beskriver kallas cachning.
Informationen vi skickar till webbplatsen om det innehåll vi har kallas metadata. Så i verkligheten säger vi inte bara "vi vill ladda din webbplats", vi säger "vi vill ladda din webbplats, och här är en del av innehållet vi redan sparat från förra gången vi laddade. Använd den för att bara skicka oss det som inte finns där, så att vi inte använder mycket bandbredd."
Layoutsamtal – Tweetlistan måste vara galen
Ett exempel på ett layoutanrop är scrollToPosition. Detta är ett vanligt exempel som finns i saker som chattappar. Om någon i en chatttråd svarar på en chattbubbla från tidigare, inkluderar vissa chattappar svaret och en länk till chattbubblan, som när du klickar navigerar dig till var ditt ursprungliga meddelande var.
I det fall där vi anropar den här metoden innan vi har lagt till en LayoutManager i vår RecyclerView och innan vi har en RecyclerView.Adapter, ignoreras helt enkelt scrollToPosition(n:Int).
Kommunikation mellan RecyclerView-komponenter
Grundarbete #4 – Återuppringningar
RecyclerView har många rörliga delar i sitt arbete. Den måste hantera LayoutManager, som talar om för oss hur vi ska organisera vyerna, antingen linjärt eller i ett rutnät. Den måste hantera en adapter som gör jobbet med att konvertera våra objekt (contactList) till vyer (OneContactView) och sedan till ViewHolders (OneContactViewHolder) som RecyclerView är villig att arbeta med med hjälp av de metoder den tillhandahåller.
Råmaterialet för RecyclerView är våra Views, t.ex. OneContactView, och en datakälla.
contactList:Array<Contact>
Vi har använt ett enkelt scenario som utgångspunkt för att få en känsla för vad RecyclerView försöker uppnå. Ett basfall där vi har en statisk array med 1000 kontakter som vi vill visa användaren är lätt att förstå. RecyclerView-maskineriet börjar verkligen ta liv när listan inte längre är statisk. Med en dynamisk lista måste vi tänka på vad som händer med vyn på skärmen när vi lägger till ett objekt i listan eller tar bort ett objekt från listan.
RecyclerView.LayoutManager
Förutom att bestämma hur våra vyer ska utformas, antingen linjärt eller i ett rutnät, gör LayoutManager mycket arbete bakom kulisserna i hel.ping Återvinningsföretaget vet när det är dags att återvinna.
Den ansvarar för att hållaping track av de vyer som för närvarande syns på skärmen och kommunicerar denna information till återvinningsmekanismen. När en användare skrollar nedåt ansvarar LayoutManager för att informera återvinningssystemet om de vyer som blir ofokuserade högst upp så att de kan återanvändas, istället för att stanna kvar där och förbruka minne eller förstöras bara för att skapa nya.
Det här betyder att LayoutManager behöver behålla track av var användaren befinner sig när de skrollar i vår lista. Den gör detta genom att ha en lista med positioner som är indexbaserade, dvs. det första objektet börjar från 0 och ökar för att matcha antalet objekt i vår lista.
Om vi kan se 10 objekt på vår lista med, säg, 100, i början är LayoutManager medveten om att den har fokus från Vy-0 hela vägen till Vy-9. När vi skrollar kan LayoutManager beräkna de vyer som hamnar ur fokus.
LayoutManager kan frigöra dessa vyer till återvinningsmekanismen så att de kan återanvändas (ny data kan bindas till dem; t.ex. kan en vys kontaktdata tas bort och ny kontaktdata från nästa segment kan ersätta platshållarna).
Detta skulle vara ett lyckligt fall om listan vi har är statisk, men ett av de vanligaste användningsfallen för att använda en RecyclerView är med dynamiska listor där data kan komma från en online-slutpunkt eller till och med från en sensor. Data läggs inte bara till, utan data på vår lista tas ibland bort eller uppdateras.
Det dynamiska tillståndet i våra data kan göra det mycket svårt att resonera kring LayoutManager. Av denna anledning har LayoutManager en egen lista över artiklar och positioner, separat från den lista som återvinningskomponenten använder. Detta säkerställer att den utför sitt layoutjobb korrekt.
Samtidigt vill LayoutManager i RecyclerView inte felaktigt framställa den data den har. För att fungera korrekt synkroniseras LayoutManager med RecyclerView.Adapter med givna intervall (60 ms) och delar information om våra listobjekt, dvs. objekt som lagts till, uppdaterats, tagits bort eller flyttats från en position till en annan. När LayoutManager tar emot denna information omorganiserar den innehållet på skärmen för att matcha ändringarna vid behov.
Många av de kärnåtgärder som hanterar RecyclerView roterar kring kommunikationen mellan RecyclerView.LayoutManager och RecyclerView.Adapter som lagrar våra listor med ibland statisk eller ibland dynamisk data.
Dessutom presenterar RecyclerView metoder vi kan använda för att lyssna på händelser, såsom onBindViewHolder när vår RecyclerView.Adapter binder innehåll från vår lista (t.ex. en kontakt) till en ViewHolder så att den används för att visa informationen på skärmen.
En annan är onCreateViewHolder, som talar om för oss när RecyclerView.Adapter tar en vanlig vy som OneContactView och konverterar den till ett ViewHolder-objekt som RecyclerView kan arbeta med.
Förutom de centrala mekanismerna som möjliggör återvinning, erbjuder RecyclerView sätt att anpassa beteende utan att påverka återvinningen. Återanvändning av vyer gör det svårt att göra vanliga saker vi är vana vid att göra med statiska vyer, som att reagera på onClick-händelser.
Som vi är medvetna om, den RecyclerView.LayoutManager som presenterar vyerna för användaren kan för ett ögonblick ha en annan lista över objekt från RecyclerView.Adapter som har listan lagrad i en databas eller strömmar in från en källa. Att placera OnClick-händelser direkt på Views kan leda till oväntat beteende, till exempel att fel kontakt tas bort.
Gradle
Om vi vill använda RecyclerView måste vi lägga till det som ett beroende i vår build.gradle-fil. I följande exempel har vi använt implementeringen "androidx.recyclerview:recyclerview:1.1.0", vilket är den senaste versionen enligt den här artikeln.
Efter att ha lagt till beroendet till vår Gradle fil kommer vi att uppmanas av Android Studio för att synkronisera ändringarna. Så här fungerar vår Gradle filen kommer att se efter att en RecyclerView har lagts till i ett tomt projekt med bara standardvärdena.
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" }
Vi har bara en layoutfil för tillfället. Vi börjar med ett enkelt exempel där vi använder en RecyclerView för att visa en lista med fruktnamn på skärmen.
Lista av föremål
Vi navigerar till vår MainActivity-fil och skapar en array med fruktnamn inuti, precis före onCreate()-metoden som genererades under installationen.
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) } }
Vårt nästa mål blir att presentera den här listan på skärmen med hjälp av en RecyclerView. För att göra detta navigerar vi till layoutkatalogen som innehåller våra layouter och skapar en vy som ansvarar för att visa en frukt.
Layout som ska användas för varje objekt i vår lista
<?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>
I TextView ovan har vi lagt till ett id-fält som kommer att användas för att identifiera en vy. Det genereras inte som standard. Vi har gett vår TextView id:t fruitName för att matcha data, som kommer att bindas till den.
Lägger till RecyclerView till huvudlayouten
I samma aktivitet finns det en layoutfil av typen main_layout.xml som genererades åt oss som standard. Om vi valde ett tomt projekt kommer det att ha genererat en XML innehåller en ConstraintLayout och inuti kommer det att finnas en TextView med texten "Hello". Vi kommer att ta bort allt innehåll och låta layouten endast innehålla RecyclerView enligt nedan:
<?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" />
Vi har också lagt till ett id-attribut för RecyclerView, som vi kommer att använda för att referera till det i vår kod.
android:id="@+id/fruitRecyclerView"
Vi navigerar sedan tillbaka till vår MainActivity-fil. Med hjälp av de ID:n vi har skapat kommer vi att kunna referera till de vyer vi just har skapat. Vi börjar med att referera till RecyclerView med hjälp av metoden findViewById() som tillhandahålls av AndroidVi kommer att göra detta med vår onCreate()-metod. Vår onCreate()-metod kommer att se ut enligt följande.
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) var myFruitRecyclerView: RecyclerView = findViewById(R.id.fruitRecyclerView) }
Skapa en ViewHolder
Nästa steg är att skapa en RecyclerView.ViewHolder, som ansvarar för att ta vår vy och konvertera den till en ViewHolder, som RecyclerView använder för att visa våra objekt. Vi gör detta direkt efter vår onCreate()-metod.
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) }
Skapa en RecyclerViewAdapter
Härnäst skapar vi en FruitArrayAdapter-klass som utökar RecyclerView.Adapter-klassen. FruitArrayAdaptern som vi skapar kommer att ansvara för följande: den tar fruktnamn från fruktmatrisen, skapar en ViewHolder med hjälp av vår vy one_fruit_view.xml, binder sedan frukten till en ViewHolder och binder dynamiskt innehållet till den vy vi skapade.
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 kommer att lägga till röda krusiduller på vår FruitArrayAdapter, vilket talar om för oss att vi behöver implementera metoder som RecyclerView kan använda för att ansluta vår array till en ViewHolder.
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) { } }
Vi börjar med den enklaste delen av den genererade koden, metoden getItemCount(). Vi vet hur man får antalet objekt i vår array genom att anropa egenskapen array size.
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) { } }
Sedan ska vi implementera metoden onCreateViewHolder. Det är här RecyclerView ber oss att hjälpa den att konstruera en FruitViewHolder. Om vi minns rätt såg vår FruitViewHolder-klass ut så här:
class FruitViewHolder(fruitView: View): RecyclerView.ViewHolder(fruitView)
Det kräver vår fruitView, som vi skapade som en XML-fil one_fruit_view.xml. Vi kan skapa en referens till denna XML och konvertera den till en View enligt följande.
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) { } }
Den återstående biten är onBindViewHolder-override.
fun onBindViewHolder(holder: FruitViewHolder, position: Int)
RecyclerView.Adapter frågar med ett positionsheltal, vilket vi använder för att hämta ett objekt från vår lista. Den ger oss också en hållare så att vi kan binda objektet vi får från fruitArray till den vy som finns inuti vyhållaren. Vyn som finns inuti en ViewHolder är tillgänglig via fältet ViewHolder.itemView. När vi väl har fått vyn kan vi använda id:t fruitName som vi skapade tidigare för att ange innehållet.
override fun onBindViewHolder(holder: FruitViewHolder, position: Int) { var ourFruitTextView = holder.itemView.findViewById<TextView>(R.id.fruitName) var aFruitName = fruitArray.get(position) ourFruitTextView.setText(aFruitName) }
Med det är vår FruitArrayAdapter klar och ser ut enligt följande.
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) } }
Slutligen är vi redo att koppla ihop de återstående delarna av vår RecyclerView, vilket skapar en LayoutManager som kommer att tala om för RecyclerView hur innehållet i listan ska visas, oavsett om det ska visas linjärt med LinearLayoutManager eller i ett rutnät med GridLayoutManager eller StaggeredGridLayoutManager.
Skapa layouthanterare
Vi går tillbaka in i vår onCreate-funktion och lägger till LayoutManager.
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) var myFruitRecyclerView: RecyclerView = findViewById(R.id.fruitRecyclerView) var fruitLinearLayout = LinearLayoutManager(this) myFruitRecyclerView.layoutManager =fruitLinearLayout }
Anslut vår adapter till föremål och ställ in den på RecyclerView
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) var myFruitRecyclerView: RecyclerView = findViewById(R.id.fruitRecyclerView) var fruitLinearLayout = LinearLayoutManager(this) myFruitRecyclerView.layoutManager =fruitLinearLayout var fruitListAdapter = FruitListAdapter(fruitNames) myFruitRecyclerView.adapter =fruitListAdapter }
Vi har också skapat en fruitListAdapter-instans och matat den med arrayen av fruktnamn. Och i princip är vi klara. Den kompletta MainActivity.kt-filen ser ut så här.
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) } } }




