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 w ramach tej lekcji dowiedzieliśmy się, jak pobierać dane o nieruchomościach na Marsie z usługi internetowej i jak tworzyć RecyclerView z układem siatki, aby wczytywać i wyświetlać obrazy z tych danych. W tym laboratorium dokończysz aplikację MarsRealEstate, implementując możliwość filtrowania nieruchomości na Marsie według tego, czy są dostępne do wynajęcia czy do kupienia. Tworzysz też widok szczegółowy, aby po kliknięciu przez użytkownika zdjęcia nieruchomości w widoku ogólnym wyświetlił się widok szczegółowy z informacjami o tej nieruchomości.
Co warto wiedzieć
- Jak tworzyć i używać fragmentów.
- Jak przechodzić między fragmentami i używać Safe Args (wtyczki Gradle) do przekazywania danych między fragmentami.
- Jak używać komponentów architektury, w tym modeli widoku, fabryk modeli widoku, przekształceń i
LiveData. - Dowiedz się, jak pobierać dane zakodowane w formacie JSON z usługi internetowej REST i przetwarzać je na obiekty Kotlin za pomocą bibliotek Retrofit i Moshi.
Czego się nauczysz
- Jak używać złożonych wyrażeń wiązania w plikach układu.
- Jak wysyłać żądania Retrofit do usługi internetowej z opcjami zapytań.
Co musisz zrobić
- Zmodyfikuj aplikację MarsRealEstate, aby oznaczać nieruchomości na Marsie, które są na sprzedaż (a nie na wynajem), ikoną dolara.
- W menu opcji na stronie przeglądu utwórz żądanie usługi internetowej, które filtruje właściwości Marsa według typu.
- Utwórz fragment szczegółowy dla usługi Mars, połącz go z siatką przeglądu za pomocą nawigacji i przekaż do niego dane usługi.
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 oraz informacja, 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. W poprzednich ćwiczeniach utworzyliśmy RecyclerView z układem siatki dla wszystkich zdjęć obiektu:

W tej wersji aplikacji pracujesz z typem nieruchomości (na wynajem lub na sprzedaż) i dodajesz ikonę do układu siatki, aby oznaczyć nieruchomości na sprzedaż:

Zmodyfikuj menu opcji aplikacji, aby filtrować siatkę i wyświetlać tylko te nieruchomości, które są na wynajem lub na sprzedaż:

Na koniec tworzysz widok szczegółowy dla poszczególnych usług i łączysz ikony w siatce przeglądu z tym fragmentem szczegółowym za pomocą nawigacji:

Do tej pory jedyną częścią danych obiektu Mars, której używasz, jest adres URL obrazu obiektu. Dane usługi, które zostały zdefiniowane w MarsProperty, obejmują też identyfikator, cenę i typ (wynajem lub sprzedaż). Przypomnijmy sobie fragment danych JSON, które otrzymujesz z usługi internetowej:
{
"price":8000000,
"id":"424908",
"type":"rent",
"img_src": "http://mars.jpl.nasa.gov/msl-raw-images/msss/01000/mcam/1000ML0044631290305226E03_DXXX.jpg"
},W tym zadaniu zaczniesz pracować z typem usługi Mars, aby dodać obraz znaku dolara do usług na stronie przeglądu, które są na sprzedaż.
Krok 1. Zaktualizuj MarsProperty, aby uwzględnić typ
Klasa MarsProperty określa strukturę danych dla każdej usługi udostępnianej przez usługę internetową. W poprzednim samouczku używaliśmy biblioteki Moshi do analizowania surowej odpowiedzi JSON z usługi internetowej Mars i przekształcania jej w poszczególne obiekty danych MarsProperty.
W tym kroku dodasz do klasy MarsProperty logikę, która będzie wskazywać, czy nieruchomość jest na wynajem (czyli czy typ to ciąg znaków "rent" lub "buy"). Będziesz używać tej logiki w kilku miejscach, więc lepiej umieścić ją w klasie danych niż ją powielać.
- Otwórz aplikację MarsRealEstate z ostatnich zajęć. (Jeśli nie masz aplikacji, możesz pobrać MarsRealEstateGrid).
- Otwórz pokój
network/MarsProperty.kt. Dodaj treść do definicji klasyMarsPropertyi dodaj niestandardowy getter dlaisRental, który zwracatrue, jeśli obiekt jest typu"rent".
data class MarsProperty(
val id: String,
@Json(name = "img_src") val imgSrcUrl: String,
val type: String,
val price: Double) {
val isRental
get() = type == "rent"
}Krok 2. Zaktualizuj układ elementu siatki
Teraz zaktualizuj układ elementu w przypadku siatki obrazów, aby symbol dolara był widoczny tylko na tych zdjęciach nieruchomości, które są na sprzedaż:

Dzięki wyrażeniom wiązania danych możesz przeprowadzić ten test w całości w pliku układu XML elementów siatki.
- Otwórz pokój
res/layout/grid_view_item.xml. Jest to plik układu dla każdej komórki w układzie siatki w przypadkuRecyclerView. Obecnie plik zawiera tylko element<ImageView>dla obrazu usługi. - Wewnątrz elementu
<data>dodaj element<import>dla klasyView. Importy są używane, gdy chcesz użyć komponentów klasy w wyrażeniu wiązania danych w pliku układu. W tym przypadku użyjesz stałychView.GONEiView.VISIBLE, więc musisz mieć dostęp do klasyView.
<import type="android.view.View"/>- Obejmij cały widok obrazu tagiem
FrameLayout, aby umożliwić umieszczenie rysunku znaku dolara na obrazie nieruchomości.
<FrameLayout
android:layout_width="match_parent"
android:layout_height="170dp">
<ImageView
android:id="@+id/mars_image"
...
</FrameLayout>- W przypadku elementu
ImageViewzmień atrybutandroid:layout_heightnamatch_parent, aby wypełnić nowy element nadrzędnyFrameLayout.
android:layout_height="match_parent"- Dodaj drugi element
<ImageView>tuż pod pierwszym, w sekcjiFrameLayout. Skorzystaj z definicji podanej poniżej. Ten obraz pojawia się w prawym dolnym rogu elementu siatki, nad obrazem Marsa, i używa elementu rysowalnego zdefiniowanego wres/drawable/ic_for_sale_outline.xmldla ikony dolara.
<ImageView
android:id="@+id/mars_property_type"
android:layout_width="wrap_content"
android:layout_height="45dp"
android:layout_gravity="bottom|end"
android:adjustViewBounds="true"
android:padding="5dp"
android:scaleType="fitCenter"
android:src="@drawable/ic_for_sale_outline"
tools:src="@drawable/ic_for_sale_outline"/>- Dodaj atrybut
android:visibilitydo widoku obrazumars_property_type. Użyj wyrażenia wiążącego, aby sprawdzić typ usługi, i przypisz widoczność do wartościView.GONE(w przypadku wypożyczenia) lubView.VISIBLE(w przypadku zakupu).
android:visibility="@{property.rental ? View.GONE : View.VISIBLE}"Do tej pory wyrażenia wiążące były widoczne tylko w układach, które używają poszczególnych zmiennych zdefiniowanych w elemencie <data>. Wyrażenia wiążące są bardzo przydatne i umożliwiają wykonywanie operacji takich jak testy i obliczenia matematyczne w układzie XML. W tym przypadku używasz operatora trójargumentowego (?:), aby przeprowadzić test (czy ten obiekt jest wypożyczalnią?). Podajesz 1 wynik dla wartości „prawda” (ukryj ikonę dolara za pomocą View.GONE) i 1 wynik dla wartości „fałsz” (wyświetl tę ikonę za pomocą View.VISIBLE).
Nowy, kompletny plik grid_view_item.xml jest widoczny poniżej:
<layout 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">
<data>
<import type="android.view.View"/>
<variable
name="property"
type="com.example.android.marsrealestate.network.MarsProperty" />
</data>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="170dp">
<ImageView
android:id="@+id/mars_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:adjustViewBounds="true"
android:padding="2dp"
app:imageUrl="@{property.imgSrcUrl}"
tools:src="@tools:sample/backgrounds/scenic"/>
<ImageView
android:id="@+id/mars_property_type"
android:layout_width="wrap_content"
android:layout_height="45dp"
android:layout_gravity="bottom|end"
android:adjustViewBounds="true"
android:padding="5dp"
android:scaleType="fitCenter"
android:src="@drawable/ic_for_sale_outline"
android:visibility="@{property.rental ? View.GONE : View.VISIBLE}"
tools:src="@drawable/ic_for_sale_outline"/>
</FrameLayout>
</layout>- Skompiluj i uruchom aplikację. Zwróć uwagę, że obiekty, które nie są wynajmowane, mają ikonę dolara.

Obecnie w siatce przeglądu aplikacja wyświetla wszystkie usługi Mars. Jeśli użytkownik szuka nieruchomości na Marsie, ikony wskazujące, które z dostępnych nieruchomości są na sprzedaż, byłyby przydatne, ale na stronie nadal jest wiele nieruchomości, przez które trzeba przewijać. W tym zadaniu dodasz do fragmentu przeglądu menu opcji, które umożliwi użytkownikowi wyświetlanie tylko wynajmowanych nieruchomości, tylko nieruchomości na sprzedaż lub wszystkich nieruchomości.

Możesz to zrobić, testując typ każdego elementu MarsProperty w siatce przeglądu i wyświetlając tylko pasujące właściwości. Prawdziwa usługa internetowa Mars ma jednak parametr zapytania lub opcję (o nazwie filter), która umożliwia pobieranie tylko właściwości typu rent lub buy. Możesz użyć tego zapytania filtra z adresem URL usługi internetowej realestate w przeglądarce w ten sposób:
https://android-kotlin-fun-mars-server.appspot.com/realestate?filter=buyW tym zadaniu zmodyfikujesz klasę MarsApiService, aby dodać opcję zapytania do żądania usługi internetowej za pomocą Retrofit. Następnie podłączasz menu opcji, aby ponownie pobrać wszystkie dane dotyczące nieruchomości na Marsie za pomocą tej opcji zapytania. Odpowiedź z usługi internetowej zawiera tylko właściwości, które Cię interesują, więc nie musisz w ogóle zmieniać logiki wyświetlania widoku w siatce przeglądu.
Krok 1. Zaktualizuj usługę Mars API
Aby zmienić żądanie, musisz wrócić do klasy MarsApiService, którą zaimplementowano w pierwszym ćwiczeniu z tej serii. Modyfikujesz klasę, aby udostępnić interfejs API filtrowania.
- Otwórz pokój
network/MarsApiService.kt. Tuż pod instrukcjami importu utwórzenumo nazwieMarsApiFilter, aby zdefiniować stałe pasujące do wartości zapytań, których oczekuje usługa internetowa.
enum class MarsApiFilter(val value: String) {
SHOW_RENT("rent"),
SHOW_BUY("buy"),
SHOW_ALL("all") }- Zmodyfikuj metodę
getProperties(), aby przyjmowała ciąg znaków jako dane wejściowe zapytania filtra, i oznacz te dane wejściowe za pomocą@Query("filter"), jak pokazano poniżej.
Gdy pojawi się prośba, zaimportujretrofit2.http.Query.
Adnotacja@Queryinformuje metodęgetProperties()(a tym samym Retrofit), aby wysłać żądanie do usługi internetowej z opcją filtra. Za każdym razem, gdy wywoływana jest funkcjagetProperties(), adres URL żądania zawiera część?filter=type, która nakazuje usłudze internetowej zwrócenie wyników pasujących do tego zapytania.
fun getProperties(@Query("filter") type: String): Krok 2. Zaktualizuj model widoku ogólnego
W metodzie getMarsRealEstateProperties() w OverviewViewModel wysyłasz do MarsApiService prośbę o dane. Teraz musisz zaktualizować tę prośbę, aby uwzględnić argument filtra.
- Otwórz pokój
overview/OverviewViewModel.kt. W Android Studio zobaczysz błędy spowodowane zmianami wprowadzonymi w poprzednim kroku. DodajMarsApiFilter(wyliczenie możliwych wartości filtra) jako parametr do wywołaniagetMarsRealEstateProperties().
Zaimportujcom.example.android.marsrealestate.network.MarsApiFilter, gdy pojawi się prośba.
private fun getMarsRealEstateProperties(filter: MarsApiFilter) {- Zmodyfikuj wywołanie
getProperties()w usłudze Retrofit, aby przekazywać to zapytanie filtra jako ciąg znaków.
var getPropertiesDeferred = MarsApi.retrofitService.getProperties(filter.value)- W bloku
init {}przekażMarsApiFilter.SHOW_ALLjako argument dogetMarsRealEstateProperties(), aby wyświetlić wszystkie usługi przy pierwszym wczytaniu aplikacji.
init {
getMarsRealEstateProperties(MarsApiFilter.SHOW_ALL)
}- Na końcu klasy dodaj metodę
updateFilter(), która przyjmuje argumentMarsApiFilteri wywołuje metodęgetMarsRealEstateProperties()z tym argumentem.
fun updateFilter(filter: MarsApiFilter) {
getMarsRealEstateProperties(filter)
}Krok 3. Połącz fragment z menu opcji
Ostatnim krokiem jest połączenie menu dodatkowego z fragmentem, aby wywoływać funkcję updateFilter() w modelu widoku, gdy użytkownik wybierze opcję menu.
- Otwórz pokój
res/menu/overflow_menu.xml. Aplikacja MarsRealEstate ma menu dodatkowe z 3 opcjami: wyświetlanie wszystkich nieruchomości, wyświetlanie tylko nieruchomości na wynajem i wyświetlanie tylko nieruchomości na sprzedaż.
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/show_all_menu"
android:title="@string/show_all" />
<item
android:id="@+id/show_rent_menu"
android:title="@string/show_rent" />
<item
android:id="@+id/show_buy_menu"
android:title="@string/show_buy" />
</menu>- Otwórz pokój
overview/OverviewFragment.kt. Na koniec zajęć zaimplementuj metodęonOptionsItemSelected(), aby obsługiwać wybór pozycji menu.
override fun onOptionsItemSelected(item: MenuItem): Boolean {
} - W
onOptionsItemSelected()wywołaj metodęupdateFilter()w modelu widoku z odpowiednim filtrem. Użyj blokuwhen {}w języku Kotlin, aby przełączać się między opcjami. UżyjMarsApiFilter.SHOW_ALLjako domyślnej wartości filtra. Zwróćtrue, ponieważ element menu został obsłużony. Na żądanie zaimportujMarsApiFilter(com.example.android.marsrealestate.network.MarsApiFilter). Pełna metodaonOptionsItemSelected()jest pokazana poniżej.
override fun onOptionsItemSelected(item: MenuItem): Boolean {
viewModel.updateFilter(
when (item.itemId) {
R.id.show_rent_menu -> MarsApiFilter.SHOW_RENT
R.id.show_buy_menu -> MarsApiFilter.SHOW_BUY
else -> MarsApiFilter.SHOW_ALL
}
)
return true
}- Skompiluj i uruchom aplikację. Aplikacja uruchomi pierwszą siatkę przeglądową ze wszystkimi typami nieruchomości, a nieruchomości na sprzedaż będą oznaczone ikoną dolara.
- W menu opcji wybierz Wypożycz. Usługi zostaną ponownie załadowane i przy żadnej z nich nie będzie widoczna ikona dolara. (Wyświetlane są tylko obiekty do wynajęcia). Odświeżenie ekranu i wyświetlenie tylko odfiltrowanych usług może potrwać kilka chwil.
- W menu opcji wybierz Kup. Usługi zostaną ponownie załadowane i przy każdej z nich pojawi się ikona dolara. (Wyświetlane są tylko nieruchomości na sprzedaż).
Teraz masz przewijaną siatkę ikon właściwości Marsa, ale czas na więcej szczegółów. W tym zadaniu dodasz fragment szczegółów, aby wyświetlić informacje o konkretnej nieruchomości. W szczegółowym fragmencie wyświetli się większy obraz, cena i rodzaj nieruchomości – czy jest ona na wynajem, czy na sprzedaż.

Ten fragment jest uruchamiany, gdy użytkownik kliknie obraz w siatce podglądu. Aby to zrobić, musisz dodać onClick do RecyclerView elementów siatki detektor, a następnie przejść do nowego fragmentu. Nawigacja odbywa się przez wywołanie zmiany LiveData w ViewModel, tak jak w przypadku poprzednich lekcji. Użyjesz też wtyczki Safe Args komponentu Navigation, aby przekazać wybrane informacje MarsProperty z fragmentu z ogólnymi informacjami do fragmentu ze szczegółami.
Krok 1. Utwórz model widoku szczegółów i zaktualizuj układ szczegółów
Podobnie jak w przypadku modelu widoku i fragmentów widoku ogólnego musisz teraz zaimplementować model widoku i pliki układu dla fragmentu szczegółów.
- Otwórz pokój
detail/DetailViewModel.kt. Podobnie jak pliki Kotlin związane z siecią znajdują się w folderzenetwork, a pliki przeglądu w folderzeoverview, folderdetailzawiera pliki powiązane z widokiem szczegółowym. Zwróć uwagę, że klasaDetailViewModel(obecnie pusta) przyjmuje w konstruktorze parametrmarsProperty.
class DetailViewModel( marsProperty: MarsProperty,
app: Application) : AndroidViewModel(app) {
}- W definicji klasy dodaj
LiveDatadla wybranej właściwości Mars, aby udostępnić te informacje w widoku szczegółowym. Postępuj zgodnie ze zwykłym wzorcem tworzeniaMutableLiveData, które będzie zawierać samoMarsProperty, a następnie udostępnij niezmienną publiczną właściwośćLiveData.
Zaimportujandroidx.lifecycle.LiveDataiandroidx.lifecycle.MutableLiveData, gdy pojawi się prośba.
private val _selectedProperty = MutableLiveData<MarsProperty>()
val selectedProperty: LiveData<MarsProperty>
get() = _selectedProperty- Utwórz blok
init {}i ustaw wartość wybranej właściwości Mars za pomocą obiektuMarsPropertyz konstruktora.
init {
_selectedProperty.value = marsProperty
}- Otwórz
res/layout/fragment_detail.xmli wyświetl go w widoku projektu.
Jest to plik układu fragmentu szczegółów. ZawieraImageViewduże zdjęcie,TextViewtyp nieruchomości (na wynajem lub na sprzedaż) iTextViewcenę. Zwróć uwagę, że układ ograniczeń jest opakowany elementemScrollView, dzięki czemu będzie się automatycznie przewijać, jeśli widok stanie się zbyt duży, aby zmieścić się na ekranie, np. gdy użytkownik wyświetli go w trybie poziomym. - Otwórz kartę Tekst w przypadku układu. U góry układu, tuż przed elementem
<ScrollView>, dodaj element<data>, aby powiązać model widoku szczegółów z układem.
<data>
<variable
name="viewModel"
type="com.example.android.marsrealestate.detail.DetailViewModel" />
</data>- Dodaj atrybut
app:imageUrldo elementuImageView. Ustaw go naimgSrcUrlz wybranej usługi modelu widoku.
Adapter powiązania, który wczytuje obraz za pomocą Glide, będzie tu również używany automatycznie, ponieważ obserwuje wszystkie atrybutyapp:imageUrl.
app:imageUrl="@{viewModel.selectedProperty.imgSrcUrl}"Krok 2. Określ nawigację w modelu widoku przeglądu
Gdy użytkownik kliknie zdjęcie w modelu przeglądu, powinno to spowodować przejście do fragmentu, który zawiera szczegóły klikniętego elementu.
- Otwórz pokój
overview/OverviewViewModel.kt. Dodaj właściwość_navigateToSelectedPropertyMutableLiveDatai udostępnij ją za pomocą niezmiennej wartościLiveData.
Gdy wartośćLiveDatazmieni się na inną niż null, nastąpi nawigacja. (Wkrótce dodasz kod, aby obserwować tę zmienną i wywoływać nawigację).
private val _navigateToSelectedProperty = MutableLiveData<MarsProperty>()
val navigateToSelectedProperty: LiveData<MarsProperty>
get() = _navigateToSelectedProperty- Na końcu klasy dodaj metodę
displayPropertyDetails(), która ustawia _navigateToSelectedPropertyna wybraną właściwość Marsa.
fun displayPropertyDetails(marsProperty: MarsProperty) {
_navigateToSelectedProperty.value = marsProperty
}- Dodaj metodę
displayPropertyDetailsComplete(), która ustawia wartość_navigateToSelectedPropertyna null. Jest to potrzebne, aby oznaczyć stan nawigacji jako ukończony i uniknąć ponownego wywołania nawigacji, gdy użytkownik wróci z widoku szczegółów.
fun displayPropertyDetailsComplete() {
_navigateToSelectedProperty.value = null
}Krok 3. Skonfiguruj odbiorniki kliknięć w adapterze siatki i fragmencie
- Otwórz pokój
overview/PhotoGridAdapter.kt. Na końcu zajęć utwórz niestandardową klasęOnClickListener, która przyjmuje lambdę z parametremmarsProperty. W klasie zdefiniuj funkcjęonClick(), która jest ustawiona na parametr lambda.
class OnClickListener(val clickListener: (marsProperty:MarsProperty) -> Unit) {
fun onClick(marsProperty:MarsProperty) = clickListener(marsProperty)
}- Przewiń w górę do definicji klasy
PhotoGridAdapteri dodaj do konstruktora prywatną właściwośćOnClickListener.
class PhotoGridAdapter( private val onClickListener: OnClickListener ) :
ListAdapter<MarsProperty,
PhotoGridAdapter.MarsPropertyViewHolder>(DiffCallback) {- Spraw, aby zdjęcie było klikalne, dodając
onClickListenerdo elementu siatki w metodzieonBindviewHolder(). Zdefiniuj odbiornik kliknięć między wywołaniami funkcjigetItem() and bind().
override fun onBindViewHolder(holder: MarsPropertyViewHolder, position: Int) {
val marsProperty = getItem(position)
holder.itemView.setOnClickListener {
onClickListener.onClick(marsProperty)
}
holder.bind(marsProperty)
}- Otwórz pokój
overview/OverviewFragment.kt. W metodzieonCreateView()zastąp wiersz, który inicjuje właściwośćbinding.photosGrid.adapter, wierszem podanym poniżej.
Ten kod dodaje obiektPhotoGridAdapter.onClickListenerdo konstruktoraPhotoGridAdapteri wywołuje funkcjęviewModel.displayPropertyDetails()z przekazanym obiektemMarsProperty. Spowoduje to wywołanie funkcjiLiveDataw modelu widoku nawigacji.
binding.photosGrid.adapter = PhotoGridAdapter(PhotoGridAdapter.OnClickListener {
viewModel.displayPropertyDetails(it)
})Krok 4. Zmodyfikuj wykres nawigacji i spraw, aby MarsProperty można było przekazywać
Gdy użytkownik kliknie zdjęcie w siatce podglądu, aplikacja powinna przejść do fragmentu szczegółów i przekazać szczegóły wybranej nieruchomości na Marsie, aby widok szczegółów mógł wyświetlić te informacje.

Obecnie masz odbiornik kliknięć z PhotoGridAdapter, który obsługuje kliknięcie, oraz sposób wywoływania nawigacji z modelu widoku. Nie masz jeszcze obiektu MarsProperty przekazywanego do fragmentu szczegółów. W tym celu użyj Safe Args z komponentu nawigacji.
- Otwórz pokój
res/navigation/nav_graph.xml. Kliknij kartę Tekst, aby wyświetlić kod XML wykresu nawigacji. - W elemencie
<fragment>fragmentu szczegółów dodaj element<argument>pokazany poniżej. Ten argument, o nazwieselectedProperty, jest typuMarsProperty.
<argument
android:name="selectedProperty"
app:argType="com.example.android.marsrealestate.network.MarsProperty"
/>- Skompiluj aplikację. Nawigacja zgłasza błąd, ponieważ
MarsPropertynie jest możliwy do przekazania. InterfejsParcelableumożliwia serializację obiektów, dzięki czemu dane obiektów mogą być przekazywane między fragmentami lub aktywnościami. W tym przypadku, aby dane w obiekcieMarsPropertymogły zostać przekazane do fragmentu szczegółów za pomocą Safe Args, obiektMarsPropertymusi implementować interfejsParcelable. Dobra wiadomość jest taka, że Kotlin udostępnia łatwy skrót do implementowania tego interfejsu. - Otwórz pokój
network/MarsProperty.kt. Dodaj adnotację@Parcelizedo definicji klasy.
W razie potrzeby zaimportujkotlinx.android.parcel.Parcelize.
Adnotacja@Parcelizeużywa rozszerzeń Kotlin Android do automatycznego wdrażania metod wParcelableinterfejsie dla tej klasy. Nie musisz nic więcej robić.
@Parcelize
data class MarsProperty (- Zmień definicję klasy
MarsProperty, aby rozszerzyćParcelable.
Na żądanie zaimportujandroid.os.Parcelable.
Definicja klasyMarsPropertywygląda teraz tak:
@Parcelize
data class MarsProperty (
val id: String,
@Json(name = "img_src") val imgSrcUrl: String,
val type: String,
val price: Double) : Parcelable {Krok 5. Połącz fragmenty
Nadal nie nawigujesz – rzeczywista nawigacja odbywa się we fragmentach. W tym kroku dodasz ostatnie elementy, które umożliwią nawigację między fragmentami przeglądu i szczegółów.
- Otwórz pokój
overview/OverviewFragment.kt. WonCreateView()poniżej wierszy, które inicjują adapter siatki zdjęć, dodaj wiersze pokazane poniżej, aby obserwowaćnavigatedToSelectedPropertyz modelu widoku przeglądu.
Wpiszandroidx.lifecycle.Observeriandroidx.navigation.fragment.findNavController, gdy pojawi się prośba.
Obserwator sprawdza, czyMarsProperty–itw funkcji lambda – nie ma wartości null. Jeśli tak, pobiera kontroler nawigacji z fragmentu zfindNavController(). WywołajdisplayPropertyDetailsComplete(), aby poinformować model widoku o zresetowaniuLiveDatado stanu null, dzięki czemu po powrocie aplikacji doOverviewFragmentnie nastąpi przypadkowe ponowne uruchomienie nawigacji.
viewModel.navigateToSelectedProperty.observe(this, Observer {
if ( null != it ) {
this.findNavController().navigate(
OverviewFragmentDirections.actionShowDetail(it))
viewModel.displayPropertyDetailsComplete()
}
})- Otwórz pokój
detail/DetailFragment.kt. Dodaj ten wiersz tuż pod wywołaniem funkcjisetLifecycleOwner()w metodzieonCreateView(). Ta linia pobiera wybranyMarsPropertyobiekt z Safe Args
.Zwróć uwagę na użycie operatora potwierdzenia, że wartość nie jest null (!!). JeśliselectedPropertynie ma, stało się coś strasznego i chcesz, aby kod zgłosił wskaźnik null. (W kodzie produkcyjnym należy w jakiś sposób obsłużyć ten błąd).
val marsProperty = DetailFragmentArgs.fromBundle(arguments!!).selectedProperty- Następnie dodaj ten wiersz, aby uzyskać nowy
DetailViewModelFactory. UżyjDetailViewModelFactory, aby uzyskać instancjęDetailViewModel. Aplikacja startowa zawiera implementacjęDetailViewModelFactory, więc wystarczy ją zainicjować.
val viewModelFactory = DetailViewModelFactory(marsProperty, application)- Na koniec dodaj tę linię, aby uzyskać
DetailViewModelz fabryki i połączyć wszystkie części.
binding.viewModel = ViewModelProviders.of(
this, viewModelFactory).get(DetailViewModel::class.java)- Skompiluj i uruchom aplikację, a potem kliknij dowolne zdjęcie nieruchomości na Marsie. Pojawi się fragment ze szczegółami tej usługi. Kliknij przycisk Wstecz, aby wrócić na stronę przeglądu. Zauważ, że ekran szczegółów jest nadal dość pusty. W następnym zadaniu dokończysz dodawanie danych usługi na tej stronie szczegółów.
Obecnie na stronie z informacjami wyświetlane jest tylko to samo zdjęcie Marsa, które zwykle widzisz na stronie przeglądu. Klasa MarsProperty ma też typ nieruchomości (do wynajęcia lub na sprzedaż) i cenę. Ekran szczegółów powinien zawierać obie te wartości. Warto też, aby w przypadku nieruchomości na wynajem było zaznaczone, że cena jest podana za miesiąc. Do obu tych czynności używasz przekształceń LiveData w modelu widoku.
- Otwórz pokój
res/values/strings.xml. Kod początkowy zawiera zasoby ciągów tekstowych (widoczne poniżej), które pomogą Ci utworzyć ciągi tekstowe dla widoku szczegółów. W przypadku ceny użyjesz zasobudisplay_price_monthly_rentallub zasobudisplay_pricew zależności od typu usługi.
<string name="type_rent">Rent</string>
<string name="type_sale">Sale</string>
<string name="display_type">For %s</string>
<string name="display_price_monthly_rental">$%,.0f/month</string>
<string name="display_price">$%,.0f</string>- Otwórz pokój
detail/DetailViewModel.kt. Na dole klasy dodaj kod widoczny poniżej.
Zaimportujandroidx.lifecycle.Transformations, jeśli pojawi się prośba.
Ta transformacja sprawdza, czy wybrana nieruchomość jest wynajmowana, przy użyciu tego samego testu co w pierwszym zadaniu. Jeśli usługa jest wynajmowana, przekształcenie wybiera odpowiedni ciąg znaków z zasobów za pomocą przełącznika Kotlinwhen {}. Oba te ciągi znaków muszą kończyć się liczbą, więc po nich dodajproperty.price.
val displayPropertyPrice = Transformations.map(selectedProperty) {
app.applicationContext.getString(
when (it.isRental) {
true -> R.string.display_price_monthly_rental
false -> R.string.display_price
}, it.price)
}- Aby uzyskać dostęp do zasobów ciągów w projekcie, zaimportuj wygenerowaną klasę
R.
import com.example.android.marsrealestate.R- Po
displayPropertyPriceprzekształceniu dodaj kod widoczny poniżej. Ta transformacja łączy kilka zasobów tekstowych w zależności od tego, czy typem usługi jest wynajem.
val displayPropertyType = Transformations.map(selectedProperty) {
app.applicationContext.getString(R.string.display_type,
app.applicationContext.getString(
when (it.isRental) {
true -> R.string.type_rent
false -> R.string.type_sale
}))
}- Otwórz pokój
res/layout/fragment_detail.xml. Pozostała tylko jedna rzecz do zrobienia: powiązać nowe ciągi tekstowe (utworzone za pomocąLiveDataprzekształceń) z widokiem szczegółów. Aby to zrobić, ustaw wartość pola tekstowego dla tekstu typu nieruchomości naviewModel.displayPropertyType, a pola tekstowego dla tekstu wartości ceny naviewModel.displayPropertyPrice.
<TextView
android:id="@+id/property_type_text"
...
android:text="@{viewModel.displayPropertyType}"
...
tools:text="To Rent" />
<TextView
android:id="@+id/price_value_text"
...
android:text="@{viewModel.displayPropertyPrice}"
...
tools:text="$100,000" />- Skompiluj i uruchom aplikację. Wszystkie dane usługi będą teraz wyświetlane na stronie szczegółów w odpowiednim formacie.

Projekt Android Studio: MarsRealEstateFinal
Wyrażenia wiążące
- Używaj wyrażeń wiązania w plikach układu XML, aby wykonywać proste operacje programowe, takie jak obliczenia matematyczne lub testy warunkowe, na powiązanych danych.
- Aby odwołać się do klas w pliku układu, użyj tagu
<import>w tagu<data>.
Opcje zapytań do usługi internetowej
- Żądania do usług internetowych mogą zawierać parametry opcjonalne.
- Aby określić parametry zapytania w żądaniu, użyj adnotacji
@Queryw Retrofit.
Kurs Udacity:
Dokumentacja dla deweloperów aplikacji na Androida:
- Omówienie ViewModel
- Omówienie LiveData
- Adaptery wiązań
- Układy i wyrażenia wiążące
- Nawigacja
- Pierwsze kroki z komponentem Navigation
- Przekazywanie danych między miejscami docelowymi (opisuje też bezpieczne argumenty)
TransformationszajęciaViewModelProviderzajęciaViewModelProvider.Factoryzajęcia
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
Do czego służy tag <import> w pliku układu XML?
▢ Dołączanie jednego pliku układu do drugiego.
▢ Umieść kod Kotlin w pliku układu.
▢ Zapewnij dostęp do usług powiązanych z danymi.
▢ umożliwiać odwoływanie się do klas i osób w nich uczestniczących w wyrażeniach wiążących;
Pytanie 2
Jak dodać opcję zapytania do wywołania usługi internetowej REST w Retrofit?
▢ Dołącz zapytanie na końcu adresu URL żądania.
▢ Dodaj do funkcji, która wysyła żądanie, parametr zapytania i oznacz go adnotacją @Query.
▢ Użyj klasy Query, aby utworzyć żądanie.
▢ W konstruktorze Retrofit użyj metody addQuery().
Rozpocznij kolejną lekcję:
Linki do innych ćwiczeń z tego kursu znajdziesz na stronie docelowej ćwiczeń z podstaw języka Kotlin na Androidzie.