Te ćwiczenia są częścią kursu Android Kotlin Fundamentals. Skorzystaj z tego kursu, jeśli będziesz wykonywać kolejno kilka ćwiczeń z programowania. Wszystkie ćwiczenia z kursu są wymienione na stronie docelowej ćwiczeń z programowania na temat Kotlin.
Wprowadzenie
Na tych lekcjach z tej lekcji dowiesz się, jak pobierać dane o nieruchomościach na Marsie z usługi internetowej oraz jak utworzyć RecyclerView
z układem siatki do wczytywania i wyświetlania obrazów z tych danych. Dzięki temu ćwiczeniu możesz ukończyć aplikację MarsRealEstate, implementując możliwość filtrowania właściwości Marsa według ich dostępności lub wynajmu. Możesz też utworzyć widok szczegółowy, więc jeśli użytkownik kliknie zdjęcie usługi w widoku ogólnym, zobaczy widok szczegółowy z informacjami o danej usłudze.
Co warto wiedzieć
- Jak tworzyć fragmenty i ich używać.
- Poruszanie się po fragmentach i używanie Safe Args (wtyczki Gradle) do przekazywania danych między fragmentami.
- Jak używać komponentów architektury, takich jak modele widoku danych, fabryki modeli, przekształcenia i
LiveData
. - Jak pobrać dane zakodowane w formacie JSON z usługi internetowej REST i przeanalizować je w obiektach Kotlin za pomocą bibliotek Retrofit i Moshi.
Czego się nauczysz:
- Jak używać złożonych wyrażeń wiązań w plikach układu.
- Jak wysyłać żądania Retrofit do usługi internetowej z opcjami zapytań.
Co chcesz zrobić
- Zmodyfikuj aplikację MarsRealEstate, aby oznaczyć nieruchomość na Marsie, która jest na sprzedaż (a nie, czy na wynajem), z ikoną dolara.
- Użyj menu opcji na stronie przeglądu, aby utworzyć żądanie usługi internetowej, które będzie filtrować usługi Mars według typu.
- Utwórz fragment szczegółów dla właściwości Marsa, połącz go z siatką Przegląd z nawigacją i przekaż do niego dane usługi.
To ćwiczenie programowania (i powiązane z nimi ćwiczenia) obejmuje aplikację MarsRealEstate, która wyświetla usługi na Marsie. Ta aplikacja łączy się z serwerem internetowym, aby pobierać i wyświetlać dane o nieruchomościach, m.in. o ich cenie oraz o tym, czy nieruchomości jest na sprzedaż lub do wynajęcia. Zdjęcia przedstawiające każdą nieruchomość to rzeczywiste zdjęcia Marsa zrobione przez marsjańskie łaziki. W poprzednich ćwiczeniach z programowania utworzono RecyclerView
z układem siatki dla wszystkich zdjęć usługi.
W tej wersji aplikacji korzystasz z typu usługi (wynajem lub kupowanie) i dodajesz ikonę do układu siatki, aby oznaczyć usługi na sprzedaż:
Menu aplikacji możesz zmodyfikować, aby wyświetlić tylko te usługi, które są przeznaczone do wynajęcia lub na sprzedaż:
Na koniec tworzysz widok szczegółowy dla pojedynczej usługi i łączysz ikony w siatce przeglądu z tym fragmentem z nawigacją:
Do tej pory jedynym elementem danych Marsa jest adres URL obrazu właściwości. Jednak dane usługi określone w klasie MarsProperty
zawierają też identyfikator, cenę oraz typ (wynajem lub sprzedaż). Aby odświeżyć pamięć, oto fragment kodu JSON, który otrzymasz 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 właściwością Marsa, aby dodać obraz właściwości z symbolem dolara do właściwości na stronie przeglądu, które są na sprzedaż.
Krok 1. Zaktualizuj typ właściwości MarsProperty, aby uwzględnić typ
Klasa MarsProperty
określa strukturę danych dla każdej usługi dostarczanej przez usługę internetową. W poprzednim ćwiczeniu z programowania użyto biblioteki Moshi do przeanalizowania nieprzetworzonej odpowiedzi JSON z usługi internetowej Marsa na potrzeby poszczególnych obiektów danych MarsProperty
.
W tym kroku dodasz logikę do klasy MarsProperty
, aby wskazać, czy usługa jest do wynajęcia (czyli czy jest to ciąg znaków "rent"
, czy "buy"
). Logika będzie używana w więcej niż jednym miejscu, dlatego lepiej jest tu umieścić ją w klasie danych niż do jej odtworzenia.
- Otwórz aplikację MarsRealEstate z ostatniego ćwiczenia z programowania. (Jeśli nie masz aplikacji, możesz pobrać aplikację MarsRealEstateGrid).
- Otwórz aplikację
network/MarsProperty.kt
. Dodaj treść do definicji klasyMarsProperty
i dodaj niestandardowy obiekt getterisRental
, 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 musisz zmienić układ elementu na siatce obrazów, aby wyświetlać znak dolara rysowany tylko w przypadku tych zdjęć nieruchomości, które są na sprzedaż:
Wyrażenia wiązania danych możesz przeprowadzić w całości w układzie XML dla elementów siatki.
- Otwórz aplikację
res/layout/grid_view_item.xml
. To jest plik układu każdej komórki w układzie siatki dlaRecyclerView
. Obecnie plik zawiera tylko element<ImageView>
obrazu usługi. - W elemencie
<data>
dodaj element<import>
klasyView
. Importów używasz, gdy chcesz użyć komponentów klasy w wyrażeniu wyrażeń danych w pliku układu. W tym przypadku będziesz używać stałychView.GONE
iView.VISIBLE
, dlatego potrzebujesz dostępu do klasyView
.
<import type="android.view.View"/>
- Obrysuj cały widok obrazu za pomocą operatora
FrameLayout
, by umożliwić nakładanie znaku dolara na obraz usługi.
<FrameLayout
android:layout_width="match_parent"
android:layout_height="170dp">
<ImageView
android:id="@+id/mars_image"
...
</FrameLayout>
- W polu
ImageView
zmień atrybutandroid:layout_height
namatch_parent
, aby wypełnić nowy element nadrzędnyFrameLayout
.
android:layout_height="match_parent"
- Dodaj drugi element
<ImageView>
tuż pod pierwszym, wewnątrz elementuFrameLayout
. Użyj definicji podanej poniżej. Obraz wyświetla się w prawym dolnym rogu elementu siatki (na górze obrazu Marsa) i jest kreślony na podstawie ikonyres/drawable/ic_for_sale_outline.xml
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:visibility
do widoku zdjęciamars_property_type
. Użyj wyrażenia wiążącego, by przetestować ten typ usługi, i przypisz widoczność doView.GONE
(w przypadku wypożyczenia) lubView.VISIBLE
(w przypadku zakupu).
android:visibility="@{property.rental ? View.GONE : View.VISIBLE}"
Do tej pory w układach, które korzystają z pojedynczych zmiennych zdefiniowanych w elemencie <data>
, były używane tylko wyrażenia wiązania. Wyrażenia wiążące są bardzo zaawansowane i umożliwiają wykonywanie takich operacji jak testy i obliczenia matematyczne w całości w układzie XML. W tym przypadku do przeprowadzenia testu użyjesz operatora potrójnego (?:
) (czy ten obiekt jest wypożyczony?). Podaj jeden wynik dla prawdy (ukryj ikonę dolara z View.GONE
) i drugi fałsz (wyświetl tę ikonę z View.VISIBLE
).
Nowy, kompletny plik grid_view_item.xml
jest wyświetlany 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ę. Pamiętaj, że usługi, które nie są wypożyczalniami, mają ikonę dolara.
Obecnie Twoja aplikacja wyświetla wszystkie właściwości Marsa w siatce przeglądu. Jeśli użytkownik chce wynająć mieszkanie na Marsie, ikony informujące, które z nich są na sprzedaż, mogą być przydatne, ale na stronie nadal może się pojawiać wiele usług. W tym zadaniu dodasz menu opcji do fragmentu przeglądu, który umożliwia użytkownikowi wyświetlenie tylko wynajmu, tylko nieruchomości na sprzedaż lub wyświetlanie wszystkich ofert.
Możesz na przykład przetestować typ każdej MarsProperty
w siatce przeglądu i wyświetlić tylko odpowiadające jej właściwości. Rzeczywista usługa internetowa Mars ma jednak parametr zapytania lub opcję (o nazwie filter
), która umożliwia uzyskanie tylko właściwości typu rent
lub buy
. Możesz użyć tego filtra z adresem URL usługi internetowej realestate
w przeglądarce:
https://android-kotlin-fun-mars-server.appspot.com/realestate?filter=buy
W tym zadaniu zmodyfikujesz klasę MarsApiService
, aby dodać opcję zapytania do usługi internetowej za pomocą Retrofit. Następnie podłącz menu opcji, by ponownie pobrać wszystkie dane właściwości Marsa przy użyciu tej opcji zapytania. Odpowiedź otrzymana z usługi internetowej zawiera tylko te usługi, które Cię interesują, więc w ogóle nie musisz zmieniać logiki wyświetlania widoku siatki.
Krok 1. Zaktualizuj usługę Mars API
Aby zmienić tę prośbę, musisz jeszcze raz obejrzeć klasę MarsApiService
zaimplementowaną w pierwszym ćwiczeniu z programowania w tej serii. Zmodyfikuj klasę, aby udostępnić interfejs API filtrowania.
- Otwórz aplikację
network/MarsApiService.kt
. Tuż pod importem utwórzenum
o nazwieMarsApiFilter
, by zdefiniować stałe wartości odpowiadające zapytaniu oczekiwanej przez usługę internetową.
enum class MarsApiFilter(val value: String) {
SHOW_RENT("rent"),
SHOW_BUY("buy"),
SHOW_ALL("all") }
- Zmodyfikuj metodę
getProperties()
tak, by pobierała dane wejściowe ciągu zapytania dla filtra. Dodaj do nich też adnotacje@Query("filter")
, jak pokazano poniżej.
Gdy pojawi się prośba, zaimportujretrofit2.http.Query
.
Adnotacja@Query
informuje metodęgetProperties()
(a tym samym Retrofit) o żądanie usługi internetowej za pomocą opcji filtrowania. Przy każdym wywołaniugetProperties()
adres URL żądania zawiera część?filter=type
, która kieruje usługę internetową, aby odpowiedziała na wyniki pasujące do tego zapytania.
fun getProperties(@Query("filter") type: String):
Krok 2. Zaktualizuj model widoku przeglądu
Żądasz danych z metody MarsApiService
w metodzie getMarsRealEstateProperties()
w OverviewViewModel
. Teraz musisz zaktualizować to żądanie, by przyjąć argument filtra.
- Otwórz aplikację
overview/OverviewViewModel.kt
. Z powodu zmian wprowadzonych w poprzednim kroku zobaczysz w Android Studio błędy. DodajMarsApiFilter
(wyliczenie możliwych wartości filtra) jako parametr do wywołaniagetMarsRealEstateProperties()
.
Zaimportujcom.example.android.marsrealestate.network.MarsApiFilter
na żądanie.
private fun getMarsRealEstateProperties(filter: MarsApiFilter) {
- Zmodyfikuj wywołanie funkcji
getProperties()
w usłudze Retrofit, aby przekazywać to zapytanie filtra w postaci ciągu znaków.
var getPropertiesDeferred = MarsApi.retrofitService.getProperties(filter.value)
- W bloku
init {}
przekażMarsApiFilter.SHOW_ALL
jako argument dogetMarsRealEstateProperties()
, aby wyświetlić wszystkie właściwości przy pierwszym załadowaniu aplikacji.
init {
getMarsRealEstateProperties(MarsApiFilter.SHOW_ALL)
}
- Na końcu klasy dodaj metodę
updateFilter()
, która przyjmuje argumentMarsApiFilter
i wywołuje za jego pomocą parametrgetMarsRealEstateProperties()
.
fun updateFilter(filter: MarsApiFilter) {
getMarsRealEstateProperties(filter)
}
Krok 3. Połącz fragment z menu opcji
Ostatnim krokiem jest powiązanie rozszerzonego menu z fragmentem w celu wywołania metody updateFilter()
w modelu widoku danych, gdy użytkownik wybierze opcję menu.
- Otwórz aplikację
res/menu/overflow_menu.xml
. W aplikacji MarsRealEstate jest dostępne rozszerzone menu, które zawiera trzy opcje: wyświetlanie wszystkich nieruchomości, tylko wypożyczanie 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 aplikację
overview/OverviewFragment.kt
. Na końcu klasy zaimplementuj metodęonOptionsItemSelected()
, która obsługuje wybrany element menu.
override fun onOptionsItemSelected(item: MenuItem): Boolean {
}
- W
onOptionsItemSelected()
musisz wywołać metodęupdateFilter()
w modelu widoku danych z odpowiednim filtrem. Aby przełączać się między opcjami, użyj blokuwhen {}
Kotlin. UżyjMarsApiFilter.SHOW_ALL
jako domyślnej wartości filtra. Zwróconotrue
, ponieważ to pozycji menu. ImportujMarsApiFilter
(com.example.android.marsrealestate.network.MarsApiFilter
) na żądanie. Pełna metodaonOptionsItemSelected()
jest widoczna 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ądu ze wszystkimi typami usług i nieruchomościami na sprzedaż oznaczonymi ikoną dolara.
- W menu „Opcje” wybierz Wypożycz. Właściwości ponownie wczytają się i żadna z nich nie wyświetli się z ikoną dolara. Wyświetlane są tylko nieruchomości na wynajem. Aby wyniki zostały odświeżone, może minąć kilka chwil, aż ekran się odświeży.
- W menu „Opcje” kliknij Kup. Właściwości zostaną ponownie załadowane, a wszystkie pojawią się z ikoną dolara. Wyświetlane są tylko nieruchomości na sprzedaż.
Teraz masz do dyspozycji przewijaną siatkę ikon dla usług Marsa, ale czas na szczegóły. W tym zadaniu dodasz fragment szczegółów, aby wyświetlić szczegóły konkretnej właściwości. Fragment szczegółów wyświetli większy obraz, cenę i typ nieruchomości – np. wypożyczalnię lub sprzedaż.
Ten fragment jest uruchamiany, gdy użytkownik kliknie obraz na siatce przeglądu. Aby to zrobić, musisz dodać detektor onClick
do elementów siatki RecyclerView
, a potem przejść do nowego fragmentu. Poruszasz się, wywołując zmianę LiveData
w ViewModel
tak samo jak w czasie tych lekcji. Można też korzystać z wtyczki Safe Args do nawigacji w celu przekazywania wybranych informacji MarsProperty
z fragmentu omówienia do fragmentu szczegółów.
Krok 1. Utwórz model widoku szczegółów i zaktualizuj układ szczegółów
Podobnie jak w przypadku modelu z widokiem przeglądu i fragmentów, musisz teraz zaimplementować pliki z modelem widoku i układem na potrzeby fragmentu szczegółów.
- Otwórz aplikację
detail/DetailViewModel.kt
. Pliki związane z siecią Kotlin znajdują się w folderzenetwork
, a pliki przeglądu w folderzeoverview
– w folderzedetail
znajdują się pliki powiązane z widokiem szczegółowym. Zauważ, że klasaDetailViewModel
(w tej chwili pusta) zawiera parametrmarsProperty
w parametrze konstruktora.
class DetailViewModel( marsProperty: MarsProperty,
app: Application) : AndroidViewModel(app) {
}
- Aby wyświetlić te informacje w widoku szczegółów, dodaj do definicji klasy Mars
LiveData
. Postępuj zgodnie ze zwykłym wzorcem tworzenia elementuMutableLiveData
, by przechowywać właściwośćMarsProperty
, a następnie udostępnić niezmienną publiczną właściwośćLiveData
.
Importuj (androidx.lifecycle.LiveData
) i importuj (androidx.lifecycle.MutableLiveData
) na żądanie.
private val _selectedProperty = MutableLiveData<MarsProperty>()
val selectedProperty: LiveData<MarsProperty>
get() = _selectedProperty
- Utwórz blok
init {}
i ustaw wartość wybranej właściwości Marsa za pomocą obiektuMarsProperty
z konstruktora.
init {
_selectedProperty.value = marsProperty
}
- Otwórz folder
res/layout/fragment_detail.xml
i przyjrzyj się mu w widoku projektu.
Jest to plik układu fragmentu szczegółów. ZawieraImageView
dla dużego zdjęcia,TextView
dla typu nieruchomości (wynajem lub sprzedaż) orazTextView
dla ceny. Zwróć uwagę, że układ ograniczenia jest opakowany za pomocąScrollView
, więc będzie się automatycznie przewijać, gdy widok będzie za duży dla wyświetlacza, na przykład gdy użytkownik wyświetli go w trybie poziomym. - Otwórz kartę Tekst układu. U góry układu tuż przed elementem
<ScrollView>
dodaj element<data>
, by powiązać z nim układ widoku szczegółów.
<data>
<variable
name="viewModel"
type="com.example.android.marsrealestate.detail.DetailViewModel" />
</data>
- Dodaj atrybut
app:imageUrl
do elementuImageView
. Ustaw w nim wartośćimgSrcUrl
z wybranej właściwości widoku.
Używany jest tu też adapter wiążący, który wczytuje obraz za pomocą gestu, ponieważ sprawdza on wszystkie atrybutyapp:imageUrl
.
app:imageUrl="@{viewModel.selectedProperty.imgSrcUrl}"
Krok 2. Zdefiniuj nawigację w modelu widoku przeglądu
Gdy użytkownik kliknie zdjęcie w modelu przeglądu, powinno to spowodować przejście do fragmentu zawierającego szczegółowe informacje o klikniętym elemencie.
- Otwórz aplikację
overview/OverviewViewModel.kt
. Dodaj właściwość_navigateToSelectedProperty
MutableLiveData
i udostępnij ją stałemuLiveData
.
Gdy tenLiveData
zmieni się na inną niż null, nawigacja zostanie aktywowana. Wkrótce dodasz kod, by obserwować tę zmienną i uruchomić nawigację.
private val _navigateToSelectedProperty = MutableLiveData<MarsProperty>()
val navigateToSelectedProperty: LiveData<MarsProperty>
get() = _navigateToSelectedProperty
- Na koniec klasy dodaj metodę
displayPropertyDetails()
, która ustawia _navigateToSelectedProperty
do wybranej właściwości Marsa.
fun displayPropertyDetails(marsProperty: MarsProperty) {
_navigateToSelectedProperty.value = marsProperty
}
- Dodaj metodę
displayPropertyDetailsComplete()
, która ma wartość_navigateToSelectedProperty
. Musisz to zaznaczyć, aby oznaczyć stan nawigacji jako zakończony i nie uruchamiać się ponownie po wyświetleniu użytkownikowi szczegółów.
fun displayPropertyDetailsComplete() {
_navigateToSelectedProperty.value = null
}
Krok 3. Skonfiguruj detektory kliknięć w adapterze siatki i we fragmencie
- Otwórz aplikację
overview/PhotoGridAdapter.kt
. Na końcu klasy utwórz niestandardową klasęOnClickListener
, która przyjmuje lambdę za pomocą parametrumarsProperty
. W klasie zdefiniuj funkcjęonClick()
ustawioną na parametr lambda.
class OnClickListener(val clickListener: (marsProperty:MarsProperty) -> Unit) {
fun onClick(marsProperty:MarsProperty) = clickListener(marsProperty)
}
- Przewiń w górę do definicji klasy obiektu
PhotoGridAdapter
i dodaj do konstruktora prywatną właściwośćOnClickListener
.
class PhotoGridAdapter( private val onClickListener: OnClickListener ) :
ListAdapter<MarsProperty,
PhotoGridAdapter.MarsPropertyViewHolder>(DiffCallback) {
- Aby umożliwić kliknięcie zdjęcia, dodaj
onClickListener
do elementu siatki w metodzieonBindviewHolder()
. Określ detektor kliknięć między wywołaniamigetItem() and bind()
.
override fun onBindViewHolder(holder: MarsPropertyViewHolder, position: Int) {
val marsProperty = getItem(position)
holder.itemView.setOnClickListener {
onClickListener.onClick(marsProperty)
}
holder.bind(marsProperty)
}
- Otwórz aplikację
overview/OverviewFragment.kt
. W metodzieonCreateView()
zastąp wiersz inicjujący właściwośćbinding.photosGrid.adapter
linią podaną poniżej.
Ten kod dodaje obiektPhotoGridAdapter.onClickListener
do konstruktoraPhotoGridAdapter
i wywołuje elementviewModel.displayPropertyDetails()
z przekazanym obiektemMarsProperty
. Powoduje to wyświetlenieLiveData
w modelu widoku danych nawigacji.
binding.photosGrid.adapter = PhotoGridAdapter(PhotoGridAdapter.OnClickListener {
viewModel.displayPropertyDetails(it)
})
Krok 4. Zmodyfikuj wykres nawigacyjny i włącz funkcję MarsProperty (pakiet)
Gdy użytkownik kliknie zdjęcie w siatce przeglądu, aplikacja powinna przejść do fragmentu szczegółów i przejść szczegóły wybranej usługi Marsa w celu wyświetlenia tych informacji w widoku szczegółowym.
Obecnie masz detektor kliknięć z usługi PhotoGridAdapter
, który obsługuje kliknięcie, oraz umożliwia uruchomienie nawigacji z modelu widoku. Nie masz jeszcze obiektu MarsProperty
przekazywanego do fragmentu szczegółów. Aby to zrobić, użyj Safe Args z komponentu nawigacyjnego.
- Otwórz aplikację
res/navigation/nav_graph.xml
. Kliknij kartę Tekst, aby wyświetlić kod XML wykresu nawigacyjnego. - W elemencie
<fragment>
fragmentu fragmentu dodaj element<argument>
pokazany poniżej. Ten argument o nazwieselectedProperty
ma typMarsProperty
.
<argument
android:name="selectedProperty"
app:argType="com.example.android.marsrealestate.network.MarsProperty"
/>
- Skompiluj aplikację. Nawigacja wyświetli błąd, ponieważ
MarsProperty
nie jest parcelable. InterfejsParcelable
umożliwia serializację obiektów, co pozwala na przesyłanie danych między fragmentami lub aktywnościami. W takim przypadku, aby dane w obiekcieMarsProperty
zostały przekazane do fragmentu szczegółów za pomocą Safe Args,MarsProperty
musi zaimplementować interfejsParcelable
. Dobra wiadomość jest taka, że Kotlin udostępnia łatwy skrót do implementacji tego interfejsu. - Otwórz aplikację
network/MarsProperty.kt
. Dodaj adnotację@Parcelize
do definicji klasy.
Importujkotlinx.android.parcel.Parcelize
, jeśli jest taka potrzeba.
Adnotacja@Parcelize
używa rozszerzeń Androida Kotlin do automatycznego implementowania metod w interfejsieParcelable
dla tych zajęć. Nie musisz nic więcej robić.
@Parcelize
data class MarsProperty (
- Zmień definicję klasy
MarsProperty
, aby przedłużyćParcelable
.
Importujandroid.os.Parcelable
, jeśli pojawi się taka prośba.
Definicja klasyMarsProperty
wyglą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 korzystasz z nawigacji – rzeczywista nawigacja ma miejsce we fragmentach. W tym kroku dodasz ostatnie bity służące do implementacji nawigacji między fragmentami przeglądu i szczegółami.
- Otwórz aplikację
overview/OverviewFragment.kt
. WonCreateView()
pod liniami, które inicjują adapter siatki zdjęć, dodaj widoczne poniżej linie, aby obserwowaćnavigatedToSelectedProperty
z modelu widoku przeglądu.
Importuj (androidx.lifecycle.Observer
) i importuj (androidx.navigation.fragment.findNavController
) na żądanie.
Obserwator sprawdza, czyMarsProperty
–it
w lambdzie – nie jest pusty. Jeśli tak, pobiera kontroler nawigacyjny z fragmentu za pomocąfindNavController()
. WywołajdisplayPropertyDetailsComplete()
, by poprosić o zresetowanie modeluLiveData
i ustawienie go do wartości null. Dzięki temu nie musisz przypadkowo uruchamiać ponownie nawigacji, gdy aplikacja wraca doOverviewFragment
.
viewModel.navigateToSelectedProperty.observe(this, Observer {
if ( null != it ) {
this.findNavController().navigate(
OverviewFragmentDirections.actionShowDetail(it))
viewModel.displayPropertyDetailsComplete()
}
})
- Otwórz aplikację
detail/DetailFragment.kt
. Dodaj ten wiersz pod wywołaniemsetLifecycleOwner()
w metodzieonCreateView()
. Ten wiersz pobiera wybrany obiektMarsProperty
z Safe Args.
Zwróć uwagę na użycie niepodlegającego pustego operatora Kotlin (!!
). Jeśli parametrselectedProperty
nie jest dostępny, wystąpiła tragiczna sytuacja. Chcesz umieścić kod pusty. (W kodzie produkcyjnym należy to zrobić w jakiś sposób).
val marsProperty = DetailFragmentArgs.fromBundle(arguments!!).selectedProperty
- Dodaj ten wiersz teraz, aby otrzymać nową kolumnę
DetailViewModelFactory
. Aby uzyskać instancjęDetailViewModel
, użyjDetailViewModelFactory
. Aplikacja startowa zawiera implementację taguDetailViewModelFactory
, więc wystarczy tylko go zainicjować.
val viewModelFactory = DetailViewModelFactory(marsProperty, application)
- Dodaj ten wiersz, aby pobrać
DetailViewModel
z fabryki i połączyć wszystkie części.
binding.viewModel = ViewModelProviders.of(
this, viewModelFactory).get(DetailViewModel::class.java)
- Skompiluj i uruchom aplikację, a następnie kliknij dowolne zdjęcie marsjańskie właściwości. Pojawi się fragment szczegółów tej właściwości. Kliknij przycisk Wstecz, aby wrócić na stronę przeglądu. Pamiętaj, że ekran szczegółów jest nadal mało widoczny. Dane usługi możesz dodać do tej strony szczegółów w następnym zadaniu.
Obecnie na stronie szczegółów widać tylko to samo zdjęcie Marsa, które znasz na stronie przeglądu. Klasa MarsProperty
zawiera też typ właściwości (wynajem lub zakup) oraz cenę usługi. Ekran szczegółów powinien zawierać obie te wartości. Pomocne będzie, jeśli właściwości związane z wypożyczeniem wskazują, że cena dotyczyła płatności za miesiąc. Aby wdrożyć te obie funkcje, użyj przekształceń typu LiveData
w widoku danych.
- Otwórz aplikację
res/values/strings.xml
. Kod początkowy zawiera poniższe ciągi tekstowe, które ułatwiają tworzenie ciągów znaków na potrzeby widoku szczegółów. W przypadku tej ceny będziesz używać zasobudisplay_price_monthly_rental
lub zasobudisplay_price
w 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 aplikację
detail/DetailViewModel.kt
. U dołu zajęć dodaj kod widoczny poniżej.
Jeśli chcesz, zaimportujandroidx.lifecycle.Transformations
.
Ta transformacja sprawdza, czy wybrana usługa jest wynajemem, korzystając z tego samego testu z pierwszego zadania. Jeśli usługa jest wynajmem, przekształcenie wybiera odpowiedni ciąg z zasobów za pomocą przełącznikawhen {}
w Kotlin. Oba te ciągi wymagają liczby na końcu, dlatego musisz połączyćproperty.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)
}
- Zaimportuj wygenerowaną klasę
R
, aby uzyskać dostęp do zasobów ciągu znaków w projekcie.
import com.example.android.marsrealestate.R
- Po przekształceniu
displayPropertyPrice
dodaj poniższy kod. Ta transformacja łączy wiele zasobów ciągów, w zależności od tego, czy typ 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 aplikację
res/layout/fragment_detail.xml
. Trzeba tylko zrobić jeszcze jedną rzecz, czyli powiązać nowe ciągi (utworzone za pomocą przekształceńLiveData
) z widokiem szczegółów. Aby to zrobić, ustaw wartość w polu tekstowym tekstu typu właściwości naviewModel.displayPropertyType
, a w polu tekstowym Wartość ceny –viewModel.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ę. Teraz wszystkie dane usługi są dobrze widoczne na stronie z informacjami.
Projekt na Android Studio: MarsRealEstateFinal
Powiązane wyrażenia
- W plikach układu XML możesz używać wyrażeń, aby wykonywać proste operacje zautomatyzowane, takie jak działania matematyczne czy warunkowe w danych powiązanych.
- Aby odwołać się do zajęć w pliku układu, użyj tagu
<import>
w tagu<data>
.
Opcje zapytań dotyczących usług internetowych
- Żądania do usług internetowych mogą zawierać parametry opcjonalne.
- Aby określić parametry zapytania w żądaniu, użyj adnotacji
@Query
w Retrofit.
Kurs Udacity:
Dokumentacja dla programistów Androida:
- Omówienie modelu Model
- Omówienie danych na żywo
- Adaptery wiążące
- Układy i wyrażenia powiązania
- Nawigacja
- Pierwsze kroki z komponentem Nawigacja
- Przekazywanie danych między lokalizacjami
- Klasa
Transformations
- Klasa
ViewModelProvider
- Klasa
ViewModelProvider.Factory
Inne:
Ta sekcja zawiera listę możliwych zadań domowych dla uczniów, którzy pracują w ramach tego ćwiczenia w ramach kursu prowadzonego przez nauczyciela. To nauczyciel może wykonać te czynności:
- W razie potrzeby przypisz zadanie domowe.
- Poinformuj uczniów, jak przesyłać zadania domowe.
- Oceń projekty domowe.
Nauczyciele mogą wykorzystać te sugestie tak długo, jak chcą lub chcą, i mogą przypisać dowolne zadanie domowe.
Jeśli samodzielnie wykonujesz te ćwiczenia z programowania, możesz sprawdzić swoją wiedzę w tych zadaniach domowych.
Odpowiedz na te pytania
Pytanie 1
Do czego służy tag <import>
w pliku układu XML?
▢ Uwzględnij jeden plik z układem w drugim.
▢ Umieść kod Kotlin w pliku układu.
▢ zapewni dostęp do usług powiązanych z danymi.
▢ Umożliwia odwoływanie się do zajęć i ich członków 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 parametr zapytania do funkcji, która wysyła żądanie, i dodaj adnotacje do parametru @Query
.
▢ Użyj klasy Query
, aby utworzyć żądanie.
▢ Użyj metody addQuery()
w narzędziu do tworzenia Retrofit.
Rozpocznij następną lekcję:
Linki do innych ćwiczeń z programowania w tym kursie znajdziesz na stronie docelowej z ćwiczeniami z podstaw Androida Kotlin.