Android Kotlin Fundamentals 08.3 Фильтрация и подробные представления с интернет-данными

Эта кодовая лаборатория является частью курса Android Kotlin Fundamentals. Вы получите максимальную отдачу от этого курса, если будете последовательно работать с лабораториями кода. Все кодовые лаборатории курса перечислены на целевой странице кодовых лабораторий Android Kotlin Fundamentals .

Введение

В предыдущих лабораторных работах для этого урока вы узнали, как получить данные о недвижимости на Марсе из веб-службы и как создать RecyclerView с макетом сетки для загрузки и отображения изображений из этих данных. В этой лабораторной работе вы завершаете работу над приложением MarsRealEstate, реализуя возможность фильтрации свойств Mars по тому, доступны ли они для аренды или покупки. Вы также создаете подробное представление, чтобы, если пользователь коснется фотографии свойства в обзоре, он увидел подробное представление с подробными сведениями об этом свойстве.

Что вы уже должны знать

  • Как создавать и использовать фрагменты.
  • Как перемещаться между фрагментами и использовать Safe Args (плагин Gradle) для передачи данных между фрагментами.
  • Как использовать компоненты архитектуры, включая модели представлений, фабрики моделей представлений, преобразования и LiveData .
  • Как получить закодированные данные JSON из веб-службы REST и проанализировать эти данные в объектах Kotlin с помощью библиотек Retrofit и Moshi .

Что вы узнаете

  • Как использовать сложные выражения привязки в ваших файлах макетов.
  • Как сделать запросы на доработку к веб-службе с параметрами запроса.

Что ты будешь делать

  • Измените приложение MarsRealEstate, чтобы пометить недвижимость Mars, выставленную на продажу (а не сдаваемую в аренду), значком доллара.
  • Используйте меню параметров на странице обзора, чтобы создать запрос веб-службы, фильтрующий свойства Mars по типу.
  • Создайте фрагмент сведений для свойства Mars, подключите этот фрагмент к сетке обзора с помощью навигации и передайте данные свойства в этот фрагмент.

В этой лаборатории кода (и связанных с ней лабораториях кода) вы работаете с приложением MarsRealEstate, которое показывает недвижимость, выставленную на продажу на Марсе. Это приложение подключается к интернет-серверу для получения и отображения данных о недвижимости, включая такие сведения, как цена и доступность недвижимости для продажи или аренды. Изображения, представляющие каждое свойство, представляют собой реальные фотографии с Марса, сделанные марсоходами НАСА. В предыдущих лабораториях кода вы создали RecyclerView с макетом сетки для всех фотографий собственности:

В этой версии приложения вы работаете с типом недвижимости (аренда или покупка) и добавляете значок в макет сетки, чтобы помечать объекты, выставленные на продажу:

Вы изменяете меню параметров приложения, чтобы отфильтровать сетку, чтобы отображались только те свойства, которые сдаются в аренду или продаются:

И, наконец, вы создаете подробное представление для отдельного свойства и соединяете значки в обзорной сетке с этим фрагментом сведений с помощью навигации:

До сих пор единственной частью данных свойства Mars, которую вы использовали, был URL-адрес изображения свойства. Но данные о собственности, которые вы определили в классе MarsProperty , также включают в себя идентификатор, цену и тип (аренда или продажа). Чтобы освежить вашу память, вот фрагмент данных JSON, которые вы получаете от веб-службы:

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

В этой задаче вы начнете работать с типом свойства Mars, чтобы добавить изображение знака доллара к свойствам на обзорной странице, которые выставлены на продажу.

Шаг 1. Обновите MarsProperty, указав тип

Класс MarsProperty определяет структуру данных для каждого свойства, предоставляемого веб-службой. В предыдущей кодовой лаборатории вы использовали библиотеку Moshi для анализа необработанного ответа JSON от веб-службы Mars на отдельные объекты данных MarsProperty .

На этом шаге вы добавляете некоторую логику в класс MarsProperty , чтобы указать, сдается ли свойство в аренду или нет (то есть, является ли тип строки "rent" или "buy" ). Вы будете использовать эту логику более чем в одном месте, поэтому лучше иметь ее здесь, в классе данных, чем копировать ее.

  1. Откройте приложение MarsRealEstate из последней лаборатории кода. (Вы можете загрузить MarsRealEstateGrid , если у вас нет приложения.)
  2. Откройте network/MarsProperty.kt . Добавьте тело в определение класса MarsProperty и добавьте пользовательский метод получения для isRental , который возвращает true , если объект имеет тип "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"
}

Шаг 2. Обновите макет элемента сетки

Теперь вы обновляете макет элемента для сетки изображений, чтобы отображать знак доллара, который можно рисовать только на тех изображениях недвижимости, которые выставлены на продажу:

С помощью выражений привязки данных вы можете выполнить этот тест полностью в макете XML для элементов сетки.

  1. Откройте res/layout/grid_view_item.xml . Это файл макета для каждой отдельной ячейки в макете сетки для RecyclerView . В настоящее время файл содержит только элемент <ImageView> для изображения свойства.
  2. Внутри элемента <data> добавьте элемент <import> для класса View . Вы используете импорт, когда хотите использовать компоненты класса внутри выражения привязки данных в файле макета. В этом случае вы собираетесь использовать View.GONE и View.VISIBLE , поэтому вам нужен доступ к классу View .
<import type="android.view.View"/>
  1. Окружите весь вид изображения с помощью FrameLayout , чтобы разрешить рисование знака доллара поверх изображения свойства.
<FrameLayout
   android:layout_width="match_parent"
   android:layout_height="170dp">
             <ImageView 
                    android:id="@+id/mars_image"
            ...
</FrameLayout>
  1. Для ImageView измените атрибут android:layout_height на match_parent , чтобы заполнить новый родительский FrameLayout .
android:layout_height="match_parent"
  1. Добавьте второй элемент <ImageView> чуть ниже первого внутри FrameLayout . Используйте определение, показанное ниже. Это изображение появляется в правом нижнем углу элемента сетки поверх изображения Марса и использует объект рисования, определенный в res/drawable/ic_for_sale_outline.xml для значка со знаком доллара.
<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. Добавьте атрибут android:visibility в представление изображения mars_property_type . Используйте выражение привязки для проверки типа свойства и назначьте видимость View.GONE (для аренды) или View.VISIBLE (для покупки).
 android:visibility="@{property.rental ? View.GONE : View.VISIBLE}"

До сих пор вы видели выражения привязки только в макетах, которые используют отдельные переменные, определенные в элементе <data> . Выражения привязки чрезвычайно эффективны и позволяют выполнять такие операции, как тесты и математические вычисления, полностью в пределах макета XML. В этом случае вы используете тернарный оператор ( ?: ) для выполнения теста (является ли этот объект арендным?). Вы предоставляете один результат для true (скройте значок доллара с помощью View.GONE ) и другой для false (покажите этот значок с помощью View.VISIBLE ).

Новый полный файл grid_view_item.xml показан ниже:

<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. Скомпилируйте и запустите приложение. Обратите внимание, что свойства, которые не сдаются в аренду, отмечены значком доллара.

В настоящее время ваше приложение отображает все свойства Марса в обзорной сетке. Если бы пользователь покупал недвижимость на Марсе для сдачи в аренду, было бы полезно иметь значки, указывающие, какие из доступных свойств выставлены на продажу, но на странице все еще есть много свойств, которые нужно прокручивать. В этой задаче вы добавляете меню опций к фрагменту обзора, которое позволяет пользователю отображать только арендуемые объекты, только недвижимость для продажи или показывать все.

Один из способов выполнить эту задачу — проверить тип каждого MarsProperty в обзорной сетке и отобразить только соответствующие свойства. Однако фактический веб-сервис Mars имеет параметр запроса или опцию (называемую filter ), которая позволяет вам получить только недвижимость типа rent или buy . Вы можете использовать этот запрос фильтра с URL-адресом веб-службы realestate в браузере следующим образом:

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

В этой задаче вы измените класс MarsApiService , чтобы добавить параметр запроса к запросу веб-службы с помощью Retrofit. Затем вы подключаете меню параметров, чтобы повторно загрузить все данные о свойствах Марса, используя этот параметр запроса. Поскольку ответ, который вы получаете от веб-службы, содержит только интересующие вас свойства, вам вообще не нужно изменять логику отображения представления для сетки обзора.

Шаг 1. Обновите службу Mars API

Чтобы изменить запрос, вам нужно снова обратиться к классу MarsApiService , который вы реализовали в первой лаборатории кода в этой серии. Вы изменяете класс, чтобы предоставить фильтрующий API.

  1. Откройте network/MarsApiService.kt . Сразу после импорта создайте enum MarsApiFilter , чтобы определить константы, соответствующие значениям запроса, ожидаемым веб-службой.
enum class MarsApiFilter(val value: String) {
   SHOW_RENT("rent"),
   SHOW_BUY("buy"),
   SHOW_ALL("all") }
  1. Измените метод getProperties() , чтобы он принимал строковые входные данные для запроса фильтра, и аннотируйте эти входные данные с помощью @Query("filter") , как показано ниже.

    Импортируйте retrofit2.http.Query при появлении запроса.

    Аннотация @Query указывает методу getProperties() (и, следовательно, Retrofit) сделать запрос веб-службы с параметром фильтра. Каждый раз, когда вызывается getProperties() , URL-адрес запроса включает часть ?filter=type , которая предписывает веб-службе отвечать с результатами, соответствующими этому запросу.
fun getProperties(@Query("filter") type: String):  

Шаг 2. Обновите модель обзора обзора

Вы запрашиваете данные из MarsApiService в getMarsRealEstateProperties() в OverviewViewModel . Теперь вам нужно обновить этот запрос, чтобы принять аргумент фильтра.

  1. Откройте overview/OverviewViewModel.kt . Вы увидите ошибки в Android Studio из-за изменений, внесенных на предыдущем шаге. Добавьте MarsApiFilter (перечисление возможных значений фильтра) в качестве параметра getMarsRealEstateProperties() .

    Импортируйте com.example.android.marsrealestate.network.MarsApiFilter по запросу.
private fun getMarsRealEstateProperties(filter: MarsApiFilter) {
  1. Измените вызов getProperties() в службе Retrofit, чтобы передать этот запрос фильтра в виде строки.
var getPropertiesDeferred = MarsApi.retrofitService.getProperties(filter.value)
  1. В блоке init {} передайте MarsApiFilter.SHOW_ALL в качестве аргумента для getMarsRealEstateProperties() , чтобы отобразить все свойства при первой загрузке приложения.
init {
   getMarsRealEstateProperties(MarsApiFilter.SHOW_ALL)
}
  1. В конце класса добавьте метод updateFilter() , который принимает аргумент MarsApiFilter и вызывает getMarsRealEstateProperties() с этим аргументом.
fun updateFilter(filter: MarsApiFilter) {
   getMarsRealEstateProperties(filter)
}

Шаг 3: Подключите фрагмент к меню опций

Последний шаг — подключить дополнительное меню к фрагменту для вызова updateFilter() в модели представления, когда пользователь выбирает пункт меню.

  1. Откройте res/menu/overflow_menu.xml . Приложение MarsRealEstate имеет существующее дополнительное меню, которое предоставляет три доступных параметра: отображение всех объектов недвижимости, отображение только объектов, сдаваемых в аренду, и отображение только объектов недвижимости, выставленных на продажу.
<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. Откройте overview/OverviewFragment.kt . В конце класса реализуйте метод onOptionsItemSelected() для обработки выбора пунктов меню.
override fun onOptionsItemSelected(item: MenuItem): Boolean {
} 
  1. В onOptionsItemSelected() вызовите метод updateFilter() для модели представления с соответствующим фильтром. Используйте блок Kotlin when {} для переключения между параметрами. Используйте MarsApiFilter.SHOW_ALL в качестве значения фильтра по умолчанию. Верните true , потому что вы обработали пункт меню. Импортируйте MarsApiFilter ( com.example.android.marsrealestate.network.MarsApiFilter ) по запросу. Полный onOptionsItemSelected() показан ниже.
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. Скомпилируйте и запустите приложение. Приложение запускает первую сетку обзора со всеми типами недвижимости и свойствами для продажи, отмеченными значком доллара.
  2. Выберите « Аренда » в меню параметров. Свойства перезагружаются, и ни одно из них не отображается со значком доллара. (Отображается только сдаваемая в аренду недвижимость.) Возможно, вам придется подождать несколько секунд, пока экран обновится и отобразятся только отфильтрованные объекты.
  3. Выберите « Купить » в меню параметров. Свойства перезагружаются снова, и все они отображаются со значком доллара. (Показаны только выставленные на продажу объекты.)

Теперь у вас есть прокручивающаяся сетка значков свойств Марса, но пришло время получить более подробную информацию. В этой задаче вы добавляете фрагмент сведений для отображения сведений об определенном свойстве. Детальный фрагмент покажет увеличенное изображение, цену и тип недвижимости — аренда или продажа.

Этот фрагмент запускается, когда пользователь касается изображения в обзорной сетке. Для этого вам нужно добавить прослушиватель onClick к элементам сетки RecyclerView , а затем перейти к новому фрагменту. Вы перемещаетесь, инициируя изменение LiveData в ViewModel , как вы это делали на протяжении всех этих уроков. Вы также используете подключаемый модуль Safe Args компонента Navigation для передачи выбранной информации MarsProperty из фрагмента обзора во фрагмент подробностей.

Шаг 1. Создайте модель подробного представления и обновите макет узла

Аналогично процессу, который вы использовали для обзорной модели представления и фрагментов, теперь вам необходимо реализовать модель представления и файлы компоновки для фрагмента сведений.

  1. Откройте detail/DetailViewModel.kt . Подобно тому, как связанные с сетью файлы Kotlin содержатся в network папке и файлы обзора в overview , detail сведений содержит файлы, связанные с представлением сведений. Обратите внимание, что класс DetailViewModel (сейчас пустой) принимает marsProperty в качестве параметра в конструкторе.
class DetailViewModel( marsProperty: MarsProperty,
                     app: Application) : AndroidViewModel(app) {
}
  1. Внутри определения класса добавьте LiveData для выбранного свойства Mars, чтобы представить эту информацию в подробном представлении. Следуйте обычному шаблону создания MutableLiveData для хранения самого MarsProperty , а затем предоставьте неизменяемое общедоступное свойство LiveData .

    Импортируйте androidx.lifecycle.LiveData и импортируйте androidx.lifecycle.MutableLiveData по запросу.
private val _selectedProperty = MutableLiveData<MarsProperty>()
val selectedProperty: LiveData<MarsProperty>
   get() = _selectedProperty
  1. Создайте блок init {} и задайте значение выбранного свойства Mars с помощью объекта MarsProperty из конструктора.
    init {
        _selectedProperty.value = marsProperty
    }
  1. Откройте res/layout/fragment_detail.xml и просмотрите его в режиме конструктора.

    Это файл макета для фрагмента детали. Он содержит ImageView для большой фотографии, TextView для типа недвижимости (аренда или продажа) и TextView для цены. Обратите внимание, что макет ограничения заключен в ScrollView , поэтому он будет автоматически прокручиваться, если представление становится слишком большим для отображения, например, когда пользователь просматривает его в ландшафтном режиме.
  2. Перейдите на вкладку « Текст » для макета. В верхней части макета, непосредственно перед элементом <ScrollView> , добавьте элемент <data> , чтобы связать модель подробного представления с макетом.
<data>
   <variable
       name="viewModel"
       type="com.example.android.marsrealestate.detail.DetailViewModel" />
</data>
  1. Добавьте атрибут app:imageUrl в элемент ImageView . Установите его в imgSrcUrl из выбранного свойства модели представления.

    Адаптер привязки, который загружает изображение с помощью Glide, также будет автоматически использоваться здесь, поскольку этот адаптер отслеживает все атрибуты app:imageUrl .
 app:imageUrl="@{viewModel.selectedProperty.imgSrcUrl}"

Шаг 2. Определите навигацию в обзорной модели представления

Когда пользователь нажимает на фотографию в обзорной модели, это должно инициировать переход к фрагменту, который показывает подробности о выбранном элементе.

  1. Откройте overview/OverviewViewModel.kt . Добавьте _navigateToSelectedProperty MutableLiveData и предоставьте ему неизменяемый LiveData .

    Когда эти LiveData ненулевыми, запускается навигация. (Вскоре вы добавите код для наблюдения за этой переменной и запуска навигации.)
private val _navigateToSelectedProperty = MutableLiveData<MarsProperty>()
val navigateToSelectedProperty: LiveData<MarsProperty>
   get() = _navigateToSelectedProperty
  1. В конце класса добавьте метод displayPropertyDetails() , который устанавливает _ navigateToSelectedProperty для выбранного свойства Mars.
fun displayPropertyDetails(marsProperty: MarsProperty) {
   _navigateToSelectedProperty.value = marsProperty
}
  1. Добавьте метод displayPropertyDetailsComplete() , который обнуляет значение _navigateToSelectedProperty . Это нужно, чтобы отметить состояние навигации как завершенное и избежать повторного запуска навигации, когда пользователь возвращается из подробного представления.
fun displayPropertyDetailsComplete() {
   _navigateToSelectedProperty.value = null
}

Шаг 3. Настройте прослушиватели кликов в сетевом адаптере и фрагменте.

  1. Откройте overview/PhotoGridAdapter.kt . В конце класса создайте собственный класс OnClickListener , который принимает лямбду с параметром marsProperty . Внутри класса определите onClick() , для которой задан параметр lambda.
class OnClickListener(val clickListener: (marsProperty:MarsProperty) -> Unit) {
     fun onClick(marsProperty:MarsProperty) = clickListener(marsProperty)
}
  1. Прокрутите вверх до определения класса PhotoGridAdapter и добавьте в конструктор частное свойство OnClickListener .
class PhotoGridAdapter( private val onClickListener: OnClickListener ) :
       ListAdapter<MarsProperty,              
           PhotoGridAdapter.MarsPropertyViewHolder>(DiffCallback) {
  1. Сделайте фото кликабельным, добавив onClickListener в элемент сетки в onBindviewHolder() . Определите прослушиватель кликов между вызовами getItem() and bind() .
override fun onBindViewHolder(holder: MarsPropertyViewHolder, position: Int) {
   val marsProperty = getItem(position)
   holder.itemView.setOnClickListener {
       onClickListener.onClick(marsProperty)
   }
   holder.bind(marsProperty)
}
  1. Откройте overview/OverviewFragment.kt . В onCreateView() замените строку, которая инициализирует свойство binding.photosGrid.adapter , строкой, показанной ниже.

    Этот код добавляет объект PhotoGridAdapter.onClickListener в конструктор PhotoGridAdapter и вызывает viewModel.displayPropertyDetails() с переданным объектом MarsProperty . Это запускает LiveData в модели представления для навигации.
binding.photosGrid.adapter = PhotoGridAdapter(PhotoGridAdapter.OnClickListener {
   viewModel.displayPropertyDetails(it)
})

Шаг 4: Измените навигационный график и сделайте MarsProperty разделяемым

Когда пользователь нажимает фотографию в сетке обзора, приложение должно перейти к фрагменту сведений и просмотреть сведения о выбранном свойстве Марса, чтобы в представлении сведений можно было отобразить эту информацию.

Прямо сейчас у вас есть прослушиватель кликов из PhotoGridAdapter для обработки нажатия и способ запуска навигации из модели представления. Но у вас еще нет объекта MarsProperty , передаваемого фрагменту сведений. Для этого вы используете Safe Args из компонента навигации.

  1. Откройте res/navigation/nav_graph.xml . Щелкните вкладку « Текст », чтобы просмотреть код XML для навигационного графика.
  2. Внутри элемента <fragment> для фрагмента сведений добавьте элемент <argument> , показанный ниже. Этот аргумент, названный selectedProperty , имеет тип MarsProperty .
<argument
   android:name="selectedProperty"
   app:argType="com.example.android.marsrealestate.network.MarsProperty"
   />
  1. Скомпилируйте приложение. Навигация выдает ошибку, потому что MarsProperty не подлежит разделу . Интерфейс Parcelable позволяет сериализовать объекты, чтобы данные объектов можно было передавать между фрагментами или действиями. В этом случае, чтобы данные внутри объекта MarsProperty передавались во фрагмент детали через Safe Args, MarsProperty должен реализовать интерфейс Parcelable . Хорошая новость заключается в том, что Kotlin предоставляет простой способ реализации этого интерфейса.
  2. Откройте network/MarsProperty.kt . Добавьте аннотацию @Parcelize в определение класса.

    Импортируйте kotlinx.android.parcel.Parcelize по запросу.

    Аннотация @Parcelize использует расширения Kotlin Android для автоматической реализации методов в интерфейсе Parcelable для этого класса. Вам больше ничего не нужно делать!
@Parcelize
data class MarsProperty (
  1. Измените определение класса MarsProperty , чтобы расширить Parcelable .

    Импортируйте android.os.Parcelable по запросу.

    Определение класса MarsProperty теперь выглядит так:
@Parcelize
data class MarsProperty (
       val id: String,
       @Json(name = "img_src") val imgSrcUrl: String,
       val type: String,
       val price: Double) : Parcelable {

Шаг 5: Соедините фрагменты

Вы по-прежнему не осуществляете навигацию — фактическая навигация происходит во фрагментах. На этом шаге вы добавляете последние биты для реализации навигации между фрагментами обзора и подробностей.

  1. Откройте overview/OverviewFragment.kt . В onCreateView() под строками, которые инициализируют адаптер сетки фотографий, добавьте строки, показанные ниже, для наблюдения за navigatedToSelectedProperty из модели обзора обзора.

    Импортируйте androidx.lifecycle.Observer и androidx.navigation.fragment.findNavController по запросу.

    Наблюдатель проверяет, не равен ли MarsPropertyit в лямбде — null, и если да, то получает навигационный контроллер из фрагмента с помощью findNavController() . Вызовите displayPropertyDetailsComplete() , чтобы указать модели представления сбросить LiveData в нулевое состояние, чтобы вы случайно не активировали навигацию снова, когда приложение вернется обратно к OverviewFragment .
viewModel.navigateToSelectedProperty.observe(this, Observer {
   if ( null != it ) {   
      this.findNavController().navigate(
              OverviewFragmentDirections.actionShowDetail(it))             
      viewModel.displayPropertyDetailsComplete()
   }
})
  1. Открыть detail/DetailFragment.kt . Добавьте эту строку сразу после вызова setLifecycleOwner() в onCreateView() . Эта строка получает выбранный объект MarsProperty из Safe Args.

    Обратите внимание на использование ненулевого оператора утверждения Kotlin ( !! ). Если selectedProperty там нет, значит, произошло что-то ужасное, и вы действительно хотите, чтобы код выдавал нулевой указатель. (В производственном коде вы должны каким-то образом обрабатывать эту ошибку.)
 val marsProperty = DetailFragmentArgs.fromBundle(arguments!!).selectedProperty
  1. Добавьте эту строку, чтобы получить новый DetailViewModelFactory . Вы будете использовать DetailViewModelFactory для получения экземпляра DetailViewModel . Стартовое приложение включает в себя реализацию DetailViewModelFactory , поэтому все, что вам нужно сделать здесь, это инициализировать ее.
val viewModelFactory = DetailViewModelFactory(marsProperty, application)
  1. Наконец, добавьте эту строку, чтобы получить модель DetailViewModel с завода и соединить все части.
      binding.viewModel = ViewModelProviders.of(
                this, viewModelFactory).get(DetailViewModel::class.java)
  1. Скомпилируйте и запустите приложение, а затем коснитесь любой фотографии Марса. Фрагмент сведений появляется для сведений об этом свойстве. Нажмите кнопку «Назад», чтобы вернуться на страницу обзора, и обратите внимание, что экран подробностей по-прежнему немного скуден. Вы закончите добавлять данные свойства на эту страницу сведений в следующей задаче.

Прямо сейчас на странице сведений отображается только та же фотография Марса, которую вы привыкли видеть на странице обзора. Класс MarsProperty также имеет тип недвижимости (аренда или покупка) и цену недвижимости. Детальный экран должен включать оба этих значения, и было бы полезно, если бы в арендной собственности указывалось, что цена указана за месяц. Вы используете преобразования LiveData в модели представления для реализации обеих этих вещей.

  1. Откройте res/values/strings.xml . Начальный код включает строковые ресурсы, показанные ниже, которые помогут вам создать строки для подробного представления. Для цены вы будете использовать либо ресурс display_price_monthly_rental , либо ресурс display_price , в зависимости от типа свойства.
<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. Откройте detail/DetailViewModel.kt . В нижней части класса добавьте код, показанный ниже.

    Импортируйте androidx.lifecycle.Transformations , если требуется.

    Это преобразование проверяет, является ли выбранное имущество сдаваемым в аренду, используя тот же тест из первой задачи. Если свойство является арендным, преобразование выбирает соответствующую строку из ресурсов с помощью Kotlin when {} . Обе эти строки нуждаются в числе в конце, поэтому впоследствии вы объединяете 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)
}
  1. Импортируйте сгенерированный класс R , чтобы получить доступ к строковым ресурсам в проекте.
import com.example.android.marsrealestate.R
  1. После преобразования displayPropertyPrice добавьте код, показанный ниже. Это преобразование объединяет несколько строковых ресурсов в зависимости от того, является ли тип свойства арендным.
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. Откройте res/layout/fragment_detail.xml . Осталось сделать еще одну вещь — привязать новые строки (созданные с помощью преобразований LiveData ) к подробному представлению. Для этого вы устанавливаете значение текстового поля для текста типа свойства в viewModel.displayPropertyType , а текстовое поле для текста значения цены — в 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. Скомпилируйте и запустите приложение. Теперь все данные о свойствах отображаются на странице сведений в красивом формате.

Проект Android Studio: MarsRealEstateFinal

Выражения привязки

  • Используйте выражения привязки в файлах макета XML для выполнения простых программных операций, таких как математические или условные тесты, над связанными данными.
  • Чтобы ссылаться на классы внутри файла макета, используйте <import> внутри тега <data> .

Параметры запроса веб-службы

  • Запросы к веб-сервисам могут включать необязательные параметры.
  • Чтобы указать параметры запроса в запросе, используйте аннотацию @Query в Retrofit .

Удасити курс:

Документация для разработчиков Android:

Другой:

В этом разделе перечислены возможные домашние задания для студентов, которые работают с этой кодовой лабораторией в рамках курса, проводимого инструктором. Инструктор должен сделать следующее:

  • При необходимости задайте домашнее задание.
  • Объясните учащимся, как сдавать домашние задания.
  • Оценивайте домашние задания.

Преподаватели могут использовать эти предложения так мало или так часто, как они хотят, и должны свободно давать любые другие домашние задания, которые они считают подходящими.

Если вы работаете с этой кодовой лабораторией самостоятельно, не стесняйтесь использовать эти домашние задания, чтобы проверить свои знания.

Ответьте на эти вопросы

Вопрос 1

Что делает <import> в XML-файле макета?

▢ Включить один файл макета в другой.

▢ Вставьте код Kotlin в файл макета.

▢ Обеспечьте доступ к свойствам с привязкой к данным.

▢ Позволяет ссылаться на классы и члены классов в выражениях привязки.

вопрос 2

Как добавить параметр запроса к вызову веб-службы REST в Retrofit?

▢ Добавьте запрос в конец URL-адреса запроса.

▢ Добавьте параметр для запроса в функцию, которая делает запрос, и аннотируйте этот параметр с помощью @Query .

▢ Используйте класс Query для создания запроса.

▢ Используйте метод addQuery() в построителе дооснащения.

Начать следующий урок: 9.1: Репозиторий

Ссылки на другие лаборатории кода в этом курсе см. на целевой странице лаборатории кода Android Kotlin Fundamentals .