Ten moduł Codelab jest częścią kursu Android Kotlin Fundamentals. Najwięcej korzyści przyniesie Ci ukończenie wszystkich ćwiczeń w kolejności. Wszystkie ćwiczenia z tego kursu znajdziesz na stronie docelowej kursu Android Kotlin Fundamentals.
Wprowadzenie
W poprzednich ćwiczeniach z programowania dowiedzieliśmy się, jak pobierać dane z usługi internetowej i analizować odpowiedź, aby uzyskać obiekt danych. W tym ćwiczeniu wykorzystasz tę wiedzę, aby wczytywać i wyświetlać zdjęcia z adresu URL. Przypomnisz sobie też, jak utworzyć RecyclerView i użyć go do wyświetlania siatki obrazów na stronie przeglądu.
Co warto wiedzieć
- Jak tworzyć i używać fragmentów.
- Jak używać komponentów architektury, w tym modeli widoku, fabryk modeli widoku, przekształceń i
LiveData. - Dowiedz się, jak pobierać dane JSON z usługi internetowej REST i analizować je w obiektach Kotlin za pomocą bibliotek Retrofit i Moshi.
- Jak utworzyć układ siatki za pomocą elementu
RecyclerView. - Jak działają
Adapter,ViewHolderiDiffUtil.
Czego się nauczysz
- Jak używać biblioteki Glide do wczytywania i wyświetlania obrazu z adresu URL.
- Jak używać
RecyclerViewi adaptera siatki do wyświetlania siatki obrazów. - Jak radzić sobie z potencjalnymi błędami podczas pobierania i wyświetlania obrazów.
Co musisz zrobić
- Zmodyfikuj aplikację MarsRealEstate, aby pobierać adres URL obrazu z danych nieruchomości na Marsie, a następnie użyj Glide do wczytania i wyświetlenia tego obrazu.
- Dodaj do aplikacji animację ładowania i ikonę błędu.
- Użyj elementu
RecyclerView, aby wyświetlić siatkę obrazów nieruchomości na Marsie. - Dodaj do elementu
RecyclerViewobsługę stanu i błędów.
W tym ćwiczeniu z programowania (i powiązanych z nim ćwiczeniach) będziesz pracować z aplikacją MarsRealEstate, która wyświetla nieruchomości na sprzedaż na Marsie. Aplikacja łączy się z serwerem internetowym, aby pobierać i wyświetlać dane dotyczące nieruchomości, w tym szczegóły takie jak cena i informacje o tym, czy nieruchomość jest dostępna na sprzedaż lub wynajem. Obrazy przedstawiające poszczególne nieruchomości to prawdziwe zdjęcia z Marsa wykonane przez łaziki NASA.

Wersja aplikacji, którą utworzysz w ramach tego ćwiczenia z programowania, wypełni stronę przeglądu, na której wyświetlana jest siatka obrazów. Obrazy są częścią danych usługi, które aplikacja pobiera z usługi internetowej dotyczącej nieruchomości na Marsie. Aplikacja będzie używać biblioteki Glide do wczytywania i wyświetlania obrazów oraz RecyclerView do tworzenia układu siatki obrazów. Aplikacja będzie też prawidłowo obsługiwać błędy sieci.
Wyświetlanie zdjęcia z adresu URL w internecie może wydawać się proste, ale wymaga sporo pracy inżynierskiej, aby działało dobrze. Obraz musi zostać pobrany, zbuforowany i zdekodowany z formatu skompresowanego do formatu, który może być używany przez Androida. Obraz powinien być przechowywany w pamięci podręcznej w pamięci, w pamięci podręcznej opartej na pamięci masowej lub w obu tych miejscach. Wszystko to musi się odbywać w wątkach w tle o niskim priorytecie, aby interfejs użytkownika pozostawał responsywny. Aby uzyskać najlepszą wydajność sieci i procesora, warto też pobierać i dekodować więcej niż 1 obraz naraz. Skuteczne wczytywanie obrazów z sieci to temat na osobne ćwiczenia z programowania.
Na szczęście możesz użyć biblioteki opracowanej przez społeczność o nazwie Glide, aby pobierać, buforować, dekodować i buforować obrazy. Glide znacznie ułatwia pracę w porównaniu z sytuacją, w której musisz wszystko robić od zera.
Glide potrzebuje w zasadzie 2 rzeczy:
- Adres URL obrazu, który chcesz wczytać i wyświetlić.
- Obiekt
ImageViewdo wyświetlania tego obrazu.
W tym zadaniu dowiesz się, jak używać Glide do wyświetlania pojedynczego obrazu z usługi internetowej dotyczącej nieruchomości. Wyświetlasz obraz przedstawiający pierwszą nieruchomość na Marsie na liście nieruchomości zwracanej przez usługę internetową. Oto zrzuty ekranu przed i po:


Krok 1. Dodaj zależność Glide
- Otwórz aplikację MarsRealEstate z ostatnich zajęć. (Jeśli nie masz tej aplikacji, możesz ją pobrać MarsRealEstateNetwork).
- Uruchom aplikację, aby sprawdzić, co robi. (Wyświetla szczegóły tekstowe nieruchomości, która hipotetycznie jest dostępna na Marsie).
- Otwórz plik build.gradle (Module: app).
- W sekcji
dependenciesdodaj ten wiersz dla biblioteki Glide:
implementation "com.github.bumptech.glide:glide:$version_glide"
Zwróć uwagę, że numer wersji jest już zdefiniowany osobno w pliku Gradle projektu.
- Kliknij Synchronizuj teraz, aby ponownie utworzyć projekt z nową zależnością.
Krok 2. Zaktualizuj model widoku
Następnie zaktualizuj klasę OverviewViewModel, aby uwzględnić dane na żywo dotyczące jednej usługi Mars.
- Otwórz pokój
overview/OverviewViewModel.kt. Tuż pod ikonąLiveDatadla_responsedodaj zarówno wewnętrzne (zmienne), jak i zewnętrzne (niezmienne) dane na żywo dla jednego obiektuMarsProperty.
Gdy pojawi się prośba, zaimportuj zajęciaMarsProperty(com.example.android.marsrealestate.network.MarsProperty).
private val _property = MutableLiveData<MarsProperty>()
val property: LiveData<MarsProperty>
get() = _property- W metodzie
getMarsRealEstateProperties()znajdź wiersz w blokutry/catch {}, który ustawia wartość_response.valuena liczbę właściwości. Dodaj test pokazany poniżej. Jeśli dostępne są obiektyMarsProperty, ten test ustawia wartość_propertyLiveDatana pierwszą właściwość wlistResult.
if (listResult.size > 0) {
_property.value = listResult[0]
}Pełny blok try/catch {} wygląda teraz tak:
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}"
}- Otwórz plik
res/layout/fragment_overview.xml. W elemencie<TextView>zmieńandroid:text, aby powiązać go z komponentemimgSrcUrlelementupropertyLiveData:
android:text="@{viewModel.property.imgSrcUrl}"- Uruchom aplikację. W pierwszej usłudze Mars wyświetli się tylko adres URL obrazu.
TextViewDo tej pory udało Ci się tylko skonfigurować model widoku i dane na żywo dla tego adresu URL.

Krok 3. Utwórz adapter powiązania i wywołaj Glide
Masz już URL obrazu do wyświetlenia, więc możesz zacząć korzystać z Glide, aby go wczytać. W tym kroku użyjesz adaptera powiązania, aby pobrać adres URL z atrybutu XML powiązanego z elementem ImageView, a następnie użyjesz biblioteki Glide do wczytania obrazu. Adaptery powiązań to metody rozszerzające, które znajdują się między widokiem a powiązanymi danymi i zapewniają niestandardowe działanie, gdy dane ulegają zmianie. W tym przypadku niestandardowe działanie polega na wywołaniu Glide w celu wczytania obrazu z adresu URL do elementu ImageView.
- Otwórz pokój
BindingAdapters.kt. Ten plik będzie zawierać adaptery powiązań, których używasz w całej aplikacji. - Utwórz funkcję
bindImage(), która przyjmuje parametryImageViewiString. Dodaj do funkcji adnotację@BindingAdapter. Adnotacja@BindingAdapterinformuje o wiązaniu danych, że ten adapter wiązania ma być wykonywany, gdy element XML ma atrybutimageUrl.
Zaimportujandroidx.databinding.BindingAdapteriandroid.widget.ImageView, gdy pojawi się prośba.
@BindingAdapter("imageUrl")
fun bindImage(imgView: ImageView, imgUrl: String?) {
}- Wewnątrz funkcji
bindImage()dodaj bloklet {}dla argumentuimgUrl:
imgUrl?.let {
}- W bloku
let {}dodaj wiersz widoczny poniżej, aby przekonwertować ciąg URL (z pliku XML) na obiektUri. Importujandroidx.core.net.toUrina żądanie.
Chcesz, aby końcowy obiektUriużywał schematu HTTPS, ponieważ serwer, z którego pobierasz obrazy, wymaga tego schematu. Aby użyć schematu HTTPS, dodajbuildUpon.scheme("https")do konstruktoratoUri. MetodatoUri()to funkcja rozszerzenia Kotlina z biblioteki podstawowej Android KTX, więc wygląda, jakby była częścią klasyString.
val imgUri = imgUrl.toUri().buildUpon().scheme("https").build()- W funkcji
let {}wywołaj funkcjęGlide.with(), aby wczytać obraz z obiektuUrido obiektuImageView. W razie potrzeby zaimportujcom.bumptech.glide.Glide.
Glide.with(imgView.context)
.load(imgUri)
.into(imgView)Krok 4. Zaktualizuj układ i fragmenty
Chociaż Glide załadował obraz, nie widać jeszcze niczego. Następnym krokiem jest zaktualizowanie układu i fragmentów za pomocą elementu ImageView, aby wyświetlić obraz.
- Otwórz pokój
res/layout/gridview_item.xml. Jest to plik zasobu układu, którego użyjesz w przypadku każdego elementu wRecyclerVieww dalszej części tego kursu. Używasz go tymczasowo, aby wyświetlić tylko jeden obraz. - Nad elementem
<ImageView>dodaj element<data>dla wiązania danych i powiąż go z klasąOverviewViewModel:
<data>
<variable
name="viewModel"
type="com.example.android.marsrealestate.overview.OverviewViewModel" />
</data>- Dodaj atrybut
app:imageUrldo elementuImageView, aby użyć nowego adaptera powiązania ładowania obrazu:
app:imageUrl="@{viewModel.property.imgSrcUrl}"- Otwórz pokój
overview/OverviewFragment.kt. W metodzieonCreateView()zakomentuj wiersz, który powiększa klasęFragmentOverviewBindingi przypisuje ją do zmiennej powiązania. To tylko tymczasowe działanie. Później wrócisz do tego kroku.
//val binding = FragmentOverviewBinding.inflate(inflater)- Zamiast tego dodaj wiersz, aby zwiększyć liczbę uczniów w klasie
GridViewItemBinding. W razie potrzeby zaimportujcom.example.android.marsrealestate. databinding.GridViewItemBinding.
val binding = GridViewItemBinding.inflate(inflater)- Uruchom aplikację. Na liście wyników powinna się teraz pojawić fotografia obrazu z pierwszego
MarsProperty.
Krok 5. Dodaj proste obrazy ładowania i błędów
Glide może poprawić wrażenia użytkownika, wyświetlając obraz zastępczy podczas ładowania obrazu i obraz błędu, jeśli ładowanie się nie powiedzie, np. gdy obrazu brakuje lub jest uszkodzony. W tym kroku dodasz tę funkcję do adaptera powiązania i układu.
- Otwórz
res/drawable/ic_broken_image.xmli po prawej stronie kliknij kartę Podgląd. W przypadku obrazu błędu używasz ikony uszkodzonego obrazu dostępnej w wbudowanej bibliotece ikon. Ten obiekt rysowalny wektorowo używa atrybutuandroid:tint, aby pokolorować ikonę na szaro.

- Otwórz pokój
res/drawable/loading_animation.xml. Ten obiekt rysowalny to animacja zdefiniowana za pomocą tagu<animate-rotate>. Animacja obraca obrazloading_img.xmlwokół punktu środkowego. (Animacja nie jest widoczna w podglądzie).

- Wróć do pliku
BindingAdapters.kt. W metodziebindImage()zmień wywołanie funkcjiGlide.with()tak, aby wywoływała funkcjęapply()międzyload()ainto(). Importujcom.bumptech.glide.request.RequestOptionsna żądanie.
Ten kod ustawia obraz zastępczy, który ma być używany podczas wczytywania (element rysowalnyloading_animation). Kod ustawia też obraz, który ma być używany, jeśli ładowanie obrazu się nie powiedzie (element rysowalnybroken_image). Pełna metodabindImage()wygląda teraz tak:
@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)
}
}
- Uruchom aplikację. W zależności od szybkości połączenia sieciowego może się na chwilę pojawić obraz wczytywania, gdy Glide pobiera i wyświetla obraz nieruchomości. Nie zobaczysz jeszcze ikony uszkodzonego obrazu, nawet jeśli wyłączysz sieć – naprawisz to w ostatniej części tego laboratorium.
Aplikacja wczytuje teraz informacje o nieruchomości z internetu. Na podstawie danych z pierwszego elementu listy MarsProperty utworzono właściwość LiveData w modelu widoku, a adres URL obrazu z danych tej właściwości został użyty do wypełnienia elementu ImageView. Celem jest jednak wyświetlenie w aplikacji siatki obrazów, więc musisz użyć elementu RecyclerView z elementem GridLayoutManager.
Krok 1. Zaktualizuj model widoku
Obecnie model widoku ma _property LiveData, który zawiera 1 obiekt MarsProperty – pierwszy na liście odpowiedzi z usługi internetowej. W tym kroku zmienisz ten element LiveData, aby zawierał całą listę obiektów MarsProperty.
- Otwórz pokój
overview/OverviewViewModel.kt. - Zmień zmienną prywatną
_propertyna_properties. Zmień typ na listę obiektówMarsProperty.
private val _properties = MutableLiveData<List<MarsProperty>>()- Zastąp zewnętrzne dane bieżące
propertywartościąproperties. Dodaj listę do typuLiveData:
val properties: LiveData<List<MarsProperty>>
get() = _properties- Przewiń w dół do metody
getMarsRealEstateProperties(). W blokutry {}zastąp cały test dodany w poprzednim zadaniu wierszem pokazanym poniżej. Ponieważ zmiennalistResultzawiera listę obiektówMarsProperty, możesz po prostu przypisać ją do zmiennej_properties.valuezamiast sprawdzać, czy odpowiedź jest prawidłowa.
_properties.value = listResultCały blok try/catch wygląda teraz tak:
try {
var listResult = getPropertiesDeferred.await()
_response.value = "Success: ${listResult.size} Mars properties retrieved"
_properties.value = listResult
} catch (e: Exception) {
_response.value = "Failure: ${e.message}"
}Krok 2. Zaktualizuj układy i fragmenty
Następnym krokiem jest zmiana układu aplikacji i fragmentów, aby zamiast widoku pojedynczego obrazu używać widoku recyklera i układu siatki.
- Otwórz pokój
res/layout/gridview_item.xml. Zmień powiązanie danych zOverviewViewModelnaMarsPropertyi zmień nazwę zmiennej na"property".
<variable
name="property"
type="com.example.android.marsrealestate.network.MarsProperty" />- W
<ImageView>zmień atrybutapp:imageUrl, aby odwoływał się do adresu URL obrazu w obiekcieMarsProperty:
app:imageUrl="@{property.imgSrcUrl}"- Otwórz pokój
overview/OverviewFragment.kt. W plikuonCreateview()odkomentuj wiersz, który powiększaFragmentOverviewBinding. Usuń lub zmień w komentarz wiersz, który zwiększa wartośćGridViewBinding. Te zmiany cofną tymczasowe zmiany wprowadzone w ostatnim zadaniu.
val binding = FragmentOverviewBinding.inflate(inflater)
// val binding = GridViewItemBinding.inflate(inflater)- Otwórz pokój
res/layout/fragment_overview.xml. Usuń cały element<TextView>. - Zamiast tego dodaj ten element
<RecyclerView>, który używa elementuGridLayoutManageri układugrid_view_itemdla pojedynczego produktu:
<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" />Krok 3. Dodaj adapter siatki zdjęć
Teraz układ fragment_overview ma RecyclerView, a układ grid_view_item ma 1 ImageView. W tym kroku powiążesz dane z RecyclerView za pomocą adaptera RecyclerView.
- Otwórz pokój
overview/PhotoGridAdapter.kt. - Utwórz klasę
PhotoGridAdapterz parametrami konstruktora podanymi poniżej. KlasaPhotoGridAdapterrozszerza klasęListAdapter, której konstruktor wymaga typu elementu listy, uchwytu widoku i implementacjiDiffUtil.ItemCallback.
Gdy pojawi się prośba, zaimportuj zajęciaandroidx.recyclerview.widget.ListAdaptericom.example.android.marsrealestate.network.MarsProperty. W kolejnych krokach zaimplementujesz pozostałe brakujące części tego konstruktora, które powodują błędy.
class PhotoGridAdapter : ListAdapter<MarsProperty,
PhotoGridAdapter.MarsPropertyViewHolder>(DiffCallback) {
}- Kliknij dowolne miejsce w klasie
PhotoGridAdapteri naciśnijControl+i, aby zaimplementować metodyListAdapter, czylionCreateViewHolder()ionBindViewHolder().
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PhotoGridAdapter.MarsPropertyViewHolder {
TODO("not implemented")
}
override fun onBindViewHolder(holder: PhotoGridAdapter.MarsPropertyViewHolder, position: Int) {
TODO("not implemented")
}- Na końcu definicji klasy
PhotoGridAdapter, po dodanych przed chwilą metodach, dodaj definicję obiektu towarzyszącego dlaDiffCallback, jak pokazano poniżej.
Zaimportujandroidx.recyclerview.widget.DiffUtil, gdy pojawi się prośba.
ObiektDiffCallbackrozszerza obiektDiffUtil.ItemCallbacko typ obiektu, który chcesz porównać –MarsProperty.
companion object DiffCallback : DiffUtil.ItemCallback<MarsProperty>() {
}- Naciśnij
Control+i, aby zaimplementować metody komparatora dla tego obiektu, czyliareItemsTheSame()iareContentsTheSame().
override fun areItemsTheSame(oldItem: MarsProperty, newItem: MarsProperty): Boolean {
TODO("not implemented")
}
override fun areContentsTheSame(oldItem: MarsProperty, newItem: MarsProperty): Boolean {
TODO("not implemented") }- W przypadku metody
areItemsTheSame()usuń TODO. Użyj operatora równości referencyjnej w języku Kotlin (===), który zwraca wartośćtrue, jeśli odwołania do obiektówoldIteminewItemsą takie same.
override fun areItemsTheSame(oldItem: MarsProperty,
newItem: MarsProperty): Boolean {
return oldItem === newItem
}- W przypadku atrybutu
areContentsTheSame()użyj standardowego operatora równości tylko w przypadku identyfikatora atrybutówoldIteminewItem.
override fun areContentsTheSame(oldItem: MarsProperty,
newItem: MarsProperty): Boolean {
return oldItem.id == newItem.id
}- W klasie
PhotoGridAdapter, pod obiektem towarzyszącym, dodaj definicję klasy wewnętrznejMarsPropertyViewHolder, która rozszerza klasęRecyclerView.ViewHolder.
W razie potrzeby zaimportuj klasyandroidx.recyclerview.widget.RecyclerViewicom.example.android.marsrealestate.databinding.GridViewItemBinding.
ZmiennaGridViewItemBindingjest potrzebna do powiązania klasyMarsPropertyz układem, więc przekaż ją do klasyMarsPropertyViewHolder. Ponieważ klasa bazowaViewHolderwymaga widoku w konstruktorze, przekazujesz do niej widok główny powiązania.
class MarsPropertyViewHolder(private var binding:
GridViewItemBinding):
RecyclerView.ViewHolder(binding.root) {
}- W
MarsPropertyViewHolderutwórz metodębind(), która przyjmuje obiektMarsPropertyjako argument i ustawiabinding.propertyna ten obiekt. WywołajexecutePendingBindings()po ustawieniu właściwości, co spowoduje natychmiastowe wykonanie aktualizacji.
fun bind(marsProperty: MarsProperty) {
binding.property = marsProperty
binding.executePendingBindings()
}- W pliku
onCreateViewHolder()usuń komentarz TODO i dodaj wiersz widoczny poniżej. W razie potrzeby zaimportujandroid.view.LayoutInflater.
MetodaonCreateViewHolder()musi zwracać nowy obiektMarsPropertyViewHolderutworzony przez rozwinięcie widokuGridViewItemBindingi użycie obiektuLayoutInflaterz kontekstuViewGrouprodzica.
return MarsPropertyViewHolder(GridViewItemBinding.inflate(
LayoutInflater.from(parent.context)))- W metodzie
onBindViewHolder()usuń komentarz TODO i dodaj wiersze pokazane poniżej. Wywołujesz tu funkcjęgetItem(), aby uzyskać obiektMarsPropertypowiązany z bieżącą pozycjąRecyclerView, a następnie przekazujesz tę właściwość do metodybind()w obiekcieMarsPropertyViewHolder.
val marsProperty = getItem(position)
holder.bind(marsProperty)Krok 4. Dodaj adapter wiążący i połącz części
Na koniec użyj BindingAdapter, aby zainicjować PhotoGridAdapter listą obiektów MarsProperty. Użycie BindingAdapter do ustawienia danych RecyclerView powoduje, że powiązanie danych automatycznie obserwuje LiveData na liście obiektów MarsProperty. Wtedy adapter powiązania jest wywoływany automatycznie, gdy zmieni się lista MarsProperty.
- Otwórz pokój
BindingAdapters.kt. - Na końcu pliku dodaj metodę
bindRecyclerView(), która przyjmuje jako argumenty obiektRecyclerViewi listę obiektówMarsProperty. Dodaj do tej metody adnotację@BindingAdapter.
Wpiszandroidx.recyclerview.widget.RecyclerViewicom.example.android.marsrealestate.network.MarsProperty, gdy pojawi się prośba.
@BindingAdapter("listData")
fun bindRecyclerView(recyclerView: RecyclerView,
data: List<MarsProperty>?) {
}- W funkcji
bindRecyclerView()przekształćrecyclerView.adapternaPhotoGridAdapteri wywołajadapter.submitList()z danymi. Informuje toRecyclerView, kiedy dostępna jest nowa lista.
W razie potrzeby zaimportuj com.example.android.marsrealestate.overview.PhotoGridAdapter.
val adapter = recyclerView.adapter as PhotoGridAdapter
adapter.submitList(data)- Otwórz pokój
res/layout/fragment_overview.xml. Dodaj atrybutapp:listDatado elementuRecyclerViewi ustaw go naviewmodel.propertiesza pomocą powiązania danych.
app:listData="@{viewModel.properties}"- Otwórz pokój
overview/OverviewFragment.kt. WonCreateView(), tuż przed wywołaniem funkcjisetHasOptionsMenu(), zainicjuj wbinding.photosGridadapterRecyclerViewjako nowy obiektPhotoGridAdapter.
binding.photosGrid.adapter = PhotoGridAdapter()- Uruchom aplikację. Powinna wyświetlić się siatka
MarsPropertyobrazów. Podczas przewijania w celu wyświetlenia nowych obrazów aplikacja pokazuje ikonę postępu wczytywania, zanim wyświetli sam obraz. Jeśli włączysz tryb samolotowy, obrazy, które nie zostały jeszcze załadowane, będą wyświetlane jako ikony uszkodzonych obrazów.

Gdy nie można pobrać obrazu, aplikacja MarsRealEstate wyświetla ikonę uszkodzonego obrazu. Gdy jednak nie ma sieci, aplikacja wyświetla pusty ekran.

Nie jest to zbyt wygodne dla użytkowników. W tym zadaniu dodasz podstawową obsługę błędów, aby użytkownik mógł lepiej zrozumieć, co się dzieje. Jeśli internet jest niedostępny, aplikacja wyświetli ikonę błędu połączenia. Podczas pobierania listy MarsProperty aplikacja będzie wyświetlać animację wczytywania.
Krok 1. Dodaj stan do modelu widoku
Na początek utwórz LiveData w modelu widoku, aby reprezentować stan żądania internetowego. Należy wziąć pod uwagę 3 stany: wczytywanie, sukces i niepowodzenie. Stan wczytywania występuje, gdy czekasz na dane w wywołaniu funkcji await().
- Otwórz pokój
overview/OverviewViewModel.kt. U góry pliku (po importach, a przed definicją klasy) dodajenum, aby reprezentować wszystkie dostępne stany:
enum class MarsApiStatus { LOADING, ERROR, DONE }- Zmień nazwy wewnętrznych i zewnętrznych definicji danych na żywo
_responsew całej klasieOverviewViewModelna_status. Ponieważ w tej instrukcji dodano obsługę_propertiesLiveData, pełna odpowiedź usługi internetowej nie została użyta. Aby śledzić bieżący stan, musisz mieć tutajLiveData, więc możesz po prostu zmienić nazwy istniejących zmiennych.
Zmień też typy z String na MarsApiStatus..
private val _status = MutableLiveData<MarsApiStatus>()
val status: LiveData<MarsApiStatus>
get() = _status- Przewiń w dół do metody
getMarsRealEstateProperties()i zaktualizuj_responsedo_statusrównież tutaj. Zmień ciąg znaków"Success"na stanMarsApiStatus.DONE, a ciąg znaków"Failure"naMarsApiStatus.ERROR. - Dodaj stan
MarsApiStatus.LOADINGna górze blokutry {}przed wywołaniem funkcjiawait(). Jest to stan początkowy, gdy korutyna jest uruchomiona i czekasz na dane. Pełny bloktry/catch {}wygląda teraz tak:
try {
_status.value = MarsApiStatus.LOADING
var listResult = getPropertiesDeferred.await()
_status.value = MarsApiStatus.DONE
_properties.value = listResult
} catch (e: Exception) {
_status.value = MarsApiStatus.ERROR
}- Po wystąpieniu stanu błędu w bloku
catch {}ustaw_propertiesLiveDatana pustą listę. Spowoduje to wyczyszczenieRecyclerView.
} catch (e: Exception) {
_status.value = MarsApiStatus.ERROR
_properties.value = ArrayList()
}Krok 2. Dodaj adapter powiązania dla elementu ImageView stanu
W modelu widoku masz teraz stan, ale jest to tylko zestaw stanów. Jak sprawić, aby pojawił się w samej aplikacji? W tym kroku użyjesz ImageView połączonego z powiązaniem danych, aby wyświetlać ikony stanów wczytywania i błędu. Gdy aplikacja jest w stanie ładowania lub błędu, ikona ImageView powinna być widoczna. Po wczytaniu aplikacji ikona ImageView powinna być niewidoczna.
- Otwórz pokój
BindingAdapters.kt. Dodaj nowy adapter powiązań o nazwiebindStatus(), który przyjmuje argumentyImageViewiMarsApiStatus. W razie potrzeby zaimportujcom.example.android.marsrealestate.overview.MarsApiStatus.
@BindingAdapter("marsApiStatus")
fun bindStatus(statusImageView: ImageView,
status: MarsApiStatus?) {
}- Dodaj instrukcję
when {}w metodziebindStatus(), aby przełączać się między różnymi stanami.
when (status) {
}- Wewnątrz
when {}dodaj przypadek stanu wczytywania (MarsApiStatus.LOADING). W tym stanie ustawImageViewna widoczny i przypisz do niego animację wczytywania. Jest to ten sam rysunek animacji, którego używasz w Glide w poprzednim zadaniu. W razie potrzeby zaimportujandroid.view.View.
when (status) {
MarsApiStatus.LOADING -> {
statusImageView.visibility = View.VISIBLE
statusImageView.setImageResource(R.drawable.loading_animation)
}
}- Dodaj przypadek dla stanu błędu, czyli
MarsApiStatus.ERROR. Podobnie jak w przypadku stanuLOADINGustaw stanImageViewna widoczny i ponownie użyj elementu rysowalnego connection-error.
MarsApiStatus.ERROR -> {
statusImageView.visibility = View.VISIBLE
statusImageView.setImageResource(R.drawable.ic_connection_error)
}- Dodaj przypadek dla stanu gotowości, czyli
MarsApiStatus.DONE. Odpowiedź jest prawidłowa, więc wyłącz widoczność stanuImageView, aby go ukryć.
MarsApiStatus.DONE -> {
statusImageView.visibility = View.GONE
}Krok 3. Dodaj widok ImageView stanu do układu
- Otwórz pokój
res/layout/fragment_overview.xml. Pod elementemRecyclerVieww elemencieConstraintLayoutdodaj elementImageViewpokazany poniżej.
Ten elementImageViewma takie same ograniczenia jak elementRecyclerView. Szerokość i wysokość są jednak ustawione nawrap_content, aby wyśrodkować obraz, a nie rozciągnąć go tak, aby wypełniał widok. Zwróć też uwagę na atrybutapp:marsApiStatus, który powoduje, że wywołanie widokuBindingAdapternastępuje, gdy zmieni się właściwość stanu w modelu widoku.
<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}" />- Włącz tryb samolotowy na emulatorze lub urządzeniu, aby zasymulować brak połączenia z siecią. Skompiluj i uruchom aplikację. Zobaczysz, że pojawi się obraz błędu:

- Naciśnij przycisk Wstecz, aby zamknąć aplikację, i wyłącz tryb samolotowy. Użyj ekranu ostatnich aplikacji, aby wrócić do aplikacji. W zależności od szybkości połączenia sieciowego może się pojawić bardzo krótki wskaźnik ładowania, gdy aplikacja wysyła zapytanie do usługi internetowej, zanim zaczną się ładować obrazy.
Projekt Android Studio: MarsRealEstateGrid
- Aby uprościć proces zarządzania obrazami, użyj biblioteki Glide do pobierania, buforowania, dekodowania i buforowania obrazów w aplikacji.
- Aby wczytać obraz z internetu, Glide potrzebuje 2 rzeczy: adresu URL obrazu i obiektu
ImageView, w którym ma umieścić obraz. Aby określić te opcje, użyj metodload()iinto()w Glide. - Adaptery powiązań to metody rozszerzające, które znajdują się między widokiem a powiązanymi z nim danymi. Adaptery powiązań zapewniają niestandardowe działanie, gdy dane ulegają zmianie, np. wywołują Glide, aby wczytać obraz z adresu URL do elementu
ImageView. - Adaptery powiązań to metody rozszerzające oznaczone adnotacją
@BindingAdapter. - Aby dodać opcje do żądania Glide, użyj metody
apply(). Na przykład użyjapply()zplaceholder(), aby określić rysunek wczytywania, aapply()zerror(), aby określić rysunek błędu. - Aby utworzyć siatkę obrazów, użyj
RecyclerViewzGridLayoutManager. - Aby zaktualizować listę właściwości, gdy ulegnie zmianie, użyj adaptera powiązań między elementem
RecyclerViewa układem.
Kurs Udacity:
Dokumentacja dla deweloperów aplikacji na Androida:
Inne:
W tej sekcji znajdziesz listę możliwych zadań domowych dla uczniów, którzy wykonują ten moduł w ramach kursu prowadzonego przez instruktora. Nauczyciel musi:
- W razie potrzeby przypisz pracę domową.
- Poinformuj uczniów, jak przesyłać projekty.
- Oceń zadania domowe.
Instruktorzy mogą korzystać z tych sugestii w dowolnym zakresie i mogą zadawać inne zadania domowe, które uznają za odpowiednie.
Jeśli wykonujesz ten kurs samodzielnie, możesz użyć tych zadań domowych, aby sprawdzić swoją wiedzę.
Odpowiedz na te pytania
Pytanie 1
Której metody Glide używasz, aby wskazać ImageView, który będzie zawierać załadowany obraz?
▢ into()
▢ with()
▢ imageview()
▢ apply()
Pytanie 2
Jak określić obraz zastępczy, który ma się wyświetlać podczas ładowania przez Glide?
▢ Użyj metody into() z elementem rysowalnym.
▢ Użyj RequestOptions() i wywołaj metodę placeholder() z elementem rysowalnym.
▢ Przypisz właściwość Glide.placeholder do elementu rysowalnego.
▢ Użyj RequestOptions() i wywołaj metodę loadingImage() z elementem rysowalnym.
Pytanie 3
Jak wskazać, że metoda jest adapterem wiążącym?
▢ Wywołaj metodę setBindingAdapter() na obiekcie LiveData.
▢ Umieść metodę w pliku Kotlin o nazwie BindingAdapters.kt.
▢ Użyj atrybutu android:adapter w układzie XML.
▢ Dodaj do metody adnotację @BindingAdapter.
Rozpocznij kolejną lekcję:
Linki do innych ćwiczeń z tego kursu znajdziesz na stronie docelowej ćwiczeń z podstaw języka Kotlin na Androidzie.