Android RecyclerView: Was ist, lernen Sie anhand einfacher Beispiele
โก Intelligente Zusammenfassung
RecyclerView ist ein fortschrittliches Android Dieses Widget stellt groรe, scrollbare Datensรคtze effizient dar, indem es eine begrenzte Anzahl von Ansichten wiederverwendet. Es nutzt einen Adapter, einen Layout-Manager und einen View-Holder, um Datensammlungen zu binden, die sich zur Laufzeit รคndern.
Was ist RecyclerView? Android?
Das RecyclerView Der RecyclerView ist eine flexiblere und fortschrittlichere Version von GridView und ListView. Er dient als Container zur Darstellung groรer Datensรคtze, die durch die Verwaltung einer begrenzten Anzahl von Ansichten effizient gescrollt werden kรถnnen. Sie kรถnnen den RecyclerView verwenden, wenn Ihre Datensammlungen sich zur Laufzeit in Abhรคngigkeit von Netzwerkereignissen oder Benutzeraktionen รคndern.
Aufrufe
Das Android Die Plattform verwendet die Klassen View und ViewGroup, um Elemente auf dem Bildschirm darzustellen. Diese Klassen sind absolut.tracDie Klassen `t` und `editText` werden je nach Anwendungsfall auf verschiedene Implementierungen erweitert. `TextView` dient beispielsweise lediglich der Anzeige von Textinhalten auf dem Bildschirm. `EditText` hingegen erbt von derselben `View`-Klasse und bietet zusรคtzliche Funktionen zur Dateneingabe.
Es ist mรถglich, eigene benutzerdefinierte Ansichten zu erstellen, um bei der Entwicklung mehr Flexibilitรคt zu erreichen.ping Benutzeroberflรคchen. Die View-Klasse bietet Methoden, die wir รผberschreiben kรถnnen, um auf dem Bildschirm zu zeichnen, sowie die Mรถglichkeit, Parameter wie Breite, Hรถhe und eigene Attribute zu รผbergeben, die wir unserer Ansicht hinzufรผgen mรถchten, um ihr das gewรผnschte Verhalten zu verleihen.
Ansichtsgruppen
Die Klasse `ViewGroup` ist eine Art von `View`, aber im Gegensatz zur einfachen `View`-Klasse, deren Aufgabe lediglich die Anzeige ist, ermรถglicht `ViewGroup` das Zusammenfassen mehrerer Ansichten zu einer einzigen Ansicht, auf die wir als Ganzes zugreifen kรถnnen. In diesem Fall wird die oberste Ansicht, der wir weitere einfache Ansichten hinzufรผgen, als โElternansichtโ bezeichnet, und die darin enthaltenen Ansichten heiรen โKinderansichtenโ.
Wir kรถnnen uns eine View als ein Array und eine ViewGroup als ein Array von Arrays vorstellen. Da ein Array von Arrays selbst wieder ein Array ist, lรคsst sich erkennen, wie eine ViewGroup wie eine View behandelt werden kann.
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
Die ViewGroup ermรถglicht es uns auรerdem, die Anordnung der Kindelemente innerhalb der Ansicht festzulegen, beispielsweise vertikal oder horizontal. Wir kรถnnen unterschiedliche Interaktionsregeln innerhalb der Ansicht definieren. So sollten beispielsweise aufeinanderfolgende TextViews einen Abstand von 12 dp haben, wรคhrend ein ImageView gefolgt von einem TextView einen Abstand von 5 dp aufweisen sollte.
Dies wรคre der Fall, wenn wir entwickeln wรผrdenping Wir erstellen unsere eigene ViewGroup von Grund auf. Um diese Konfigurationen zu vereinfachen, Android stellt eine Klasse namens LayoutParams bereit, mit der wir diese Konfigurationen eingeben kรถnnen.
Das Android Dokumentation Es werden einige Standardparameter bereitgestellt, die wir beim Konfigurieren unserer eigenen ViewGroup verwenden. Hรคufig verwendete Parameter betreffen Breite, Hรถhe und Rand. Standardmรครig haben diese Konfigurationen die Struktur `android:layout_height` fรผr die Hรถhe und `android:layout_width` fรผr die Breite. Beim Erstellen Ihrer ViewGroup kรถnnen Sie darรผber hinaus LayoutParams definieren, die das gewรผnschte Verhalten Ihrer ViewGroup festlegen.
Android Es enthรคlt standardmรครige Ansichten und Ansichtsgruppen, mit denen wir viele gรคngige Aufgaben erledigen kรถnnen. Ein Beispiel dafรผr ist die bereits erwรคhnte TextView. Diese einfache Ansicht bietet konfigurierbare Eigenschaften wie Hรถhe, Breite und Textgrรถรe. Auรerdem gibt es, wie bereits erwรคhnt, eine ImageView zur Bildanzeige und EditText sowie viele weitere Elemente. Android verfรผgt auรerdem รผber benutzerdefinierte ViewGroups, zu denen wir unsere Ansichten hinzufรผgen und das erwartete Verhalten erzielen kรถnnen.
LinearLayout
LinearLayout ermรถglicht das Hinzufรผgen von View-Elementen. Es verfรผgt รผber ein Ausrichtungsattribut, das die Anordnung auf dem Bildschirm festlegt. Zusรคtzlich gibt es `LinearLayout.LayoutParams`, die die Regeln fรผr die darin enthaltenen Views definieren. Beispielsweise zentriert das Attribut `android:center_horizontal` die Views horizontal, wรคhrend `android:center_vertical` den Inhalt der View vertikal zentriert.
Hier sind einige Bilder, die das Zentrieren veranschaulichen. Wir gehen von einem einfachen TextView innerhalb eines 200 x 200 Pixel groรen Bereichs aus; die Zentrierungsattribute wรผrden folgendes Verhalten bewirken.
android:center_horizontal
Horizontal zentrierter Inhalt
android:center_vertical
Vertikal zentrierter Inhalt
android:center
Zentrierter Inhalt
Kernkomponenten von RecyclerView
Kernkomponenten von RecyclerView
Im Folgenden sind die wichtigen Komponenten von RecyclerView aufgefรผhrt:
RecyclerView.Adapter
Grundlagen Nr. 1 โ Adaptermuster
Ein Adapter ist ein Gerรคt, das die Eigenschaften eines Systems oder Gerรคts an die eines ansonsten inkompatiblen Gerรคts oder Systems anpasst. Manche Adapter verรคndern Signal- oder Stromversorgungseigenschaften, andere hingegen adaptieren lediglich die physische Form eines Steckers an einen anderen.
Ein einfaches Beispiel aus dem Alltag, das die Verwendung eines Adapters verdeutlicht: Man mรถchte Gerรคte miteinander verbinden, deren Anschlรผsse aber nicht kompatibel sind. Das kann beispielsweise der Fall sein, wenn man in ein anderes Land reist, in dem andere Steckdosen verwendet werden. Mit dem mitgebrachten Handy- oder Laptop-Ladegerรคt kann man es dort nicht anschlieรen. Man gibt aber nicht auf, sondern verwendet einfach einen Adapter, der zwischen Steckdose und Ladegerรคt geschaltet wird und das Laden ermรถglicht.
Dies ist der Fall in der Programmierung, wenn wir zwei Datenstrukturen miteinander verbinden wollen, um eine Aufgabe zu erfรผllen, deren Standardports aber keine Mรถglichkeit zur Kommunikation untereinander bieten.
Wir verwenden das einfache Beispiel eines Gerรคts und eines Ladegerรคts. Wir betrachten zwei Ladegerรคte, ein amerikanisches und ein britisches.
class AmericanCharger() { var chargingPower = 10 } class BritishCharger(){ var chargingPower = 5 }
Wir werden dann zwei Gerรคte herstellen.
class AmericanDevice() class BritishDevice()
Als Beispiel kรถnnen wir dann einige Instanzen der Gerรคte zum Mitspielen erstellen.
var myAmericanPhone = new AmericanDevice() var myBritishPhone = new BritishDevice()
Anschlieรend fรผhren wir das Konzept des Ladens fรผr beide Gerรคte ein, indem wir eine Methode namens `charge()` in den Gerรคten hinzufรผgen. Die Methode verwendet das jeweilige Ladegerรคt und fรผhrt den Ladevorgang entsprechend durch.
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 } }
In diesem Fall wรผrden wir, basierend auf unserer Analogie, aus dem einen oder anderen Grund ein britisches Ladegerรคt benรถtigen, wenn wir ein amerikanisches Gerรคt verwenden, oder umgekehrt.
In der Programmierwelt ist dies normalerweise der Fall, wenn Bibliotheken gemischt werden, die die gleiche Funktionalitรคt bieten (in unserem Kontext ist unsere gemeinsame Funktionalitรคt kostenpflichtig). Wir mรผssten einen Weg finden, dies zu ermรถglichen.
Folgt man der Analogie, mรผssten wir in ein Elektronikgeschรคft gehen und einen Adapter kaufen, mit dem wir amerikanische Gerรคte mit britischen Ladegerรคten aufladen kรถnnen. Aus Programmierersicht wรคren wir der Hersteller des Adapters.
Wir erstellen einen Adapter, der genau dem Muster entspricht, das wir fรผr die Erstellung des anderen Adapters benรถtigen. Wir implementieren ihn als Klasse, wie folgt. Es muss nicht unbedingt eine Klasse sein; es kรถnnte auch eine Funktion sein, die die allgemeine Funktionsweise des Adaptermusters verdeutlicht. Wir verwenden eine Klasse, da dies dem hรคufigsten Anwendungsfall entspricht. 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 } }
In der Programmierwelt ist der Unterschied bei den Anschlรผssen vergleichbar mit dem Unterschied bei den Lademethoden. Ladegerรคte mit unterschiedlichen Lademethoden wรคren unbrauchbar.
var myBritishDevice = new BritishDevice() var americanChargerIFound = new AmericanCharger()
Der Versuch, die Methode `charge()` von `myBritishDevice` mit `americanChargerIFound` aufzurufen, schlรคgt fehl, da `AmericanDevice` nur `AmericanCharger` akzeptiert. Daher ist dies nicht mรถglich.
var myBritishDevice = new BritishDevice() var americanChargerIFound = new AmericanCharger() myBritishDevice.charge(americanChargerIFound)
In diesem Szenario erweist sich der von uns erstellte Adapter โAmericanToBritishChargerAdapterโ als nรผtzlich. Mit der Methode `returnNewCharger()` kรถnnen wir ein neues โBritishChargerโ-Objekt erstellen, das wir zum Laden verwenden kรถnnen. Dazu erstellen wir lediglich eine Instanz unseres Adapters und รผbergeben ihr das vorhandene โAmericanChargerโ-Objekt. Daraufhin wird ein โBritishChargerโ-Objekt erstellt, das wir verwenden kรถnnen.
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
Bei der Arbeit mit einer ViewGroup befinden sich darin platzierte Views. Der LayoutManager hat die Aufgabe, die Anordnung der Views innerhalb der ViewGroup zu beschreiben.
Zum Vergleich: Bei der Arbeit mit einer LinearLayout-ViewGroup mรถchten wir die Elemente entweder vertikal oder horizontal anordnen kรถnnen. Dies lรคsst sich einfach realisieren, indem wir ein Ausrichtungsattribut hinzufรผgen, das angibt, wie das LinearLayout auf dem Bildschirm positioniert wird. Wir kรถnnen dies mithilfe von โฆ erreichen. android:orientation=VERTICAL|HORIZONTAL Attribut.
Wir haben auรerdem eine weitere ViewGroup namens GridLayout. Sie dient dazu, Views in einer rechteckigen Rasterstruktur anzuordnen. Dies kann beispielsweise sinnvoll sein, um die dem App-Nutzer prรคsentierten Daten รผbersichtlicher zu gestalten. GridLayout bietet Konfigurationsmรถglichkeiten, um dieses Ziel durch die Definition der Rasterdimensionen zu erreichen; beispielsweise ein 4ร4- oder ein 3ร2-Raster.
RecyclerView.ViewHolder
Der ViewHolder ist ein AbstracDie Klasse, die wir ebenfalls von RecyclerView erweitern, stellt uns ViewHolder zur Verfรผgung. Dieser bietet uns allgemeine Methoden, mit denen wir auf eine View zugreifen kรถnnen, die wir in RecyclerView platziert haben, selbst nachdem die Recyclingmechanismen von RecyclerView verschiedene Referenzen geรคndert haben, von denen wir nichts wissen.
Groรe Listen
RecyclerViews werden verwendet, wenn wir dem Benutzer eine sehr groรe Anzahl von Ansichten prรคsentieren mรถchten, ohne dabei den Speicherplatz zu erschรถpfen. RAM auf unserem Gerรคt fรผr jede einzelne Instanz der erstellten Ansicht.
Nehmen wir als Beispiel eine Kontaktliste. Wir hรคtten dann eine allgemeine Vorstellung davon, wie ein Kontakt in der Liste aussehen wรผrde. Anschlieรend wรผrden wir ein Layout-Template erstellen, das eigentlich eine Ansicht (View) ist, mit Feldern, in die verschiedene Daten aus unserer Kontaktliste eingefรผgt werden. Der folgende Pseudocode verdeutlicht den gesamten Zweck:
//OneContactView
<OneContact>
<TextView>{{PlaceHolderForName}}</TextView>
<TextView>{{PlaceHolderForAddress}}</TextView>
<ImageView>{{PlaceHolderForProfilePicture}}</ImageView>
<TextView>{{PlaceHolderForPhoneNumber}}</TextView>
</OneContact>
Wir hรคtten dann eine Kontaktliste dieser Art:
<ContactList> </ContactList>
Wรผrden wir die Inhalte fest im Code verankern, hรคtten wir keine Mรถglichkeit, programmatisch neue Inhalte zur Liste hinzuzufรผgen, ohne die Anwendung neu zu schreiben. Glรผcklicherweise wird das Hinzufรผgen einer Ansicht zu einer Ansichtsgruppe von einer solchen Funktion unterstรผtzt. addView(view:View) Methode. Dennoch ist dies nicht die Art und Weise, wie dem RecyclerView untergeordnete Ansichten hinzugefรผgt werden.
In unserem Anwendungsfall haben wir eine lange Kontaktliste. Fรผr jeden Kontakt in der Liste mรผssen wir eine `OneContactView` erstellen und die Daten innerhalb dieser Ansicht entsprechend den Feldern unserer `Contact`-Klasse befรผllen. Anschlieรend mรผssen wir die Ansicht dem `RecyclerView` hinzufรผgen, um die Liste anzuzeigen.
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)
Wir haben ein Array von Kontakten. Die OneContactView enthรคlt Slots, um Inhalte aus der Contact-Klasse zu รผbernehmen und anzuzeigen. In der RecyclerView mรผssen wir Views hinzufรผgen, damit sie die Wiederverwertungsfunktion nutzen kann.
Der RecyclerView erlaubt es uns nicht, eine Ansicht hinzuzufรผgen, sondern nur einen ViewHolder. In diesem Fall mรถchten wir also zwei Komponenten verbinden, die nicht zusammenpassen. Hier kommt unser Adapter ins Spiel. Der RecyclerView stellt uns einen Adapter zur Verfรผgung, รคhnlich wie unser AmericanToBritishChargerAdapter() Das hat uns zuvor ermรถglicht, unseren amerikanischen Ladeadapter, der mit unserem britischen Gerรคt unbrauchbar war, in etwas Brauchbares umzuwandeln, das einem Netzteil im realen Leben รคhnelt.
In diesem Szenario wรผrde der Adapter unser Array von Kontakten und unsere Ansicht entgegennehmen und daraus ViewHolder generieren, die der RecyclerView akzeptieren kann.
Der RecyclerView bietet eine Schnittstelle, die wir erweitern kรถnnen, um unseren Adapter รผber die Klasse `RecyclerView.Adapter` zu erstellen. Innerhalb dieses Adapters befindet sich eine Mรถglichkeit, die ViewHolder-Klasse zu erstellen, mit der der RecyclerView arbeiten soll. Wir haben also dieselbe Situation wie zuvor, nur mit einer zusรคtzlichen Komponente: dem Adapter.
Wir haben ein Array von Kontakten, eine Ansicht zur Anzeige eines einzelnen Kontakts (OneContactView) und einen RecyclerView, eine Liste von Ansichten, die Recycling-Dienste bereitstellt, aber nur ViewHolder akzeptiert. In diesem Szenario haben wir nun eine RecyclerView.Adapter-Klasse, die eine Methode zum Erstellen von ViewHoldern enthรคlt.
fun createViewHolder(@NonNull parent: ViewGroup, viewType: Int): ViewHolder
Der RecyclerView.ViewHolder ist ein absolutertracDie Klasse nimmt unsere View als Argument entgegen und konvertiert sie in einen ViewHolder. Sie verwendet das Wrapper-Muster, das zur Erweiterung der Funktionalitรคt von Klassen dient.
Grundarbeit Nr. 2 โ Wrapper-Muster
Anhand eines einfachen Beispiels werden wir demonstrieren, wie wir Tiere zum Sprechen bringen kรถnnen.
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
Im obigen Beispiel haben wir zwei Tiere. Falls wir ihnen aus irgendeinem Grund eine Sprachausgabe hinzufรผgen wollten, der Bibliotheksautor aber nicht kooperativ war, kรถnnten wir trotzdem eine Lรถsung finden. Wir brรคuchten dazu einen Wrapper fรผr unsere Tierklasse. Diesen wรผrden wir erreichen, indem wir das Tier als Konstruktor fรผr unsere Klasse verwenden.
class SpeechPoweredAnimalByWrapper(var myAnimal:Animal){ fun sound(){ myAnimal.sound() } fun speak(){ println("Hello, my name is ${myAnimal.name}") } }
Nun kรถnnen wir eine Tierinstanz an den SpeechPoweredAnimalByWrapper รผbergeben. Der Aufruf der Methode `sound()` ruft die entsprechende Methode `sound()` des รผbergebenen Tieres auf. Zusรคtzlich gibt es eine Methode `speak()`, die als neue Funktionalitรคt fรผr die รผbergebenen Tiere gilt. Die Verwendung ist wie folgt:
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"
Mithilfe dieses Musters kรถnnen wir Klassen nehmen und ihnen Funktionalitรคt hinzufรผgen. Wir mรผssen lediglich eine Klasseninstanz und die neuen, durch unseren Wrapper definierten Methoden รผbergeben.ping Klasse.
Im obigen Fall haben wir eine konkrete Klasse verwendet. Es ist auch mรถglich, dasselbe in einer abstrakten Sprache zu implementieren.tract-Klasse. Wir mรผssten die Klasse SpeechPoweredAnimalByWrapper in abs รคndern.tract, und damit sind wir fertig. Wir werden den Klassennamen kรผrzen, um ihn besser lesbar zu machen.
abstract class SpeechPowered(var myAnimal:Animal){ fun sound(){ myAnimal.sound() } fun speak(){ println("Hello, my name is ${myAnimal.name}") } }
Es ist dasselbe wie zuvor, aber es hรคtte eine andere Bedeutung. In einer normalen Klasse kรถnnen wir eine Instanz einer Klasse auf dieselbe Weise erzeugen, wie wir cat1 und dog1 erzeugt haben. Abstract-Klassen sind jedoch nicht zur Instanziierung gedacht, sondern dienen der Erweiterung anderer Klassen. Wie wรผrden wir also den neuen SpeechPowered(var myAnimal:Animal)-Abs verwenden?tract-Klasse? Wir kรถnnen sie nutzen, indem wir neue Klassen erstellen, die sie erweitern und dadurch ihre Funktionalitรคt erhalten.
In unserem Beispiel erstellen wir eine Klasse SpeechPoweredAnimal, die die Klasse erweitert.
class SpeechPoweredAnimal(var myAnimal:Animal):SpeechPowered(myAnimal)
var cat1 = Cat("Tubby") var speakingKitty = SpeechPoweredAnimal(cat1) speakingKitty.speak() //"Hello, my name is Tubby"
Dies ist dasselbe Muster, das im ViewHolder verwendet wird. Die Klasse RecyclerView.ViewHolder ist ein absoluter ViewHolder.tracEine Klasse, die der Ansicht zusรคtzliche Funktionalitรคt hinzufรผgt, รคhnlich wie wir den Tieren die Sprechmethode hinzugefรผgt haben. Diese zusรคtzliche Funktionalitรคt ermรถglicht die Interaktion mit dem RecyclerView.
So erstellen wir einen OneContactViewHolder aus 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 verfรผgt รผber einen Adapter, der es uns ermรถglicht, unser Kontakte-Array mit der ContactsView รผber RecyclerView zu verbinden.
Ansicht hinzufรผgen
Die ViewGroup wird nicht automatisch neu gezeichnet, sondern folgt einem bestimmten Zeitplan. Auf Ihrem Gerรคt kann die Neuzeichnung beispielsweise alle 10 ms oder 100 ms erfolgen. Oder, wenn wir einen unrealistischen Wert wรคhlen, beispielsweise 1 Minute, werden die รnderungen beim Hinzufรผgen einer Ansicht zu einer ViewGroup 1 Minute spรคter sichtbar, wenn die ViewGroup aktualisiert wird.
RecyclerView.Recycler
Grundlagen #3 โ Geocaching
Eines der besten Beispiele dafรผr, wo wir regelmรครig Seiten aktualisieren, ist der Browser. Stellen wir uns beispielsweise vor, die Website, die wir besuchen, ist statisch und sendet keine dynamischen Inhalte; wir mรผssten die Seite stรคndig aktualisieren, um die รnderungen zu sehen.
Nehmen wir als Beispiel Twitter. Dort wรผrden statische Tweets aufgelistet, und neue Tweets kรถnnten nur durch Klicken auf die Aktualisierungsschaltflรคche angezeigt werden.
Das Neuladen des gesamten Bildschirms ist offensichtlich kostspielig. Stellen Sie sich vor, wir hรคtten nur begrenztes Datenvolumen bei unserem Mobilfunkanbieter und unsere Tweet-Liste enthielte viele Bilder und Videos; es wรคre teuer, den gesamten Seiteninhalt bei jedem Aktualisieren neu herunterzuladen.
Wir benรถtigen eine Mรถglichkeit, die bereits geladenen Tweets zu speichern und sicherzustellen, dass unsere nรคchste Anfrage angibt, welche Tweets bereits vorhanden sind. Dadurch werden nicht alle Tweets erneut heruntergeladen, sondern nur die neuen abgerufen. Zusรคtzlich wird geprรผft, ob ein lokal gespeicherter Tweet nicht mehr vorhanden ist, um ihn gegebenenfalls lokal zu lรถschen. Dieses Verfahren nennt man Caching.
Die Informationen, die wir รผber die Inhalte unserer Website senden, werden Metadaten genannt. Wir sagen also nicht einfach nur โWir mรถchten Ihre Website ladenโ, sondern โWir mรถchten Ihre Website laden und hier sind einige Inhalte, die wir beim letzten Laden gespeichert haben; bitte verwenden Sie diese, um uns nur die fehlenden Daten zu senden, damit wir nicht unnรถtig Bandbreite verbrauchen.โ
Layout-Aufrufe โ Die Tweet-Liste muss verrรผckt sein
Ein Beispiel fรผr einen Layoutaufruf ist `scrollToPosition`. Dies ist ein hรคufiges Beispiel, etwa in Chat-Apps. Wenn jemand in einem Chatverlauf auf eine vorherige Chatblase antwortet, fรผgen manche Chat-Apps die Antwort und einen Link zur Chatblase hinzu. Ein Klick darauf fรผhrt zurรผck zur ursprรผnglichen Nachricht.
Falls wir diese Methode aufrufen, bevor wir einen LayoutManager zu unserem RecyclerView hinzugefรผgt haben und bevor wir einen RecyclerView.Adapter haben, wird scrollToPosition(n:Int) einfach ignoriert.
Kommunikation zwischen RecyclerView-Komponenten
Grundlagen #4 โ Rรผckrufe
Der RecyclerView ist ein komplexes System mit vielen Komponenten. Er interagiert mit dem LayoutManager, der die Anordnung der Views โ linear oder in einem Raster โ festlegt. Auรerdem benรถtigt er einen Adapter, der die Elemente (Kontaktliste) in Views (OneContactView) und anschlieรend in ViewHolder (OneContactViewHolder) umwandelt, mit denen der RecyclerView mithilfe seiner Methoden arbeiten kann.
Das Rohmaterial fรผr den RecyclerView sind unsere Views, z. B. OneContactView, und eine Datenquelle.
contactList:Array<Contact>
Wir haben ein einfaches Szenario als Ausgangspunkt gewรคhlt, um zu verstehen, was der RecyclerView leisten soll. Ein Basisfall mit einem statischen Array von 1000 Kontakten, das wir dem Benutzer anzeigen mรถchten, ist leicht verstรคndlich. Der RecyclerView entfaltet sein volles Potenzial jedoch erst, wenn die Liste nicht mehr statisch ist. Bei einer dynamischen Liste mรผssen wir berรผcksichtigen, was mit der Ansicht auf dem Bildschirm passiert, wenn wir ein Element hinzufรผgen oder entfernen.
RecyclerView.LayoutManager
Abgesehen von der Entscheidung, wie unsere Ansichten angeordnet werden, entweder linear oder in einem Raster, erledigt der LayoutManager im Hintergrund eine Menge Arbeit.ping Der Recyclingbetrieb weiร, wann Recycling ansteht.
Es ist verantwortlich fรผr die Keeping tracDer LayoutManager erfasst die Anzahl der aktuell auf dem Bildschirm sichtbaren Ansichten und รผbermittelt diese Information an den Recyclingmechanismus. Beim Scrollen nach unten informiert der LayoutManager das Recyclingsystem รผber die Ansichten, die am oberen Rand den Fokus verlieren, damit diese wiederverwendet werden kรถnnen, anstatt dort zu verbleiben und Speicher zu belegen oder zerstรถrt zu werden, nur um neue zu erstellen.
Das bedeutet, dass der LayoutManager Folgendes beibehalten muss: track gibt die Position des Nutzers beim Scrollen durch unsere Liste an. Dies geschieht mithilfe einer Liste von Positionen, die indexbasiert sind, d. h. das erste Element beginnt bei 0 und wird entsprechend der Anzahl der Elemente in unserer Liste erhรถht.
Wenn wir zu Beginn 10 Elemente unserer Liste mit beispielsweise 100 Elementen sehen kรถnnen, weiร der LayoutManager, dass er die Ansichten View-0 bis View-9 im Fokus hat. Beim Scrollen kann der LayoutManager die Ansichten berechnen, die den Fokus verlieren.
Der LayoutManager kann diese Ansichten dem Recyclingmechanismus รผbergeben, sodass sie wiederverwendet werden kรถnnen (neue Daten kรถnnen an sie gebunden werden; z. B. kรถnnen die Kontaktdaten einer Ansicht entfernt und neue Kontaktdaten aus dem nรคchsten Segment die Platzhalter ersetzen).
Das wรคre ideal, wenn die Liste statisch wรคre. RecyclerViews werden aber hรคufig mit dynamischen Listen verwendet, deren Daten von einem Online-Endpunkt oder sogar von einem Sensor stammen kรถnnen. Dabei werden nicht nur Daten hinzugefรผgt, sondern auch entfernt oder aktualisiert.
Der dynamische Zustand unserer Daten kann die Funktionsweise des LayoutManagers erheblich erschweren. Aus diesem Grund verwaltet der LayoutManager eine eigene Liste der Elemente und Positionen, getrennt von der Liste, die die Recycling-Komponente verwendet. Dies gewรคhrleistet die korrekte Ausfรผhrung des Layouts.
Gleichzeitig mรถchte der LayoutManager des RecyclerView die vorhandenen Daten korrekt darstellen. Um korrekt zu funktionieren, synchronisiert er sich in regelmรครigen Abstรคnden (60 ms) mit dem RecyclerView.Adapter und รผbermittelt Informationen รผber die Listenelemente, z. B. hinzugefรผgte, aktualisierte, entfernte oder verschobene Elemente. Nach Erhalt dieser Informationen passt der LayoutManager die Bildschirminhalte entsprechend den รnderungen an.
Viele der Kernoperationen, die sich mit RecyclerView befassen, drehen sich um die Kommunikation zwischen dem RecyclerView.LayoutManager und dem RecyclerView.Adapter, der unsere Listen mit manchmal statischen oder manchmal dynamischen Daten speichert.
Darรผber hinaus stellt uns RecyclerView Methoden zur Verfรผgung, mit denen wir auf Ereignisse reagieren kรถnnen, wie beispielsweise onBindViewHolder, wenn unser RecyclerView.Adapter Inhalte aus unserer Liste (z. B. einen Kontakt) an einen ViewHolder bindet, damit dieser zur Anzeige der Informationen auf dem Bildschirm verwendet wird.
Eine weitere Methode ist onCreateViewHolder, die uns mitteilt, wann der RecyclerView.Adapter eine regulรคre View wie OneContactView nimmt und sie in ein ViewHolder-Element umwandelt, mit dem der RecyclerView arbeiten kann.
Abgesehen von den Kernmechanismen, die das Recycling ermรถglichen, bietet der RecyclerView Mรถglichkeiten zur Verhaltensanpassung, ohne das Recycling zu beeintrรคchtigen. Die Wiederverwendung von Views erschwert jedoch gรคngige Aktionen, die wir von statischen Views gewohnt sind, wie beispielsweise die Reaktion auf onClick-Ereignisse.
Wie wir wissen, RecyclerView.LayoutManager die dem Benutzer die Ansichten prรคsentiert, kรถnnte fรผr einen Moment eine andere Liste von Elementen haben als die RecyclerView.Adapter Die Liste wird in einer Datenbank gespeichert oder von einer Quelle gestreamt. Das direkte Platzieren von OnClick-Ereignissen auf Ansichten kann zu unerwartetem Verhalten fรผhren, z. B. zum Lรถschen des falschen Kontakts.
Gradle
Um RecyclerView zu verwenden, muss es als Abhรคngigkeit in der build.gradle-Datei hinzugefรผgt werden. Im folgenden Beispiel wurde die Implementierung โandroidx.recyclerview:recyclerview:1.1.0โ verwendet, die laut diesem Artikel die aktuellste Version ist.
Nach dem Hinzufรผgen der Abhรคngigkeit zu unserem Gradle Datei werden wir aufgefordert, Android Studio um die รnderungen zu synchronisieren. So funktioniert unser Gradle Die Datei kรผmmert sich darum, nachdem man in einem leeren Projekt mit den Standardeinstellungen einen RecyclerView hinzugefรผgt hat.
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" }
Wir haben momentan nur eine Layoutdatei. Wir beginnen mit einem einfachen Beispiel, in dem wir einen RecyclerView verwenden, um eine Liste von Fruchtnamen auf dem Bildschirm anzuzeigen.
Liste von Gegenstรคnden
Wir werden zu unserer MainActivity-Datei navigieren und direkt vor der onCreate()-Methode, die wรคhrend der Einrichtung generiert wurde, ein Array mit Fruchtnamen erstellen.
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) } }
Unser nรคchstes Ziel ist es, diese Liste mithilfe eines RecyclerView auf dem Bildschirm darzustellen. Dazu navigieren wir zum Layout-Verzeichnis, das unsere Layouts enthรคlt, und erstellen eine View, die fรผr die Anzeige einer einzelnen Frucht zustรคndig ist.
Layout, das fรผr jedes Element in unserer Liste verwendet werden soll
<?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>
Im obigen TextView haben wir ein ID-Feld hinzugefรผgt, das zur Identifizierung einer View dient. Es wird nicht standardmรครig generiert. Wir haben unserem TextView die ID โfruitNameโ gegeben, um die Daten zuzuordnen, die daran gebunden werden.
Hinzufรผgen der RecyclerView zum Hauptlayout
In derselben Aktivitรคt gibt es eine standardmรครig generierte Layoutdatei namens main_layout.xml. Wenn wir ein leeres Projekt ausgewรคhlt haben, wurde automatisch eine solche Datei generiert. XML Wir werden den gesamten Inhalt eines ConstraintLayouts lรถschen und das Layout so anpassen, dass es nur noch den RecyclerView enthรคlt (siehe unten).
<?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" />
Wir haben auรerdem ein id-Attribut fรผr den RecyclerView hinzugefรผgt, mit dem wir in unserem Code darauf verweisen werden.
android:id="@+id/fruitRecyclerView"
Anschlieรend kehren wir zu unserer MainActivity-Datei zurรผck. Mithilfe der erstellten IDs kรถnnen wir auf die soeben erstellten Views zugreifen. Wir beginnen mit dem Zugriff auf den RecyclerView mithilfe der Methode `findViewById()`, die von der Bibliothek bereitgestellt wird. AndroidWir werden dies in unserer onCreate()-Methode tun. Unsere onCreate()-Methode sieht wie folgt aus.
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) var myFruitRecyclerView: RecyclerView = findViewById(R.id.fruitRecyclerView) }
Erstellen Sie einen ViewHolder
Als Nรคchstes erstellen wir einen RecyclerView.ViewHolder, der unsere View in einen ViewHolder umwandelt, den der RecyclerView zur Anzeige unserer Elemente verwendet. Dies geschieht direkt nach der onCreate()-Methode.
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) }
Erstellen Sie einen RecyclerViewAdapter
Als Nรคchstes erstellen wir eine Klasse `FruitArrayAdapter`, die von der Klasse `RecyclerView.Adapter` erbt. Der von uns erstellte `FruitArrayAdapter` ist fรผr Folgendes zustรคndig: Er liest die Fruchtnamen aus dem Frucht-Array, erstellt einen ViewHolder mithilfe unserer Ansicht `one_fruit_view.xml`, bindet die Frรผchte an den ViewHolder und bindet dessen Inhalt dynamisch an die erstellte Ansicht.
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 wird rote Wellenlinien auf unserem FruitArrayAdapter hinzufรผgen, was uns darauf hinweist, dass wir Methoden implementieren mรผssen, die der RecyclerView verwenden kann, um unser Array mit einem ViewHolder zu verbinden.
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) { } }
Wir beginnen mit dem einfachsten Teil des generierten Codes, der Methode `getItemCount()`. Wir wissen, wie wir die Anzahl der Elemente in unserem Array ermitteln, indem wir die Eigenschaft `size` des Arrays aufrufen.
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) { } }
Anschlieรend implementieren wir die Methode `onCreateViewHolder`. Hier bittet uns der RecyclerView um Unterstรผtzung beim Erstellen eines `FruitViewHolder`. Zur Erinnerung: So sah unsere `FruitViewHolder`-Klasse aus:
class FruitViewHolder(fruitView: View): RecyclerView.ViewHolder(fruitView)
Dazu benรถtigen wir unsere fruitView, die wir als XML-Datei one_fruit_view.xml erstellt haben. Wir kรถnnen eine Referenz auf diese XML-Datei erstellen und sie wie folgt in eine View konvertieren.
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) { } }
Der verbleibende Teil ist die รberschreibung von onBindViewHolder.
fun onBindViewHolder(holder: FruitViewHolder, position: Int)
Der RecyclerView.Adapter fragt nach einer Positionsangabe (Integer), mit der wir ein Element aus unserer Liste abrufen. Er stellt uns auรerdem einen ViewHolder bereit, mit dem wir das Element aus dem `fruitArray` an die darin enthaltene View binden kรถnnen. Die View im ViewHolder ist รผber das Feld `ViewHolder.itemView` zugรคnglich. Sobald wir die View haben, kรถnnen wir den Inhalt mithilfe der zuvor erstellten ID `fruitName` festlegen.
override fun onBindViewHolder(holder: FruitViewHolder, position: Int) { var ourFruitTextView = holder.itemView.findViewById<TextView>(R.id.fruitName) var aFruitName = fruitArray.get(position) ourFruitTextView.setText(aFruitName) }
Damit ist unser FruitArrayAdapter fertiggestellt und sieht wie folgt aus.
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) } }
Schlieรlich sind wir bereit, die restlichen Teile unseres RecyclerView zu verbinden. Dazu gehรถrt die Erstellung eines LayoutManagers, der dem RecyclerView mitteilt, wie der Inhalt der Liste angezeigt werden soll, entweder linear mit Hilfe des LinearLayoutManager oder in einem Raster mit Hilfe des GridLayoutManager oder des StaggeredGridLayoutManager.
Erstellen Sie einen Layout-Manager
Wir kehren nun zu unserer onCreate-Funktion zurรผck und fรผgen den LayoutManager hinzu.
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 }
Hรคngen Sie unseren Adapter an Elemente an und stellen Sie ihn auf RecyclerView ein
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 }
Wir haben auรerdem eine Instanz von `fruitListAdapter` erstellt und ihr das Array mit den Fruchtnamen รผbergeben. Damit sind wir im Prinzip fertig. Die vollstรคndige Datei `MainActivity.kt` sieht wie folgt aus.
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) } } }
Laden Sie das Projekt herunter





