Dieses Codelab ist Teil des Android Kotlin Fundamentals-Kurss. Sie profitieren von diesem Kurs, wenn Sie nacheinander die Codelabs durcharbeiten. Alle Kurs-Codelabs finden Sie auf der Landingpage für Kotlin-Grundlagen für Android-Entwickler.
Einführung
Im vorherigen Codelab haben Sie gelernt, wie Sie Daten aus einem Webdienst abrufen und die Antwort in ein Datenobjekt parsen. In diesem Codelab baust du auf diesem Wissen auf, um Fotos über eine Web-URL zu laden und anzuzeigen. Sie sehen sich außerdem an, wie Sie einen RecyclerView
erstellen und damit ein Raster mit Bildern auf der Übersichtsseite aufrufen.
Wichtige Informationen
- Fragmente erstellen und verwenden
- Architekturkomponenten verwenden, einschließlich Modellen ansehen, Modellfabriken ansehen, Transformationen und
LiveData
ansehen. - JSON- Inhalt aus einem REST-Webdienst abrufen und mit den Retrofit- und Moshi-Bibliotheken in Kotlin-Objekte parsen.
- So erstellst du ein Rasterlayout mit einem
RecyclerView
. - Funktionsweise von
Adapter
,ViewHolder
undDiffUtil
.
Lerninhalte
- Mit der Glide-Bibliothek ein Bild aus einer Web-URL laden und anzeigen
RecyclerView
- und Rasteradapter für Rasterbilder verwenden- Umgang mit möglichen Fehlern beim Herunterladen und Anzeigen von Bildern
Aufgabe
- Ändern Sie die MarsRealEstate-App, um die Bild-URL aus den Mars-Property-Daten zu erhalten. Mit Glide können Sie das Bild laden und anzeigen.
- Ladesymbol und Fehlersymbol zur App hinzufügen
- Verwenden Sie einen
RecyclerView
, um ein Raster der Mars-Property-Bilder anzuzeigen. - Fügen Sie der
RecyclerView
Status- und Fehlerbehandlung hinzu.
In diesem Codelab und den zugehörigen Codelabs arbeiten Sie mit einer App namens MarsRealEstate, die Eigenschaften zum Verkauf auf Mars zeigt. Die App stellt eine Verbindung zu einem Internetserver her, um Unterkunftsdaten abzurufen und anzuzeigen, einschließlich Details wie dem Preis und ob die Immobilie zum Verkauf oder zur Vermietung verfügbar ist. Die Bilder, die die einzelnen Gebäude repräsentieren, sind echte Fotos des Mars, die von den Mars-Rovern der NASA aufgenommen wurden.
Die Version der Anwendung, die Sie in diesem Codelab erstellen, füllt die Übersichtsseite aus, auf der ein Raster mit Bildern angezeigt wird. Die Bilder sind Teil der Property-Daten, die Ihre App vom Webdienst für Mars-Immobilien erhält. Ihre App verwendet die Glide-Bibliothek zum Laden und Anzeigen der Bilder und eine RecyclerView
zum Erstellen des Raster-Layouts für die Bilder. Außerdem können Netzwerkfehler in Ihrer App problemlos behoben werden.
Das Anzeigen eines Fotos über eine Web-URL kann auf den ersten Blick verständlich aussehen. Trotzdem gibt es einige technische Änderungen, die erforderlich sind, um das Foto anzuzeigen. Es muss aus einem komprimierten Format heruntergeladen, zwischengespeichert und decodiert werden. Dann kann es das Image verwenden, das Android verwenden kann. Das Image sollte in einem In-Memory-Cache oder in einem speicherbasierten Cache gespeichert werden. All das muss in Hintergrundthreads mit niedriger Priorität passieren, damit die Benutzeroberfläche schnell reagiert. Für eine optimale Netzwerk- und CPU-Leistung sollten Sie auch mehrere Images gleichzeitig abrufen und decodieren. Das Laden von Bildern aus dem Netzwerk kann ein Codelab sein.
Sie können aber auch eine von der Community entwickelte Bibliothek namens Glide verwenden, um Bilder herunterzuladen, zu puffern, zu decodieren und im Cache zu speichern. Mit Glide musst du viel weniger Arbeit machen, als wenn du alles von Grund auf selbst machen müsstest.
Glide benötigt im Wesentlichen zwei Dinge:
- Die URL des Bildes, das Sie laden und anzeigen möchten.
- Ein
ImageView
-Objekt zum Anzeigen dieses Bildes
In dieser Aufgabe erfahren Sie, wie Sie mit Glide ein einzelnes Bild des Immobiliendiensts präsentieren. Sie sehen das Bild, das die erste Mars-Property darstellt, in der Liste der Properties, die der Webdienst zurückgibt. Hier sind die Screenshots vor und nach der Änderung:
Schritt 1: Glide-Abhängigkeit hinzufügen
- Öffnen Sie die MarsRealEstate App im letzten Codelab. (Du kannst MarsRealEstateNetworkhier herunterladen, wenn du die App nicht hast.)
- Führen Sie die App aus, um ihre Funktion zu sehen. (Hier sehen Sie Textdetails einer Unterkunft, die auf dem Mars hypothetisch verfügbar ist.)
- Öffnen Sie build.gradle (Modul: App).
- Fügen Sie im Abschnitt
dependencies
die folgende Zeile für die Glide-Bibliothek hinzu:
implementation "com.github.bumptech.glide:glide:$version_glide"
Die Versionsnummer ist in der Gradle-Datei des Projekts bereits separat definiert.
- Klicken Sie auf Jetzt synchronisieren, um das Projekt mit der neuen Abhängigkeit neu zu erstellen.
Schritt 2: Ansichtsmodell aktualisieren
Als Nächstes aktualisiere die Klasse OverviewViewModel
, um Live-Daten für eine einzelne Mars-Property einzuschließen.
- Öffnen Sie
overview/OverviewViewModel.kt
. Fügen Sie unterhalb desLiveData
für die_response
sowohl interne (änderbare) als auch externe (unveränderbare) Live-Daten für ein einzelnesMarsProperty
-Objekt hinzu.
Importieren Sie die KlasseMarsProperty
(com.example.android.marsrealestate.network.MarsProperty
), wenn Sie dazu aufgefordert werden.
private val _property = MutableLiveData<MarsProperty>()
val property: LiveData<MarsProperty>
get() = _property
- Suchen Sie mit der
getMarsRealEstateProperties()
-Methode nach der Zeile innerhalb destry/catch {}
-Blocks, mit der_response.value
auf die Anzahl der Unterkünfte festgelegt wird. Fügen Sie den unten angezeigten Test hinzu. WennMarsProperty
-Objekte verfügbar sind, wird bei diesem Test der Wert von_property
LiveData
auf die erste Property imlistResult
festgelegt.
if (listResult.size > 0) {
_property.value = listResult[0]
}
Der vollständige try/catch {}
-Block sieht jetzt so aus:
try {
var listResult = getPropertiesDeferred.await()
_response.value = "Success: ${listResult.size} Mars properties retrieved"
if (listResult.size > 0) {
_property.value = listResult[0]
}
} catch (e: Exception) {
_response.value = "Failure: ${e.message}"
}
- Öffnen Sie die Datei
res/layout/fragment_overview.xml
. Ändern Sie im<TextView>
-Elementandroid:text
so, dass es an die KomponenteimgSrcUrl
derproperty
-LiveData
gebunden ist:
android:text="@{viewModel.property.imgSrcUrl}"
- Führen Sie die App aus. Mit
TextView
wird nur die URL des Bildes in der ersten Mars-Property angezeigt. Sie haben bereits das Ansichtsmodell und die Live-Daten für diese URL eingerichtet.
Schritt 3: Bindungsadapter erstellen und Glide aufrufen
Sie haben jetzt die URL eines Bilds, das Sie anzeigen können. Mit der Glide-Ladefunktion können Sie dieses Bild jetzt laden. In diesem Schritt verwenden Sie einen Bindungsadapter, um die URL aus einem XML-Attribut abzurufen, das mit einem ImageView
verknüpft ist. Und Sie verwenden Glide, um das Bild zu laden. Bindungsadapter sind Erweiterungsmethoden, die zwischen einer Ansicht und gebundenen Daten liegen, um ein benutzerdefiniertes Verhalten bereitzustellen, wenn sich die Daten ändern. In diesem Fall ist das benutzerdefinierte Verhalten, Glide aufzurufen, damit ein Bild von einer URL in eine ImageView
geladen wird.
- Öffnen Sie
BindingAdapters.kt
. Diese Datei enthält die Bindungsadapter, die Sie in der gesamten App verwenden. - Erstelle eine
bindImage()
-Funktion, die eineImageView
und eineString
als Parameter verwendet. Funktion mit@BindingAdapter
annotieren Die Annotation@BindingAdapter
teilt der Datenbindung mit, dass dieser Bindungsadapter ausgeführt werden soll, wenn ein XML-Element das AttributimageUrl
hat.
Importieren Sieandroidx.databinding.BindingAdapter
undandroid.widget.ImageView
, wenn Sie dazu aufgefordert werden.
@BindingAdapter("imageUrl")
fun bindImage(imgView: ImageView, imgUrl: String?) {
}
- Fügen Sie in der Funktion
bindImage()
einen Blocklet {}
für das ArgumentimgUrl
hinzu:
imgUrl?.let {
}
- Fügen Sie im Block
let {}
die unten gezeigte Zeile ein, um den URL-String (aus dem XML-Code) in einUri
-Objekt zu konvertieren. Importieren Sieandroidx.core.net.toUri
auf Anforderung.
Das endgültigeUri
-Objekt sollte das HTTPS-Schema verwenden, weil der Server, von dem Sie die Bilder abrufen, dieses Schema benötigt. Wenn Sie das HTTPS-Schema verwenden möchten, hängen SiebuildUpon.scheme("https")
an dentoUri
-Builder an. DietoUri()
-Methode ist eine Kotlin-Erweiterungsfunktion aus der Android KTX-Kernbibliothek. Sie sieht also so aus, als wäre sie zurString
-Klasse.
val imgUri = imgUrl.toUri().buildUpon().scheme("https").build()
- Rufen Sie noch innerhalb von
let {}
Glide.with()
auf, um das Bild aus demUri
-Objekt inImageView
zu laden. Importieren Siecom.bumptech.glide.Glide
, wenn Sie dazu aufgefordert werden.
Glide.with(imgView.context)
.load(imgUri)
.into(imgView)
Schritt 4: Layout und Fragmente aktualisieren
Obwohl Glide das Bild geladen hat, ist es noch nichts zu sehen. Als Nächstes musst du das Layout und die Fragmente mit einem ImageView
aktualisieren, um das Bild anzeigen zu lassen.
- Öffnen Sie
res/layout/gridview_item.xml
. Dies ist die Layoutressourcendatei, die du für jedes Element in derRecyclerView
später im Codelab verwendest. Hier wird vorübergehend nur das eine Bild angezeigt. - Fügen Sie über dem Element
<ImageView>
ein<data>
-Element für die Datenbindung hinzu und binden Sie es an die KlasseOverviewViewModel
:
<data>
<variable
name="viewModel"
type="com.example.android.marsrealestate.overview.OverviewViewModel" />
</data>
- Fügen Sie dem Element
ImageView
einapp:imageUrl
-Attribut hinzu, um den neuen Bindungsadapter für das Laden von Bildern zu verwenden:
app:imageUrl="@{viewModel.property.imgSrcUrl}"
- Öffnen Sie
overview/OverviewFragment.kt
. Kommentieren Sie in der MethodeonCreateView()
die Zeile aus, in der die KlasseFragmentOverviewBinding
aufgebläht wird, und weisen Sie sie der Bindungsvariablen zu. Dies ist nur vorübergehend. Du kannst später noch einmal darauf zugreifen.
//val binding = FragmentOverviewBinding.inflate(inflater)
- Fügen Sie eine Zeile hinzu, um stattdessen die Klasse
GridViewItemBinding
zu erhöhen. Importieren Siecom.example.android.marsrealestate. databinding.GridViewItemBinding
, wenn Sie dazu aufgefordert werden.
val binding = GridViewItemBinding.inflate(inflater)
- Führe die App aus. Nun sollte das Foto des ersten
MarsProperty
in der Ergebnisliste zu sehen sein.
Schritt 5: Einfache Lade- und Fehlerbilder hinzufügen
Glide verbessert die Nutzererfahrung, da beim Laden des Bildes ein Platzhalterbild und bei einem Fehler das Fehlerbild angezeigt wird, etwa wenn das Bild fehlt oder beschädigt ist. In diesem Schritt fügen Sie diese Funktion dem Bindungsadapter und dem Layout hinzu.
- Öffnen Sie
res/drawable/ic_broken_image.xml
und klicken Sie rechts auf den Tab Vorschau. Für das Fehlerbild verwenden Sie das Symbol für das fehlerhafte Bild, das in der integrierten Symbolbibliothek verfügbar ist. Diese Vektorgrafik kann mit dem Attribut „android:tint
“ grau dargestellt werden.
- Öffnen Sie
res/drawable/loading_animation.xml
. Diese Auszeichnung ist eine Animation, die mit dem<animate-rotate>
-Tag definiert wird. Die Animation dreht das Bild, d. h.,loading_img.xml
um den Mittelpunkt. Die Animation wird nicht in der Vorschau angezeigt.
- Kehren Sie zur Datei
BindingAdapters.kt
zurück. Aktualisieren Sie in derbindImage()
-Methode den Aufruf vonGlide.with()
, um die Funktionapply()
zwischenload()
undinto()
aufzurufen. Importieren Siecom.bumptech.glide.request.RequestOptions
, wenn Sie dazu aufgefordert werden.
Mit diesem Code wird das Platzhalterbild für das Laden festgelegt, das beim Laden verwendet wird (dieloading_animation
-Leiste). Mit dem Code wird auch ein Bild festgelegt, das verwendet werden soll, wenn beim Laden des Bildes ein Fehler auftritt (diebroken_image
-Leiste). Die vollständige MethodebindImage()
sieht jetzt so aus:
@BindingAdapter("imageUrl")
fun bindImage(imgView: ImageView, imgUrl: String?) {
imgUrl?.let {
val imgUri =
imgUrl.toUri().buildUpon().scheme("https").build()
Glide.with(imgView.context)
.load(imgUri)
.apply(RequestOptions()
.placeholder(R.drawable.loading_animation)
.error(R.drawable.ic_broken_image))
.into(imgView)
}
}
- Führen Sie die App aus. Abhängig von der Geschwindigkeit Ihrer Netzwerkverbindung kann das Ladebild kurz angezeigt werden, während Glide die Property herunterlädt und anzeigt. Allerdings sehen Sie das Symbol für das defekte Bild noch nicht, selbst wenn Sie das Netzwerk deaktivieren. Sie korrigieren das im letzten Teil des Codelabs.
Ihre App lädt jetzt Property-Informationen aus dem Internet. Anhand der Daten aus dem ersten MarsProperty
-Listenelement haben Sie eine LiveData
-Property im Darstellungsmodell erstellt und die Bild-URL aus diesen Property-Daten verwendet, um eine ImageView
zu füllen. Da Ihre App jedoch ein Raster von Bildern enthalten soll, verwenden Sie RecyclerView
mit GridLayoutManager
.
Schritt 1: Modell der Datenansicht aktualisieren
Momentan hat das Ansichtsmodell ein _property
-LiveData
, das ein MarsProperty
-Objekt enthält – das erste in der Antwortliste des Webdiensts. In diesem Schritt ändern Sie LiveData
, sodass die gesamte Liste der MarsProperty
-Objekte enthalten ist.
- Öffnen Sie
overview/OverviewViewModel.kt
. - Ändern Sie die private
_property
-Variable zu_properties
. Ändern Sie den Typ in eine Liste vonMarsProperty
-Objekten.
private val _properties = MutableLiveData<List<MarsProperty>>()
- Ersetze die externen Live-Daten
property
durchproperties
. Fügen Sie die Liste hier auch dem TypLiveData
hinzu:
val properties: LiveData<List<MarsProperty>>
get() = _properties
- Scrollen Sie nach unten zur Methode
getMarsRealEstateProperties()
. Ersetzen Sie imtry {}
-Block den gesamten Test, den Sie in der vorherigen Aufgabe hinzugefügt haben, durch die unten gezeigte Zeile. Weil die VariablelistResult
eine Liste vonMarsProperty
-Objekten enthält, können Sie sie einfach_properties.value
zuweisen, anstatt eine erfolgreiche Antwort zu testen.
_properties.value = listResult
Der gesamte try/catch
-Block sieht jetzt so aus:
try {
var listResult = getPropertiesDeferred.await()
_response.value = "Success: ${listResult.size} Mars properties retrieved"
_properties.value = listResult
} catch (e: Exception) {
_response.value = "Failure: ${e.message}"
}
Schritt 2: Layouts und Fragmente aktualisieren
Als Nächstes ändern Sie das Layout und die Fragmente der App, um eine Recycler- und eine Rasteransicht anstelle der einzelnen Bildansicht zu verwenden.
- Öffnen Sie
res/layout/gridview_item.xml
. Ändern Sie die Datenbindung vonOverviewViewModel
zuMarsProperty
und benennen Sie die Variable in"property"
um.
<variable
name="property"
type="com.example.android.marsrealestate.network.MarsProperty" />
- Ändern Sie im
<ImageView>
das Attributapp:imageUrl
, um auf die Bild-URL im ObjektMarsProperty
zu verweisen:
app:imageUrl="@{property.imgSrcUrl}"
- Öffnen Sie
overview/OverviewFragment.kt
. Heben Sie inonCreateview()
die Kommentarzeile auf, dieFragmentOverviewBinding
aufgebläht. Löschen oder kommentieren Sie die Zeile, dieGridViewBinding
aufgebläht. Dadurch werden die in der letzten Aufgabe vorgenommenen Änderungen rückgängig gemacht.
val binding = FragmentOverviewBinding.inflate(inflater)
// val binding = GridViewItemBinding.inflate(inflater)
- Öffnen Sie
res/layout/fragment_overview.xml
. Das gesamte<TextView>
-Element löschen. - Füge stattdessen dieses
<RecyclerView>
-Element hinzu, das einGridLayoutManager
- und dasgrid_view_item
-Layout für ein einzelnes Element verwendet:
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/photos_grid"
android:layout_width="0dp"
android:layout_height="0dp"
android:padding="6dp"
android:clipToPadding="false"
app:layoutManager=
"androidx.recyclerview.widget.GridLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:spanCount="2"
tools:itemCount="16"
tools:listitem="@layout/grid_view_item" />
Schritt 3: Fotorasteradapter hinzufügen
Jetzt hat das fragment_overview
-Layout ein RecyclerView
, das grid_view_item
-Layout einen einzelnen ImageView
. In diesem Schritt binden Sie die Daten über einen RecyclerView
-Adapter an die RecyclerView
.
- Öffnen Sie
overview/PhotoGridAdapter.kt
. - Erstelle die
PhotoGridAdapter
-Klasse mit den unten aufgeführten Konstruktorparametern. Die KlassePhotoGridAdapter
erweitertListAdapter
, dessen Konstruktor den Listenelementtyp, den Ansichtsinhaber und eineDiffUtil.ItemCallback
-Implementierung benötigt.
Importieren Sie die Klassenandroidx.recyclerview.widget.ListAdapter
undcom.example.android.marsrealestate.network.MarsProperty
, wenn Sie dazu aufgefordert werden. Mit den folgenden Schritten implementierst du die anderen fehlenden Teile dieses Konstruktors, die Fehler verursachen.
class PhotoGridAdapter : ListAdapter<MarsProperty,
PhotoGridAdapter.MarsPropertyViewHolder>(DiffCallback) {
}
- Klicken Sie auf eine beliebige Stelle in der Klasse
PhotoGridAdapter
und drücken SieControl+i
, um dieListAdapter
-Methoden zu implementieren. Dies sindonCreateViewHolder()
undonBindViewHolder()
.
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PhotoGridAdapter.MarsPropertyViewHolder {
TODO("not implemented")
}
override fun onBindViewHolder(holder: PhotoGridAdapter.MarsPropertyViewHolder, position: Int) {
TODO("not implemented")
}
- Fügen Sie am Ende der Klassendefinition
PhotoGridAdapter
nach den gerade hinzugefügten Methoden eine Companion-Objektdefinition fürDiffCallback
hinzu (siehe unten).
Importieren Sieandroidx.recyclerview.widget.DiffUtil
, wenn Sie dazu aufgefordert werden.
Das ObjektDiffCallback
erweitertDiffUtil.ItemCallback
um den zu vergleichenden Objekttyp:MarsProperty
.
companion object DiffCallback : DiffUtil.ItemCallback<MarsProperty>() {
}
- Drücke
Control+i
, um die Vergleichsmethoden für dieses Objekt zu implementieren. Dies sindareItemsTheSame()
undareContentsTheSame()
.
override fun areItemsTheSame(oldItem: MarsProperty, newItem: MarsProperty): Boolean {
TODO("not implemented")
}
override fun areContentsTheSame(oldItem: MarsProperty, newItem: MarsProperty): Boolean {
TODO("not implemented") }
- Entferne die TODO-Methode für die
areItemsTheSame()
-Methode. Verwenden Sie den Kotlin-Operator „<reference equality“ (===
), dertrue
zurückgibt, wenn die Objekte, auf dieoldItem
verweist, undnewItem
identisch sind.
override fun areItemsTheSame(oldItem: MarsProperty,
newItem: MarsProperty): Boolean {
return oldItem === newItem
}
- Für
areContentsTheSame()
verwenden Sie den standardmäßigen Gleichheitsoperator nur für die ID vonoldItem
undnewItem
.
override fun areContentsTheSame(oldItem: MarsProperty,
newItem: MarsProperty): Boolean {
return oldItem.id == newItem.id
}
- Fügen Sie außerdem innerhalb der Klasse
PhotoGridAdapter
unter dem Companion-Objekt eine innere Klassendefinition fürMarsPropertyViewHolder
hinzu, dieRecyclerView.ViewHolder
verlängert.
Importieren Sieandroidx.recyclerview.widget.RecyclerView
undcom.example.android.marsrealestate.databinding.GridViewItemBinding
, wenn Sie dazu aufgefordert werden.
Sie benötigen die VariableGridViewItemBinding
, um dieMarsProperty
an das Layout zu binden. Übergeben Sie daher die Variable anMarsPropertyViewHolder
. Weil die BasisklasseViewHolder
eine Ansicht in ihrem Konstruktor erfordert, wird sie an die Bindungs-Stammansicht übergeben.
class MarsPropertyViewHolder(private var binding:
GridViewItemBinding):
RecyclerView.ViewHolder(binding.root) {
}
- Erstelle in
MarsPropertyViewHolder
einebind()
-Methode, mit der einMarsProperty
-Objekt als Argument verwendet wird undbinding.property
auf dieses Objekt festgelegt wird. Rufen SieexecutePendingBindings()
auf, nachdem Sie die Eigenschaft festgelegt haben. Dadurch wird die Aktualisierung sofort ausgeführt.
fun bind(marsProperty: MarsProperty) {
binding.property = marsProperty
binding.executePendingBindings()
}
- Entfernen Sie in
onCreateViewHolder()
die TODO-Aufgabe und fügen Sie die unten gezeigte Zeile hinzu. Importieren Sieandroid.view.LayoutInflater
, wenn Sie dazu aufgefordert werden.
DieonCreateViewHolder()
-Methode muss ein neuesMarsPropertyViewHolder
zurückgeben, das erstellt wurde, indem derGridViewItemBinding
aufgebläht und derLayoutInflater
aus dem übergeordnetenViewGroup
-Kontext verwendet wurde.
return MarsPropertyViewHolder(GridViewItemBinding.inflate(
LayoutInflater.from(parent.context)))
- Entfernen Sie in der
onBindViewHolder()
-Methode die TODO-Eingabe und fügen Sie die unten angezeigten Zeilen hinzu. Hier rufen SiegetItem()
auf, um dasMarsProperty
-Objekt abzurufen, das mit der aktuellenRecyclerView
-Position verknüpft ist, und diese Property dann an diebind()
-Methode in derMarsPropertyViewHolder
zu übergeben.
val marsProperty = getItem(position)
holder.bind(marsProperty)
Schritt 4: Bindungsadapter hinzufügen und Teile verbinden
Abschließend verwenden Sie einen BindingAdapter
, um den PhotoGridAdapter
mit der Liste der MarsProperty
-Objekte zu initialisieren. Mit einem BindingAdapter
, um die RecyclerView
Daten festzulegen, führt die Datenbindung dazu, dass LiveData
automatisch die Liste der MarsProperty
Objekte überwacht. Der Bindungsadapter wird automatisch aufgerufen, wenn sich die Liste MarsProperty
ändert.
- Öffnen Sie
BindingAdapters.kt
. - Fügen Sie am Ende der Datei eine
bindRecyclerView()
-Methode hinzu, in der einRecyclerView
-Objekt und eine Liste vonMarsProperty
-Objekten als Argumente verwendet werden. Annotieren Sie diese Methode mit einer@BindingAdapter
.
Importieren Sieandroidx.recyclerview.widget.RecyclerView
undcom.example.android.marsrealestate.network.MarsProperty
, wenn Sie dazu aufgefordert werden.
@BindingAdapter("listData")
fun bindRecyclerView(recyclerView: RecyclerView,
data: List<MarsProperty>?) {
}
- Führen Sie in der Funktion
bindRecyclerView()
die OptionrecyclerView.adapter
inPhotoGridAdapter
aus und rufen Sieadapter.submitList()
mit den Daten auf. Dadurch wirdRecyclerView
mitgeteilt, wenn eine neue Liste verfügbar ist.
Importieren Sie com.example.android.marsrealestate.overview.PhotoGridAdapter
, wenn Sie dazu aufgefordert werden.
val adapter = recyclerView.adapter as PhotoGridAdapter
adapter.submitList(data)
- Öffnen Sie
res/layout/fragment_overview.xml
. Fügen Sie dem ElementRecyclerView
das Attributapp:listData
hinzu und setzen Sie es mit Datenbindung aufviewmodel.properties
.
app:listData="@{viewModel.properties}"
- Öffnen Sie
overview/OverviewFragment.kt
. InonCreateView()
, kurz vor dem Aufruf vonsetHasOptionsMenu()
, initialisieren Sie denRecyclerView
-Adapter inbinding.photosGrid
mit einem neuenPhotoGridAdapter
-Objekt.
binding.photosGrid.adapter = PhotoGridAdapter()
- Führen Sie die App aus. Sie sollten ein Raster mit
MarsProperty
Bildern sehen. Während Sie scrollen, um neue Bilder zu sehen, zeigt die App das Ladesymbol an, bevor das Bild selbst angezeigt wird. Wenn Sie den Flugmodus aktivieren, werden Bilder, die noch nicht geladen wurden, als Symbole für fehlerhafte Bilder angezeigt.
In der MarsRealEstate-App wird das Symbol für ein fehlerhaftes Bild angezeigt, wenn ein Bild nicht abgerufen werden kann. Wenn kein Netzwerk vorhanden ist, wird in der App ein leerer Bildschirm angezeigt.
Das ist ein tolles Erlebnis. In dieser Aufgabe fügen Sie eine grundlegende Fehlerbehandlung hinzu, um dem Nutzer einen besseren Überblick über das Problem zu geben. Wenn das Internet nicht verfügbar ist, wird in der App das Symbol für den Verbindungsfehler angezeigt. Während die App die Liste MarsProperty
abruft, zeigt die App die Ladeanimation an.
Schritt 1: Status zum Ansichtsmodell hinzufügen
Zuerst erstellen Sie eine LiveData
im Ansichtsmodell, um den Status der Webanfrage darzustellen. Es gibt drei Status: Laden, Erfolg und Fehler. Der Ladestatus wird durchgeführt, während du beim Anruf an await()
auf Daten wartest.
- Öffnen Sie
overview/OverviewViewModel.kt
. Fügen Sie oben in der Datei (nach den Importen vor der Klassendefinition) einenum
hinzu, das alle verfügbaren Status darstellt:
enum class MarsApiStatus { LOADING, ERROR, DONE }
- Benennen Sie sowohl die interne als auch die externe Live-Definition des
_response
in der KlasseOverviewViewModel
in_status
um. Da du bereits die Unterstützung für_properties
LiveData
in diesem Codelab hinzugefügt hast, wurde die vollständige Webdienstantwort nicht verwendet. Sie benötigen hier einLiveData
-Element, um den aktuellen Status zu verfolgen und einfach die vorhandenen Variablen umzubenennen.
Auch die Typen von String
in MarsApiStatus.
ändern
private val _status = MutableLiveData<MarsApiStatus>()
val status: LiveData<MarsApiStatus>
get() = _status
- Scrollen Sie nach unten zur
getMarsRealEstateProperties()
-Methode und aktualisieren Sie_response
auch in_status
. Ändern Sie den String"Success"
in den StatusMarsApiStatus.DONE
und den String"Failure"
inMarsApiStatus.ERROR
. - Fügen Sie oben im
try {}
-Block einenMarsApiStatus.LOADING
-Status vor dem Aufruf vonawait()
hinzu. Dies ist der ursprüngliche Status, während die Koroutine ausgeführt wird und du auf Daten wartet. Der vollständigetry/catch {}
-Block sieht jetzt so aus:
try {
_status.value = MarsApiStatus.LOADING
var listResult = getPropertiesDeferred.await()
_status.value = MarsApiStatus.DONE
_properties.value = listResult
} catch (e: Exception) {
_status.value = MarsApiStatus.ERROR
}
- Nach dem Fehlerstatus im
catch {}
Block setzen Sie_properties
LiveData
auf eine leere Liste. Dadurch wird derRecyclerView
gelöscht.
} catch (e: Exception) {
_status.value = MarsApiStatus.ERROR
_properties.value = ArrayList()
}
Schritt 2: Bindungsadapter für den Status „ImageView“ hinzufügen
Sie haben jetzt einen Status im Ansichtsmodell, doch es ist nur eine Reihe von Zuständen. Wie sieht es in der App selbst aus? In diesem Schritt verwenden Sie eine ImageView
, die mit Datenbindung verbunden ist, um Symbole für die Lade- und Fehlerstatus anzuzeigen. Wenn sich die App im Lade- oder Fehlerstatus befindet, sollte ImageView
angezeigt werden. Wenn die Anwendung vollständig geladen wurde, sollte ImageView
unsichtbar sein.
- Öffnen Sie
BindingAdapters.kt
. Fügen Sie einen neuen Bindungsadapter mit dem NamenbindStatus()
hinzu, der einenImageView
- und einenMarsApiStatus
-Wert als Argumente verwendet. Importieren Siecom.example.android.marsrealestate.overview.MarsApiStatus
, wenn Sie dazu aufgefordert werden.
@BindingAdapter("marsApiStatus")
fun bindStatus(statusImageView: ImageView,
status: MarsApiStatus?) {
}
- Fügen Sie innerhalb der Methode
bindStatus()
einwhen {}
hinzu, um zwischen den verschiedenen Status zu wechseln.
when (status) {
}
- Fügen Sie innerhalb von
when {}
eine Anfrage für den Ladestatus (MarsApiStatus.LOADING
) hinzu. Setzen Sie für diesen Status den WertImageView
auf „sichtbar“ und weisen Sie ihn der Ladeanimation zu. Dies ist dieselbe Animations-Draming, die Sie in der vorherigen Aufgabe für Glide verwendet haben. Importieren Sieandroid.view.View
, wenn Sie dazu aufgefordert werden.
when (status) {
MarsApiStatus.LOADING -> {
statusImageView.visibility = View.VISIBLE
statusImageView.setImageResource(R.drawable.loading_animation)
}
}
- Fügen Sie einen Fall für den Fehlerstatus hinzu:
MarsApiStatus.ERROR
. Ähnlich wie beim StatusLOADING
kannst du den StatusImageView
so festlegen, dass der Verbindungsfehler sichtbar und wieder sichtbar wird.
MarsApiStatus.ERROR -> {
statusImageView.visibility = View.VISIBLE
statusImageView.setImageResource(R.drawable.ic_connection_error)
}
- Fügen Sie einen Fall für den Status „Fertig“ hinzu. Er lautet
MarsApiStatus.DONE
. Hier haben Sie eine erfolgreiche Antwort. Deaktivieren Sie daher die Sichtbarkeit des StatusImageView
, um sie auszublenden.
MarsApiStatus.DONE -> {
statusImageView.visibility = View.GONE
}
Schritt 3: Status „ImageView“ zum Layout hinzufügen
- Öffnen Sie
res/layout/fragment_overview.xml
. Füge unter demRecyclerView
Element innerhalb vonConstraintLayout
die unten angezeigtenImageView
hinzu.
DieseImageView
hat dieselben Beschränkungen wie dieRecyclerView
. Breite und Höhe nutzen jedochwrap_content
, um das Bild zu zentrieren anstatt es zu strecken. Beachten Sie auch dasapp:marsApiStatus
-Attribut, das den Aufruf IhrerBindingAdapter
aufzeichnet, wenn sich die Status-Property im Datenansichtsmodell ändert.
<ImageView
android:id="@+id/status_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:marsApiStatus="@{viewModel.status}" />
- Aktivieren Sie im Emulator oder Gerät den Flugmodus, um eine fehlende Netzwerkverbindung zu simulieren. Wenn Sie die App kompilieren und ausführen, sehen Sie das Fehlerbild:
- Tippen Sie auf die Schaltfläche „Zurück“, um die App zu schließen, und deaktivieren Sie den Flugmodus. Verwenden Sie das Fenster, wenn Sie die App aufrufen möchten. Je nachdem, wie schnell Ihre Netzwerkverbindung ist, wird möglicherweise ein extrem kurzer Ladesymbol angezeigt.
Android Studio-Projekt: MarsRealEstateGrid
- Mit der Glide-Bibliothek können Sie Bilder in Ihrer App herunterladen, zwischenspeichern, decodieren und im Cache speichern, um die Verwaltung von Bildern zu vereinfachen.
- Glide benötigt zwei Dinge, um ein Bild aus dem Internet zu laden: die URL eines Bilds und ein
ImageView
-Objekt, in das das Bild eingefügt werden soll. Verwende zum Festlegen dieser Optionen die Methodenload()
undinto()
mit Glide. - Bindungsadapter sind Erweiterungsmethoden zwischen einer Ansicht und den gebundenen Daten. Bindungsadapter bieten ein benutzerdefiniertes Verhalten, wenn sich die Daten ändern, z. B. um Glide aufzurufen, um ein Bild von einer URL in eine
ImageView
zu laden. - Bindungsadapter sind Erweiterungsmethoden, die mit der Annotation
@BindingAdapter
versehen sind. - Zum Hinzufügen von Optionen zur Glide-Anfrage verwenden Sie die Methode
apply()
. Beispiel: Sie könnenapply()
zusammen mitplaceholder()
verwenden, um eine Ladedatei anzugeben. Wenn Sieapply()
verwenden, geben Sie eineerror()
-Zeichenleiste an. - Wenn du ein Raster aus Bildern erstellen möchtest, verwende ein
RecyclerView
mit einemGridLayoutManager
. - Wenn Sie die Liste der Unterkünfte ändern möchten, verwenden Sie einen Bindungsadapter zwischen
RecyclerView
und dem Layout.
Udacity-Kurs:
Android-Entwicklerdokumentation:
Sonstiges:
In diesem Abschnitt werden mögliche Hausaufgaben für Schüler oder Studenten aufgeführt, die an diesem von einem Kursleiter geleiteten Codelab arbeiten. Die Lehrkraft kann Folgendes tun:
- Bei Bedarf können Sie die entsprechenden Aufgaben zuweisen.
- Schülern mitteilen, wie sie Aufgaben für die Aufgabe abgeben
- Benoten Sie die Hausaufgaben.
Lehrkräfte können diese Vorschläge so oft oder so oft verwenden, wie sie möchten. anderen Aufgaben können sie nach Belieben zugewiesen werden.
Wenn Sie alleine an diesem Codelab arbeiten, können Sie Ihr Wissen mit diesen Hausaufgaben testen.
Diese Fragen beantworten
Frage 1
Mit welcher Glide-Methode geben Sie das ImageView
an, das das geladene Bild enthalten soll?
▢ into()
▢ with()
▢ imageview()
▢ apply()
Frage 2
Wie gebe ich ein Platzhalterbild an, das angezeigt wird, wenn Glide geladen wird?
▢ Nutze die Methode into()
mit einer Schublade.
▢ Verwende RequestOptions()
und rufe die placeholder()
-Methode mit einer Schublade auf.
▢ Ziehe die Property Glide.placeholder
einer Drawable zu.
▢ Verwende RequestOptions()
und rufe die loadingImage()
-Methode mit einer Schublade auf.
Frage 3
Wie geben Sie an, dass eine Methode ein Bindungsadapter ist?
▢ Rufe die Methode setBindingAdapter()
in LiveData
auf.
▢ Lege die Methode in eine Kotlin-Datei namens BindingAdapters.kt
ein.
▢ Sie verwenden das Attribut android:adapter
im XML-Layout.
▢ Führt die Methode mit @BindingAdapter
an.
Beginnen Sie mit der nächsten Lektion:
Links zu anderen Codelabs in diesem Kurs finden Sie auf der Landingpage zu Kotlin-Grundlagen für Android.