Dasar-Dasar Android Kotlin 08.3 Memfilter dan tampilan detail dengan data internet

Codelab ini adalah bagian dari kursus Dasar-Dasar Android Kotlin. Anda akan mendapatkan manfaat maksimal dari kursus ini jika menyelesaikan codelab secara berurutan. Semua codelab kursus tercantum di halaman landing codelab Dasar-Dasar Android Kotlin.

Pengantar

Dalam codelab sebelumnya untuk pelajaran ini, Anda telah mempelajari cara mendapatkan data tentang real estate di Mars dari layanan web, dan cara membuat RecyclerView dengan tata letak petak untuk memuat dan menampilkan gambar dari data tersebut. Dalam codelab ini, Anda akan menyelesaikan aplikasi MarsRealEstate dengan menerapkan kemampuan untuk memfilter properti Mars berdasarkan ketersediaannya untuk disewa atau dibeli. Anda juga membuat tampilan detail sehingga jika pengguna mengetuk foto properti di ringkasan, mereka akan melihat tampilan detail dengan detail tentang properti tersebut.

Yang harus sudah Anda ketahui

  • Cara membuat dan menggunakan fragmen.
  • Cara bernavigasi antar-fragmen dan menggunakan Safe Args (plugin Gradle) untuk meneruskan data antar-fragmen.
  • Cara menggunakan komponen arsitektur termasuk model tampilan, factory model tampilan, transformasi, dan LiveData.
  • Cara mengambil data yang dienkode JSON dari layanan web REST dan mengurai data tersebut ke dalam objek Kotlin dengan library Retrofit dan Moshi.

Yang akan Anda pelajari

  • Cara menggunakan ekspresi binding yang kompleks dalam file tata letak.
  • Cara membuat permintaan Retrofit ke layanan web dengan opsi kueri.

Yang akan Anda lakukan

  • Ubah aplikasi MarsRealEstate untuk menandai properti Mars yang dijual (bukan yang disewakan) dengan ikon tanda dolar.
  • Gunakan menu opsi di halaman ringkasan untuk membuat permintaan layanan web yang memfilter properti Mars menurut jenisnya.
  • Buat fragmen detail untuk properti Mars, hubungkan fragmen tersebut ke petak ringkasan dengan navigasi, dan teruskan data properti ke fragmen tersebut.

Dalam codelab ini (dan codelab terkait), Anda akan menggunakan aplikasi bernama MarsRealEstate, yang menampilkan properti yang dijual di Mars. Aplikasi ini terhubung ke server internet untuk mengambil dan menampilkan data properti, termasuk detail seperti harga dan apakah properti tersedia untuk dijual atau disewakan. Gambar yang merepresentasikan setiap properti adalah foto kehidupan nyata dari Mars yang diambil dari penjelajah Mars NASA. Dalam codelab sebelumnya, Anda membuat RecyclerView dengan tata letak petak untuk semua foto properti:

Dalam versi aplikasi ini, Anda akan menangani jenis properti (sewa versus beli) dan menambahkan ikon ke tata letak petak untuk menandai properti yang dijual:

Anda mengubah menu opsi aplikasi untuk memfilter petak guna menampilkan hanya properti yang disewakan atau dijual:

Terakhir, Anda akan membuat tampilan detail untuk setiap properti, dan menghubungkan ikon di petak ringkasan ke fragmen detail tersebut dengan navigasi:

Hingga saat ini, satu-satunya bagian data properti Mars yang telah Anda gunakan adalah URL untuk gambar properti. Namun, data properti—yang Anda tentukan di class MarsProperty—juga mencakup ID, harga, dan jenis (sewa atau dijual). Untuk menyegarkan ingatan Anda, berikut cuplikan data JSON yang Anda dapatkan dari layanan web:

{
   "price":8000000,
   "id":"424908",
   "type":"rent",
   "img_src": "http://mars.jpl.nasa.gov/msl-raw-images/msss/01000/mcam/1000ML0044631290305226E03_DXXX.jpg"
},

Dalam tugas ini, Anda akan mulai menggunakan jenis properti Mars untuk menambahkan gambar tanda dolar ke properti di halaman ringkasan yang dijual.

Langkah 1: Perbarui MarsProperty untuk menyertakan jenis

Class MarsProperty menentukan struktur data untuk setiap properti yang disediakan oleh layanan web. Dalam codelab sebelumnya, Anda menggunakan library Moshi untuk mengurai respons JSON mentah dari layanan web Mars menjadi objek data MarsProperty individual.

Pada langkah ini, Anda akan menambahkan beberapa logika ke class MarsProperty untuk menunjukkan apakah properti disewakan atau tidak (yaitu, apakah jenisnya adalah string "rent" atau "buy"). Anda akan menggunakan logika ini di lebih dari satu tempat, jadi lebih baik jika logika ini ada di sini dalam class data daripada direplikasi.

  1. Buka aplikasi MarsRealEstate dari codelab terakhir. (Anda dapat mendownload MarsRealEstateGrid jika tidak memiliki aplikasi.)
  2. Buka network/MarsProperty.kt. Tambahkan isi ke definisi class MarsProperty, dan tambahkan getter kustom untuk isRental yang menampilkan true jika objek berjenis "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"
}

Langkah 2: Perbarui tata letak item petak

Sekarang Anda memperbarui tata letak item untuk petak gambar guna menampilkan drawable tanda dolar hanya pada gambar properti yang dijual:

Dengan ekspresi data binding, Anda dapat melakukan pengujian ini sepenuhnya di tata letak XML untuk item petak.

  1. Buka res/layout/grid_view_item.xml. Ini adalah file tata letak untuk setiap sel dalam tata letak petak untuk RecyclerView. Saat ini, file hanya berisi elemen <ImageView> untuk gambar properti.
  2. Di dalam elemen <data>, tambahkan elemen <import> untuk class View. Anda menggunakan impor saat ingin menggunakan komponen class di dalam ekspresi data binding dalam file tata letak. Dalam hal ini, Anda akan menggunakan konstanta View.GONE dan View.VISIBLE, sehingga Anda memerlukan akses ke class View.
<import type="android.view.View"/>
  1. Kelilingi seluruh tampilan gambar dengan FrameLayout, agar drawable tanda dolar dapat ditumpuk di atas gambar properti.
<FrameLayout
   android:layout_width="match_parent"
   android:layout_height="170dp">
             <ImageView 
                    android:id="@+id/mars_image"
            ...
</FrameLayout>
  1. Untuk ImageView, ubah atribut android:layout_height menjadi match_parent, untuk mengisi induk baru FrameLayout.
android:layout_height="match_parent"
  1. Tambahkan elemen <ImageView> kedua tepat di bawah elemen pertama, di dalam FrameLayout. Gunakan definisi yang ditunjukkan di bawah. Gambar ini muncul di pojok kanan bawah item petak, di atas gambar Mars, dan menggunakan drawable yang ditentukan dalam res/drawable/ic_for_sale_outline.xml untuk ikon tanda dolar.
<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"/>
  1. Tambahkan atribut android:visibility ke tampilan gambar mars_property_type. Gunakan ekspresi pengikatan untuk menguji jenis properti, dan tetapkan visibilitas ke View.GONE (untuk rental) atau View.VISIBLE (untuk pembelian).
 android:visibility="@{property.rental ? View.GONE : View.VISIBLE}"

Hingga saat ini, Anda hanya melihat ekspresi binding dalam tata letak yang menggunakan variabel individual yang ditentukan dalam elemen <data>. Ekspresi binding sangat canggih dan memungkinkan Anda melakukan operasi seperti pengujian dan penghitungan matematika sepenuhnya dalam tata letak XML. Dalam hal ini, Anda menggunakan operator ternary (?:) untuk melakukan pengujian (apakah objek ini adalah objek sewa?). Anda memberikan satu hasil untuk benar (sembunyikan ikon tanda dolar dengan View.GONE) dan hasil lainnya untuk salah (tampilkan ikon tersebut dengan View.VISIBLE).

File grid_view_item.xml lengkap yang baru ditampilkan di bawah:

<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>
  1. Kompilasi dan jalankan aplikasi, lalu perhatikan bahwa properti yang bukan properti sewa memiliki ikon tanda dolar.

Saat ini, aplikasi Anda menampilkan semua properti Mars dalam petak ringkasan. Jika pengguna berbelanja properti sewa di Mars, ikon untuk menunjukkan properti yang tersedia untuk dijual akan berguna, tetapi masih banyak properti yang harus di-scroll di halaman. Dalam tugas ini, Anda akan menambahkan menu opsi ke fragmen ringkasan yang memungkinkan pengguna menampilkan hanya properti sewa, hanya properti dijual, atau menampilkan semua.

Salah satu cara untuk menyelesaikan tugas ini adalah dengan menguji jenis untuk setiap MarsProperty di petak ringkasan dan hanya menampilkan properti yang cocok. Namun, layanan web Mars yang sebenarnya memiliki parameter atau opsi kueri (disebut filter) yang memungkinkan Anda hanya mendapatkan properti jenis rent atau jenis buy. Anda dapat menggunakan kueri filter ini dengan URL layanan web realestate di browser seperti ini:

https://android-kotlin-fun-mars-server.appspot.com/realestate?filter=buy

Dalam tugas ini, Anda akan mengubah class MarsApiService untuk menambahkan opsi kueri ke permintaan layanan web dengan Retrofit. Kemudian, Anda menghubungkan menu opsi untuk mendownload ulang semua data properti Mars menggunakan opsi kueri tersebut. Karena respons yang Anda dapatkan dari layanan web hanya berisi properti yang Anda minati, Anda tidak perlu mengubah logika tampilan untuk petak ringkasan sama sekali.

Langkah 1: Perbarui layanan Mars API

Untuk mengubah permintaan, Anda perlu membuka kembali class MarsApiService yang Anda terapkan di codelab pertama dalam seri ini. Anda mengubah class untuk menyediakan API pemfilteran.

  1. Buka network/MarsApiService.kt. Tepat di bawah impor, buat enum bernama MarsApiFilter untuk menentukan konstanta yang cocok dengan nilai kueri yang diharapkan oleh layanan web.
enum class MarsApiFilter(val value: String) {
   SHOW_RENT("rent"),
   SHOW_BUY("buy"),
   SHOW_ALL("all") }
  1. Ubah metode getProperties() untuk mengambil input string untuk kueri filter, dan beri anotasi pada input tersebut dengan @Query("filter"), seperti yang ditunjukkan di bawah.

    Impor retrofit2.http.Query bila diminta.

    Anotasi @Query memberi tahu metode getProperties() (dan dengan demikian Retrofit) untuk membuat permintaan layanan web dengan opsi filter. Setiap kali getProperties() dipanggil, URL permintaan akan menyertakan bagian ?filter=type, yang mengarahkan layanan web untuk merespons dengan hasil yang cocok dengan kueri tersebut.
fun getProperties(@Query("filter") type: String):  

Langkah 2: Perbarui model tampilan ringkasan

Anda meminta data dari MarsApiService dalam metode getMarsRealEstateProperties() di OverviewViewModel. Sekarang Anda perlu memperbarui permintaan tersebut untuk mengambil argumen filter.

  1. Buka overview/OverviewViewModel.kt. Anda akan melihat error di Android Studio karena perubahan yang Anda buat di langkah sebelumnya. Tambahkan MarsApiFilter (enum kemungkinan nilai filter) sebagai parameter ke panggilan getMarsRealEstateProperties().

    Impor com.example.android.marsrealestate.network.MarsApiFilter bila diminta.
private fun getMarsRealEstateProperties(filter: MarsApiFilter) {
  1. Ubah panggilan ke getProperties() di layanan Retrofit untuk meneruskan kueri filter tersebut sebagai string.
var getPropertiesDeferred = MarsApi.retrofitService.getProperties(filter.value)
  1. Di blok init {}, teruskan MarsApiFilter.SHOW_ALL sebagai argumen ke getMarsRealEstateProperties(), untuk menampilkan semua properti saat aplikasi pertama kali dimuat.
init {
   getMarsRealEstateProperties(MarsApiFilter.SHOW_ALL)
}
  1. Di akhir class, tambahkan metode updateFilter() yang menggunakan argumen MarsApiFilter dan memanggil getMarsRealEstateProperties() dengan argumen tersebut.
fun updateFilter(filter: MarsApiFilter) {
   getMarsRealEstateProperties(filter)
}

Langkah 3: Hubungkan fragmen ke menu opsi

Langkah terakhir adalah menghubungkan menu tambahan ke fragmen untuk memanggil updateFilter() di model tampilan saat pengguna memilih opsi menu.

  1. Buka res/menu/overflow_menu.xml. Aplikasi MarsRealEstate memiliki menu tambahan yang sudah ada dan menyediakan tiga opsi yang tersedia: menampilkan semua properti, hanya menampilkan properti sewa, dan hanya menampilkan properti yang dijual.
<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>
  1. Buka overview/OverviewFragment.kt. Di akhir class, terapkan metode onOptionsItemSelected() untuk menangani pilihan item menu.
override fun onOptionsItemSelected(item: MenuItem): Boolean {
} 
  1. Di onOptionsItemSelected(), panggil metode updateFilter() pada model tampilan dengan filter yang sesuai. Gunakan blok when {} Kotlin untuk beralih antar-opsi. Gunakan MarsApiFilter.SHOW_ALL untuk nilai filter default. Tampilkan true, karena Anda telah menangani item menu. Impor MarsApiFilter (com.example.android.marsrealestate.network.MarsApiFilter) bila diminta. Metode onOptionsItemSelected() lengkap ditampilkan di bawah.
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
}
  1. Kompilasi dan jalankan aplikasi. Aplikasi meluncurkan petak ringkasan pertama dengan semua jenis properti dan properti yang dijual ditandai dengan ikon dolar.
  2. Pilih Sewa dari menu opsi. Properti dimuat ulang dan tidak ada yang muncul dengan ikon dolar. (Hanya properti rental yang ditampilkan.) Anda mungkin harus menunggu beberapa saat hingga tampilan dimuat ulang untuk hanya menampilkan properti yang difilter.
  3. Pilih Beli dari menu opsi. Properti dimuat ulang, dan semuanya muncul dengan ikon dolar. (Hanya properti yang dijual yang ditampilkan.)

Sekarang Anda memiliki petak ikon yang dapat di-scroll untuk properti Mars, tetapi sekarang saatnya mendapatkan detail selengkapnya. Dalam tugas ini, Anda akan menambahkan fragmen detail untuk menampilkan detail properti tertentu. Fragmen detail akan menampilkan gambar yang lebih besar, harga, dan jenis properti—apakah disewakan atau dijual.

Fragmen ini diluncurkan saat pengguna mengetuk gambar di petak ringkasan. Untuk melakukannya, Anda perlu menambahkan pemroses onClick ke item petak RecyclerView, lalu membuka fragmen baru. Anda berpindah bagian dengan memicu perubahan LiveData di ViewModel, seperti yang telah Anda lakukan di seluruh pelajaran ini. Anda juga menggunakan plugin Safe Args komponen Navigation untuk meneruskan informasi MarsProperty yang dipilih dari fragmen ringkasan ke fragmen detail.

Langkah 1: Buat model tampilan detail dan perbarui tata letak detail

Mirip dengan proses yang Anda gunakan untuk model tampilan dan fragmen ringkasan, Anda sekarang perlu menerapkan model tampilan dan file tata letak untuk fragmen detail.

  1. Buka detail/DetailViewModel.kt. Sama seperti file Kotlin terkait jaringan yang ada di folder network dan file ringkasan di overview, folder detail berisi file yang terkait dengan tampilan detail. Perhatikan bahwa class DetailViewModel (saat ini kosong) menggunakan marsProperty sebagai parameter dalam konstruktor.
class DetailViewModel( marsProperty: MarsProperty,
                     app: Application) : AndroidViewModel(app) {
}
  1. Di dalam definisi class, tambahkan LiveData untuk properti Mars yang dipilih, untuk menampilkan informasi tersebut ke tampilan detail. Ikuti pola biasa dalam membuat MutableLiveData untuk menyimpan MarsProperty itu sendiri, lalu ekspos properti LiveData publik yang tidak dapat diubah.

    Impor androidx.lifecycle.LiveData dan impor androidx.lifecycle.MutableLiveData bila diminta.
private val _selectedProperty = MutableLiveData<MarsProperty>()
val selectedProperty: LiveData<MarsProperty>
   get() = _selectedProperty
  1. Buat blok init {} dan tetapkan nilai properti Mars yang dipilih dengan objek MarsProperty dari konstruktor.
    init {
        _selectedProperty.value = marsProperty
    }
  1. Buka res/layout/fragment_detail.xml dan lihat dalam tampilan desain.

    Ini adalah file tata letak untuk fragmen detail. CSS ini berisi ImageView untuk foto besar, TextView untuk jenis properti (sewa atau jual), dan TextView untuk harga. Perhatikan bahwa tata letak batasan dibungkus dengan ScrollView sehingga akan otomatis men-scroll jika tampilan terlalu besar untuk layar, misalnya saat pengguna melihatnya dalam mode lanskap.
  2. Buka tab Teks untuk tata letak. Di bagian atas tata letak, tepat sebelum elemen <ScrollView>, tambahkan elemen <data> untuk mengaitkan model tampilan detail dengan tata letak.
<data>
   <variable
       name="viewModel"
       type="com.example.android.marsrealestate.detail.DetailViewModel" />
</data>
  1. Tambahkan atribut app:imageUrl ke elemen ImageView. Setel ke imgSrcUrl dari properti yang dipilih model tampilan.

    Adaptor binding yang memuat gambar menggunakan Glide juga akan otomatis digunakan di sini, karena adaptor tersebut memantau semua atribut app:imageUrl.
 app:imageUrl="@{viewModel.selectedProperty.imgSrcUrl}"

Langkah 2: Tentukan navigasi dalam model tampilan ringkasan

Saat pengguna mengetuk foto dalam model ringkasan, hal ini akan memicu navigasi ke fragmen yang menampilkan detail tentang item yang diklik.

  1. Buka overview/OverviewViewModel.kt. Tambahkan properti _navigateToSelectedProperty MutableLiveData dan ekspos dengan LiveData yang tidak dapat diubah.

    Saat LiveData ini berubah menjadi non-null, navigasi akan dipicu. (Sebentar lagi Anda akan menambahkan kode untuk mengamati variabel ini dan memicu navigasi.)
private val _navigateToSelectedProperty = MutableLiveData<MarsProperty>()
val navigateToSelectedProperty: LiveData<MarsProperty>
   get() = _navigateToSelectedProperty
  1. Di akhir class, tambahkan metode displayPropertyDetails() yang menyetel _navigateToSelectedProperty ke properti Mars yang dipilih.
fun displayPropertyDetails(marsProperty: MarsProperty) {
   _navigateToSelectedProperty.value = marsProperty
}
  1. Tambahkan metode displayPropertyDetailsComplete() yang membatalkan nilai _navigateToSelectedProperty. Anda memerlukan ini untuk menandai status navigasi sebagai selesai, dan untuk menghindari navigasi dipicu lagi saat pengguna kembali dari tampilan detail.
fun displayPropertyDetailsComplete() {
   _navigateToSelectedProperty.value = null
}

Langkah 3: Siapkan pemroses klik di adaptor dan fragmen petak

  1. Buka overview/PhotoGridAdapter.kt. Di akhir class, buat class OnClickListener kustom yang menggunakan lambda dengan parameter marsProperty. Di dalam class, tentukan fungsi onClick() yang ditetapkan ke parameter lambda.
class OnClickListener(val clickListener: (marsProperty:MarsProperty) -> Unit) {
     fun onClick(marsProperty:MarsProperty) = clickListener(marsProperty)
}
  1. Scroll ke atas ke definisi class untuk PhotoGridAdapter, lalu tambahkan properti OnClickListener pribadi ke konstruktor.
class PhotoGridAdapter( private val onClickListener: OnClickListener ) :
       ListAdapter<MarsProperty,              
           PhotoGridAdapter.MarsPropertyViewHolder>(DiffCallback) {
  1. Buat foto dapat diklik dengan menambahkan onClickListener ke item petak dalam metode onBindviewHolder(). Tentukan pemroses klik di antara panggilan ke getItem() and bind().
override fun onBindViewHolder(holder: MarsPropertyViewHolder, position: Int) {
   val marsProperty = getItem(position)
   holder.itemView.setOnClickListener {
       onClickListener.onClick(marsProperty)
   }
   holder.bind(marsProperty)
}
  1. Buka overview/OverviewFragment.kt. Dalam metode onCreateView(), ganti baris yang menginisialisasi properti binding.photosGrid.adapter dengan baris yang ditampilkan di bawah.

    Kode ini menambahkan objek PhotoGridAdapter.onClickListener ke konstruktor PhotoGridAdapter, dan memanggil viewModel.displayPropertyDetails() dengan objek MarsProperty yang diteruskan. Hal ini memicu LiveData di model tampilan untuk navigasi.
binding.photosGrid.adapter = PhotoGridAdapter(PhotoGridAdapter.OnClickListener {
   viewModel.displayPropertyDetails(it)
})

Langkah 4: Ubah grafik navigasi dan jadikan MarsProperty dapat di-parcel

Saat pengguna mengetuk foto di petak ringkasan, aplikasi harus membuka fragmen detail dan meneruskan detail properti Mars yang dipilih sehingga tampilan detail dapat menampilkan informasi tersebut.

Saat ini Anda memiliki pemroses klik dari PhotoGridAdapter untuk menangani ketukan, dan cara untuk memicu navigasi dari model tampilan. Namun, Anda belum memiliki objek MarsProperty yang diteruskan ke fragmen detail. Untuk itu, Anda menggunakan Safe Args dari komponen navigasi.

  1. Buka res/navigation/nav_graph.xml. Klik tab Text untuk melihat kode XML untuk grafik navigasi.
  2. Di dalam elemen <fragment> untuk fragmen detail, tambahkan elemen <argument> yang ditunjukkan di bawah. Argumen ini, yang disebut selectedProperty, memiliki jenis MarsProperty.
<argument
   android:name="selectedProperty"
   app:argType="com.example.android.marsrealestate.network.MarsProperty"
   />
  1. Kompilasi aplikasi. Navigasi menampilkan error karena MarsProperty tidak dapat parcelable. Antarmuka Parcelable memungkinkan objek diserialisasi, sehingga data objek dapat diteruskan antar-fragmen atau aktivitas. Dalam hal ini, agar data di dalam objek MarsProperty diteruskan ke fragmen detail melalui Safe Args, MarsProperty harus menerapkan antarmuka Parcelable. Kabar baiknya adalah Kotlin menyediakan pintasan mudah untuk menerapkan antarmuka tersebut.
  2. Buka network/MarsProperty.kt. Tambahkan anotasi @Parcelize ke definisi class.

    Impor kotlinx.android.parcel.Parcelize jika diminta.

    Anotasi @Parcelize menggunakan ekstensi Android Kotlin untuk otomatis menerapkan metode di antarmuka Parcelable untuk class ini. Anda tidak perlu melakukan apa pun.
@Parcelize
data class MarsProperty (
  1. Ubah definisi class MarsProperty untuk memperluas Parcelable.

    Impor android.os.Parcelable jika diminta.

    Definisi class MarsProperty sekarang terlihat seperti ini:
@Parcelize
data class MarsProperty (
       val id: String,
       @Json(name = "img_src") val imgSrcUrl: String,
       val type: String,
       val price: Double) : Parcelable {

Langkah 5: Hubungkan fragmen

Anda masih belum melakukan navigasi—navigasi sebenarnya terjadi di fragmen. Pada langkah ini, Anda akan menambahkan bagian terakhir untuk mengimplementasikan navigasi antara fragmen ringkasan dan detail.

  1. Buka overview/OverviewFragment.kt. Di onCreateView(), di bawah baris yang menginisialisasi adaptor petak foto, tambahkan baris yang ditunjukkan di bawah untuk mengamati navigatedToSelectedProperty dari model tampilan ringkasan.

    Impor androidx.lifecycle.Observer dan impor androidx.navigation.fragment.findNavController bila diminta.

    Observer menguji apakah MarsPropertyit dalam lambda—tidak null, dan jika ya, observer akan mendapatkan pengontrol navigasi dari fragmen dengan findNavController(). Panggil displayPropertyDetailsComplete() untuk memberi tahu model tampilan agar mereset LiveData ke status null, sehingga Anda tidak akan secara tidak sengaja memicu navigasi lagi saat aplikasi kembali ke OverviewFragment.
viewModel.navigateToSelectedProperty.observe(this, Observer {
   if ( null != it ) {   
      this.findNavController().navigate(
              OverviewFragmentDirections.actionShowDetail(it))             
      viewModel.displayPropertyDetailsComplete()
   }
})
  1. Buka detail/DetailFragment.kt. Tambahkan baris ini tepat di bawah panggilan ke setLifecycleOwner() dalam metode onCreateView(). Baris ini mendapatkan objek MarsProperty yang dipilih dari Safe Args.

    Perhatikan penggunaan operator pernyataan not-null Kotlin (!!). Jika selectedProperty tidak ada, sesuatu yang buruk telah terjadi dan Anda benar-benar ingin kode menampilkan pointer null. (Dalam kode produksi, Anda harus menangani error tersebut dengan cara tertentu.)
 val marsProperty = DetailFragmentArgs.fromBundle(arguments!!).selectedProperty
  1. Tambahkan baris ini berikutnya, untuk mendapatkan DetailViewModelFactory baru. Anda akan menggunakan DetailViewModelFactory untuk mendapatkan instance DetailViewModel. Aplikasi awal menyertakan implementasi DetailViewModelFactory, jadi yang perlu Anda lakukan di sini hanyalah menginisialisasinya.
val viewModelFactory = DetailViewModelFactory(marsProperty, application)
  1. Terakhir, tambahkan baris ini untuk mendapatkan DetailViewModel dari pabrik dan menghubungkan semua bagian.
      binding.viewModel = ViewModelProviders.of(
                this, viewModelFactory).get(DetailViewModel::class.java)
  1. Kompilasi dan jalankan aplikasi, lalu ketuk foto properti Mars mana pun. Fragmen detail akan muncul untuk detail properti tersebut. Ketuk tombol Kembali untuk kembali ke halaman ringkasan, dan perhatikan bahwa layar detail masih agak kosong. Anda akan menyelesaikan penambahan data properti ke halaman detail tersebut di tugas berikutnya.

Saat ini, halaman detail hanya menampilkan foto Mars yang sama yang biasa Anda lihat di halaman ringkasan. Class MarsProperty juga memiliki jenis properti (sewa atau beli) dan harga properti. Layar detail harus menyertakan kedua nilai ini, dan akan sangat membantu jika properti sewa menunjukkan bahwa harga adalah nilai per bulan. Anda menggunakan transformasi LiveData dalam model tampilan untuk menerapkan kedua hal tersebut.

  1. Buka res/values/strings.xml. Kode awal menyertakan resource string, yang ditampilkan di bawah, untuk membantu Anda membuat string untuk tampilan detail. Untuk harga, Anda akan menggunakan resource display_price_monthly_rental atau resource display_price, bergantung pada jenis properti.
<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>
  1. Buka detail/DetailViewModel.kt. Di bagian bawah class, tambahkan kode yang ditampilkan di bawah.

    Impor androidx.lifecycle.Transformations jika diminta.

    Transformasi ini menguji apakah properti yang dipilih adalah properti sewa, menggunakan pengujian yang sama dari tugas pertama. Jika properti adalah rental, transformasi memilih string yang sesuai dari resource dengan switch when {} Kotlin. Kedua string ini memerlukan angka di bagian akhir, jadi Anda menggabungkan property.price setelahnya.
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)
}
  1. Impor class R yang dihasilkan untuk mendapatkan akses ke resource string dalam project.
import com.example.android.marsrealestate.R
  1. Setelah transformasi displayPropertyPrice, tambahkan kode yang ditunjukkan di bawah. Transformasi ini menggabungkan beberapa resource string, berdasarkan apakah jenis properti adalah rental.
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
                   }))
}
  1. Buka res/layout/fragment_detail.xml. Hanya ada satu hal lagi yang harus dilakukan, yaitu mengikat string baru (yang Anda buat dengan transformasi LiveData) ke tampilan detail. Untuk melakukannya, Anda menetapkan nilai kolom teks untuk teks jenis properti ke viewModel.displayPropertyType, dan kolom teks untuk teks nilai harga ke 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" />
  1. Kompilasi dan jalankan aplikasi. Sekarang semua data properti muncul di halaman detail, dengan format yang bagus.

Project Android Studio: MarsRealEstateFinal

Ekspresi binding

  • Gunakan ekspresi binding dalam file tata letak XML untuk melakukan operasi terprogram sederhana, seperti matematika atau pengujian bersyarat, pada data terikat.
  • Untuk mereferensikan class di dalam file tata letak, gunakan tag <import> di dalam tag <data>.

Opsi kueri layanan web

  • Permintaan ke layanan web dapat mencakup parameter opsional.
  • Untuk menentukan parameter kueri dalam permintaan, gunakan anotasi @Query di Retrofit.

Kursus Udacity:

Dokumentasi developer Android:

Lainnya:

Bagian ini mencantumkan kemungkinan tugas pekerjaan rumah untuk siswa yang mengerjakan codelab ini sebagai bagian dari kursus yang dipimpin oleh instruktur. Instruktur menentukan hal berikut:

  • Memberikan pekerjaan rumah jika diperlukan.
  • Memberi tahu siswa cara mengirimkan tugas pekerjaan rumah.
  • Memberi nilai tugas pekerjaan rumah.

Instruktur bisa menggunakan saran ini sesuai kebutuhan, dan bebas menugaskan pekerjaan rumah lain yang dirasa cocok.

Jika Anda menyelesaikan codelab ini sendiri, gunakan tugas pekerjaan rumah ini untuk menguji pengetahuan Anda.

Jawab pertanyaan-pertanyaan berikut

Pertanyaan 1

Apa fungsi tag <import> dalam file tata letak XML?

▢ Sertakan satu file tata letak di file lainnya.

▢ Sematkan kode Kotlin dalam file tata letak.

▢ Sediakan akses ke properti yang terikat oleh data.

▢ Memungkinkan Anda mereferensikan class dan anggota class dalam ekspresi binding.

Pertanyaan 2

Bagaimana cara Anda menambahkan opsi kueri ke panggilan layanan web REST di Retrofit?

▢ Tambahkan kueri ke bagian akhir URL permintaan.

▢ Tambahkan parameter untuk kueri ke fungsi yang membuat permintaan, dan berikan anotasi parameter tersebut dengan @Query.

▢ Gunakan class Query untuk membuat permintaan.

▢ Gunakan metode addQuery() di builder Retrofit.

Mulai pelajaran berikutnya: 9.1: Repositori

Untuk link ke codelab lain dalam kursus ini, lihat halaman landing codelab Dasar-Dasar Android Kotlin.