Эта кодовая лаборатория является частью курса Android Kotlin Fundamentals. Вы получите максимальную отдачу от этого курса, если будете последовательно работать с лабораториями кода. Все кодовые лаборатории курса перечислены на целевой странице кодовых лабораторий Android Kotlin Fundamentals .
Введение
В предыдущей лаборатории кода вы узнали, как получить данные из веб-службы и преобразовать ответ в объект данных. В этой лаборатории кода вы опираетесь на эти знания, чтобы загружать и отображать фотографии с URL-адреса в Интернете. Вы также вернетесь к тому, как создать RecyclerView
и использовать его для отображения сетки изображений на обзорной странице.
Что вы уже должны знать
- Как создавать и использовать фрагменты.
- Как использовать компоненты архитектуры, включая модели представлений, фабрики моделей представлений, преобразования и
LiveData
. - Как получить JSON из веб-службы REST и проанализировать эти данные в объектах Kotlin с помощью библиотек Retrofit и Moshi .
- Как создать макет сетки с помощью
RecyclerView
. - Как работают
Adapter
,ViewHolder
иDiffUtil
.
Что вы узнаете
- Как использовать библиотеку Glide для загрузки и отображения изображения с веб-URL.
- Как использовать
RecyclerView
и адаптер сетки для отображения сетки изображений. - Как обрабатывать потенциальные ошибки при загрузке и отображении изображений.
Что ты будешь делать
- Измените приложение MarsRealEstate, чтобы получить URL-адрес изображения из данных свойства Mars, и используйте Glide для загрузки и отображения этого изображения.
- Добавьте в приложение анимацию загрузки и значок ошибки.
- Используйте
RecyclerView
для отображения сетки изображений свойств Марса. - Добавьте статус и обработку ошибок в
RecyclerView
.
В этой лаборатории кода (и связанных с ней лабораториях кода) вы работаете с приложением MarsRealEstate, которое показывает недвижимость, выставленную на продажу на Марсе. Приложение подключается к интернет-серверу для получения и отображения данных о недвижимости, включая такие сведения, как цена и доступность недвижимости для продажи или аренды. Изображения, представляющие каждое свойство, представляют собой реальные фотографии с Марса, сделанные марсоходами НАСА.
Версия приложения, которую вы создаете в этой кодовой лаборатории, заполняет обзорную страницу, на которой отображается сетка изображений. Изображения являются частью данных о собственности, которые ваше приложение получает от веб-службы недвижимости Mars. Ваше приложение будет использовать библиотеку Glide для загрузки и отображения изображений и RecyclerView
для создания макета сетки для изображений. Ваше приложение также будет изящно обрабатывать сетевые ошибки.
Отображение фотографии с URL-адреса в Интернете может показаться простым, но для того, чтобы это работало хорошо, требуется немало инженерных разработок. Изображение должно быть загружено, помещено в буфер и декодировано из сжатого формата в изображение, которое может использовать Android. Изображение должно быть кэшировано в кеше в памяти, в кеше хранилища или в том и другом. Все это должно происходить в низкоприоритетных фоновых потоках, чтобы пользовательский интерфейс оставался отзывчивым. Кроме того, для наилучшей производительности сети и процессора вам может потребоваться получить и декодировать более одного изображения одновременно. Изучение того, как эффективно загружать изображения из сети, само по себе может стать лабораторией кода.
К счастью, вы можете использовать разработанную сообществом библиотеку под названием Glide для загрузки, буферизации, декодирования и кэширования изображений. Glide оставляет вам гораздо меньше работы, чем если бы вам пришлось делать все это с нуля.
Glide в основном нужны две вещи:
- URL-адрес изображения, которое вы хотите загрузить и показать.
- Объект
ImageView
для отображения этого изображения.
В этом задании вы узнаете, как использовать Glide для отображения одного изображения из веб-службы недвижимости. Вы отображаете изображение, представляющее первое свойство Mars в списке свойств, возвращаемых веб-службой. Вот скриншоты до и после:
Шаг 1: Добавьте зависимость Glide
- Откройте приложение MarsRealEstate из последней лаборатории кода. (Вы можете скачать MarsRealEstateNetwork здесь, если у вас нет приложения.)
- Запустите приложение, чтобы увидеть, что оно делает. (Он отображает текстовые сведения о свойстве, которое гипотетически доступно на Марсе.)
- Откройте build.gradle (Модуль: приложение) .
- В разделе
dependencies
добавьте эту строку для библиотеки Glide:
implementation "com.github.bumptech.glide:glide:$version_glide"
Обратите внимание, что номер версии уже определен отдельно в файле проекта Gradle.
- Нажмите « Синхронизировать сейчас» , чтобы перестроить проект с новой зависимостью.
Шаг 2. Обновите модель представления
Затем вы обновляете класс OverviewViewModel
, чтобы включить оперативные данные для одного свойства Mars.
- Откройте
overview/OverviewViewModel.kt
. Сразу подLiveData
для_response
добавьте как внутренние (изменяемые), так и внешние (неизменяемые) оперативные данные для одного объектаMarsProperty
.
Импортируйте классMarsProperty
(com.example.android.marsrealestate.network.MarsProperty
) по запросу.
private val _property = MutableLiveData<MarsProperty>()
val property: LiveData<MarsProperty>
get() = _property
- В
getMarsRealEstateProperties()
найдите строку внутри блокаtry/catch {}
, которая задает для_response.value
количество свойств. Добавьте тест, показанный ниже. Если объектыMarsProperty
доступны, этот тест устанавливает значение_property
LiveData
в первое свойство вlistResult
.
if (listResult.size > 0) {
_property.value = listResult[0]
}
Полный блок try/catch {}
теперь выглядит так:
try {
var listResult = getPropertiesDeferred.await()
_response.value = "Success: ${listResult.size} Mars properties retrieved"
if (listResult.size > 0) {
_property.value = listResult[0]
}
} catch (e: Exception) {
_response.value = "Failure: ${e.message}"
}
- Откройте файл
res/layout/fragment_overview.xml
. В элементе<TextView>
изменитеandroid:text
для привязки к компонентуimgSrcUrl
property
LiveData
:
android:text="@{viewModel.property.imgSrcUrl}"
- Запустите приложение.
TextView
отображает только URL-адрес изображения в первом свойстве Mars. Все, что вы сделали до сих пор, — это настроили модель представления и оперативные данные для этого URL.
Шаг 3. Создайте адаптер привязки и вызовите Glide.
Теперь у вас есть URL-адрес изображения для отображения, и пришло время начать работу с Glide для загрузки этого изображения. На этом этапе вы используете адаптер привязки для получения URL-адреса из XML-атрибута, связанного с ImageView
, и используете Glide для загрузки изображения. Адаптеры привязки — это методы расширения, которые располагаются между представлением и связанными данными для обеспечения пользовательского поведения при изменении данных. В этом случае пользовательское поведение заключается в вызове Glide для загрузки изображения из URL-адреса в ImageView
.
- Откройте
BindingAdapters.kt
. Этот файл будет содержать адаптеры привязки, которые вы используете в приложении. - Создайте функцию
bindImage()
, которая принимаетImageView
иString
в качестве параметров. Аннотируйте функцию с помощью@BindingAdapter
. Аннотация@BindingAdapter
сообщает привязке данных, что вы хотите, чтобы этот адаптер привязки выполнялся, когда элемент XML имеет атрибутimageUrl
.
Импортируйтеandroidx.databinding.BindingAdapter
иandroid.widget.ImageView
по запросу.
@BindingAdapter("imageUrl")
fun bindImage(imgView: ImageView, imgUrl: String?) {
}
- Внутри функции
bindImage()
добавьте блокlet {}
для аргументаimgUrl
:
imgUrl?.let {
}
- Внутри блока
let {}
добавьте показанную ниже строку, чтобы преобразовать строку URL (из XML) в объектUri
. Импортируйтеandroidx.core.net.toUri
по запросу.
Вы хотите, чтобы окончательный объектUri
использовал схему HTTPS, потому что сервер, с которого вы загружаете изображения, требует этой схемы. Чтобы использовать схему HTTPS, добавьтеbuildUpon.scheme("https")
кtoUri
. МетодtoUri()
— это функция расширения Kotlin из базовой библиотеки Android KTX, поэтому он выглядит так, как будто он является частью классаString
.
val imgUri = imgUrl.toUri().buildUpon().scheme("https").build()
- Внутри
let {}
вызовитеGlide.with()
, чтобы загрузить изображение из объектаUri
вImageView
. Импортируйтеcom.bumptech.glide.Glide
по запросу.
Glide.with(imgView.context)
.load(imgUri)
.into(imgView)
Шаг 4: Обновите макет и фрагменты
Хотя Glide загрузил изображение, пока ничего не видно. Следующим шагом является обновление макета и фрагментов с помощью ImageView
для отображения изображения.
- Откройте
res/layout/gridview_item.xml
. Это файл ресурсов макета, который вы будете использовать для каждого элемента вRecyclerView
позже в лаборатории кода. Вы используете его временно здесь, чтобы показать только одно изображение. - Над элементом
<ImageView>
добавьте элемент<data>
для привязки данных и привяжите его к классуOverviewViewModel
:
<data>
<variable
name="viewModel"
type="com.example.android.marsrealestate.overview.OverviewViewModel" />
</data>
- Добавьте атрибут
app:imageUrl
в элементImageView
, чтобы использовать новый адаптер привязки загрузки изображения:
app:imageUrl="@{viewModel.property.imgSrcUrl}"
- Откройте
overview/OverviewFragment.kt
. ВonCreateView()
закомментируйте строку, которая расширяет классFragmentOverviewBinding
и присваивает его переменной привязки. Это временно; вы вернетесь к этому позже.
//val binding = FragmentOverviewBinding.inflate(inflater)
- Вместо этого добавьте строку для расширения класса
GridViewItemBinding
. Импортируйтеcom.example.android.marsrealestate. databinding.GridViewItemBinding
по запросу.
val binding = GridViewItemBinding.inflate(inflater)
- Запустите приложение. Теперь вы должны увидеть фотографию изображения с первого
MarsProperty
в списке результатов.
Шаг 5: Добавьте простую загрузку и изображения ошибок
Glide может улучшить взаимодействие с пользователем, показывая замещающее изображение при загрузке изображения и изображение ошибки, если загрузка не удалась, например, если изображение отсутствует или повреждено. На этом этапе вы добавляете эту функциональность в адаптер привязки и в макет.
- Откройте
res/drawable/ic_broken_image.xml
и щелкните вкладку Preview справа. Для изображения ошибки вы используете значок сломанного изображения, доступный во встроенной библиотеке значков. Этот векторный рисунок использует атрибутandroid:tint
для окрашивания значка в серый цвет.
- Откройте
res/drawable/loading_animation.xml
. Этот объект рисования представляет собой анимацию, определенную с помощью<animate-rotate>
. Анимация вращает рисуемое изображениеloading_img.xml
вокруг центральной точки. (Вы не видите анимацию в предварительном просмотре.)
- Вернитесь к файлу
BindingAdapters.kt
. ВbindImage()
обновите вызовGlide.with()
, чтобы вызвать функциюapply()
междуload()
иinto()
. Импортируйтеcom.bumptech.glide.request.RequestOptions
по запросу.
Этот код устанавливает загружаемое изображение заполнителя, которое будет использоваться во время загрузки (рисуемый объектloading_animation
). Код также устанавливает изображение, которое будет использоваться, если загрузка изображения не удалась (broken_image
). ПолныйbindImage()
теперь выглядит так:
@BindingAdapter("imageUrl")
fun bindImage(imgView: ImageView, imgUrl: String?) {
imgUrl?.let {
val imgUri =
imgUrl.toUri().buildUpon().scheme("https").build()
Glide.with(imgView.context)
.load(imgUri)
.apply(RequestOptions()
.placeholder(R.drawable.loading_animation)
.error(R.drawable.ic_broken_image))
.into(imgView)
}
}
- Запустите приложение. В зависимости от скорости вашего сетевого подключения вы можете ненадолго увидеть загружаемое изображение, когда Glide загружает и отображает изображение свойства. Но вы все равно не увидите значок сломанного изображения, даже если отключите сеть — вы исправите это в последней части кодовой лаборатории.
Теперь ваше приложение загружает информацию о свойствах из Интернета. Используя данные из первого элемента списка MarsProperty
, вы создали свойство LiveData
в модели представления и использовали URL-адрес изображения из данных этого свойства для заполнения ImageView
. Но цель состоит в том, чтобы ваше приложение отображало сетку изображений, поэтому вы хотите использовать RecyclerView
с GridLayoutManager
.
Шаг 1. Обновите модель представления
Прямо сейчас в модели представления есть _property
LiveData
, который содержит один объект MarsProperty
— первый в списке ответов от веб-службы. На этом шаге вы изменяете эти LiveData
, чтобы они содержали весь список объектов MarsProperty
.
- Откройте
overview/OverviewViewModel.kt
. - Измените приватную переменную
_property
на_properties
. Измените тип на список объектовMarsProperty
.
private val _properties = MutableLiveData<List<MarsProperty>>()
- Замените оперативные данные внешнего
property
properties
. Сюда же добавьте список в типLiveData
:
val properties: LiveData<List<MarsProperty>>
get() = _properties
- Прокрутите вниз до
getMarsRealEstateProperties()
. Внутри блокаtry {}
замените весь тест, который вы добавили в предыдущей задаче, строкой, показанной ниже. Поскольку переменнаяlistResult
содержит список объектовMarsProperty
, вы можете просто присвоить ее_properties.value
вместо проверки успешного ответа.
_properties.value = listResult
Весь блок try/catch
теперь выглядит так:
try {
var listResult = getPropertiesDeferred.await()
_response.value = "Success: ${listResult.size} Mars properties retrieved"
_properties.value = listResult
} catch (e: Exception) {
_response.value = "Failure: ${e.message}"
}
Шаг 2: Обновите макеты и фрагменты
Следующим шагом является изменение макета и фрагментов приложения для использования представления повторного использования и макета сетки, а не представления с одним изображением.
- Откройте
res/layout/gridview_item.xml
. Измените привязку данных сOverviewViewModel
наMarsProperty
и переименуйте переменную в"property"
.
<variable
name="property"
type="com.example.android.marsrealestate.network.MarsProperty" />
- В
<ImageView>
измените атрибутapp:imageUrl
, чтобы он ссылался на URL-адрес изображения в объектеMarsProperty
:
app:imageUrl="@{property.imgSrcUrl}"
- Откройте
overview/OverviewFragment.kt
. ВonCreateview()
раскомментируйте строку, которая увеличиваетFragmentOverviewBinding
. Удалите или закомментируйте строку, которая увеличиваетGridViewBinding
. Эти изменения отменяют временные изменения, сделанные вами в последней задаче.
val binding = FragmentOverviewBinding.inflate(inflater)
// val binding = GridViewItemBinding.inflate(inflater)
- Откройте
res/layout/fragment_overview.xml
. Удалите весь элемент<TextView>
. - Вместо этого добавьте этот элемент
<RecyclerView>
, который используетGridLayoutManager
и макетgrid_view_item
для одного элемента:
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/photos_grid"
android:layout_width="0dp"
android:layout_height="0dp"
android:padding="6dp"
android:clipToPadding="false"
app:layoutManager=
"androidx.recyclerview.widget.GridLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:spanCount="2"
tools:itemCount="16"
tools:listitem="@layout/grid_view_item" />
Шаг 3. Добавьте адаптер сетки фотографий
Теперь в макете fragment_overview
есть RecyclerView
, а в макете grid_view_item
— один ImageView
. На этом этапе вы привязываете данные к RecyclerView
через адаптер RecyclerView
.
- Откройте
overview/PhotoGridAdapter.kt
. - Создайте класс
PhotoGridAdapter
с параметрами конструктора, показанными ниже. КлассPhotoGridAdapter
расширяетListAdapter
, конструктору которого требуется тип элемента списка, держатель представления и реализацияDiffUtil.ItemCallback
.
Импортируйтеandroidx.recyclerview.widget.ListAdapter
иcom.example.android.marsrealestate.network.MarsProperty
по запросу. На следующих шагах вы реализуете другие отсутствующие части этого конструктора, которые вызывают ошибки.
class PhotoGridAdapter : ListAdapter<MarsProperty,
PhotoGridAdapter.MarsPropertyViewHolder>(DiffCallback) {
}
- Щелкните в любом месте класса
PhotoGridAdapter
и нажмитеControl+i
, чтобы реализовать методыListAdapter
, а именноonCreateViewHolder()
иonBindViewHolder()
.
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PhotoGridAdapter.MarsPropertyViewHolder {
TODO("not implemented")
}
override fun onBindViewHolder(holder: PhotoGridAdapter.MarsPropertyViewHolder, position: Int) {
TODO("not implemented")
}
- В конце определения класса
PhotoGridAdapter
после только что добавленных методов добавьте определение сопутствующего объекта дляDiffCallback
, как показано ниже.
Импортируйтеandroidx.recyclerview.widget.DiffUtil
по запросу.
ОбъектDiffCallback
расширяетDiffUtil.ItemCallback
типом объекта, который вы хотите сравнить —MarsProperty
.
companion object DiffCallback : DiffUtil.ItemCallback<MarsProperty>() {
}
- Нажмите
Control+i
, чтобы реализовать методы сравнения для этого объекта, а именноareItemsTheSame()
иareContentsTheSame()
.
override fun areItemsTheSame(oldItem: MarsProperty, newItem: MarsProperty): Boolean {
TODO("not implemented")
}
override fun areContentsTheSame(oldItem: MarsProperty, newItem: MarsProperty): Boolean {
TODO("not implemented") }
- Для
areItemsTheSame()
удалите TODO. Используйте оператор ссылочного равенства Kotlin (===
), который возвращаетtrue
, если ссылки на объекты дляoldItem
иnewItem
совпадают.
override fun areItemsTheSame(oldItem: MarsProperty,
newItem: MarsProperty): Boolean {
return oldItem === newItem
}
- Для
areContentsTheSame()
используйте стандартный оператор равенства только для идентификатораoldItem
иnewItem
.
override fun areContentsTheSame(oldItem: MarsProperty,
newItem: MarsProperty): Boolean {
return oldItem.id == newItem.id
}
- По-прежнему внутри класса
PhotoGridAdapter
, ниже объекта-компаньона, добавьте определение внутреннего класса дляMarsPropertyViewHolder
, которое расширяетRecyclerView.ViewHolder
.
Импортируйтеandroidx.recyclerview.widget.RecyclerView
иcom.example.android.marsrealestate.databinding.GridViewItemBinding
по запросу.
Вам нужна переменнаяGridViewItemBinding
для привязкиMarsProperty
к макету, поэтому передайте переменную вMarsPropertyViewHolder
. Поскольку базовому классуViewHolder
требуется представление в его конструкторе, вы передаете ему корневое представление привязки.
class MarsPropertyViewHolder(private var binding:
GridViewItemBinding):
RecyclerView.ViewHolder(binding.root) {
}
- В
MarsPropertyViewHolder
создайте методbind()
, который принимает объектMarsProperty
в качестве аргумента и устанавливаетbinding.property
к этому объекту. ВызовитеexecutePendingBindings()
после установки свойства, что приведет к немедленному выполнению обновления.
fun bind(marsProperty: MarsProperty) {
binding.property = marsProperty
binding.executePendingBindings()
}
- В
onCreateViewHolder()
удалите TODO и добавьте строку, показанную ниже. Импортируйтеandroid.view.LayoutInflater
по запросу.
МетодonCreateViewHolder()
должен возвращать новыйMarsPropertyViewHolder
, созданный путем увеличенияGridViewItemBinding
и использованияLayoutInflater
из контекста родительскойViewGroup
.
return MarsPropertyViewHolder(GridViewItemBinding.inflate(
LayoutInflater.from(parent.context)))
- В
onBindViewHolder()
удалите TODO и добавьте строки, показанные ниже. Здесь вы вызываетеgetItem()
, чтобы получить объектMarsProperty
связанный с текущей позициейRecyclerView
, а затем передаете это свойство методуbind()
вMarsPropertyViewHolder
.
val marsProperty = getItem(position)
holder.bind(marsProperty)
Шаг 4: Добавьте адаптер для привязки и соедините детали
Наконец, используйте BindingAdapter
для инициализации PhotoGridAdapter
со списком объектов MarsProperty
. Использование BindingAdapter
для установки данных RecyclerView
приводит к тому, что привязка данных автоматически LiveData
для списка объектов MarsProperty
. Затем адаптер привязки вызывается автоматически при изменении списка MarsProperty
.
- Откройте
BindingAdapters.kt
. - В конце файла добавьте метод
bindRecyclerView()
, который принимаетRecyclerView
и список объектовMarsProperty
в качестве аргументов. Аннотируйте этот метод с помощью@BindingAdapter
.
Импортируйтеandroidx.recyclerview.widget.RecyclerView
иcom.example.android.marsrealestate.network.MarsProperty
по запросу.
@BindingAdapter("listData")
fun bindRecyclerView(recyclerView: RecyclerView,
data: List<MarsProperty>?) {
}
- Внутри функции
bindRecyclerView()
recyclerView.adapter
кPhotoGridAdapter
и вызовитеadapter.submitList()
с данными. Это сообщаетRecyclerView
, когда доступен новый список.
Импортируйте com.example.android.marsrealestate.overview.PhotoGridAdapter
по запросу.
val adapter = recyclerView.adapter as PhotoGridAdapter
adapter.submitList(data)
- Откройте
res/layout/fragment_overview.xml
. Добавьте атрибутapp:listData
в элементRecyclerView
и задайте для него значениеviewmodel.properties
используя привязку данных.
app:listData="@{viewModel.properties}"
- Откройте
overview/OverviewFragment.kt
. ВonCreateView()
непосредственно перед вызовомsetHasOptionsMenu()
инициализируйте адаптерRecyclerView
вbinding.photosGrid
новым объектомPhotoGridAdapter
.
binding.photosGrid.adapter = PhotoGridAdapter()
- Запустите приложение. Вы должны увидеть сетку изображений
MarsProperty
. Когда вы прокручиваете, чтобы увидеть новые изображения, приложение показывает значок процесса загрузки перед отображением самого изображения. Если вы включите режим полета, изображения, которые еще не загружены, отображаются в виде значков неработающих изображений.
Приложение MarsRealEstate отображает значок сломанного изображения, когда изображение не может быть загружено. Но когда нет сети, приложение показывает пустой экран.
Это не лучший пользовательский опыт. В этой задаче вы добавляете базовую обработку ошибок, чтобы дать пользователю лучшее представление о том, что происходит. Если Интернет недоступен, приложение отобразит значок ошибки подключения. Пока приложение получает список MarsProperty
, оно будет отображать анимацию загрузки.
Шаг 1. Добавьте статус в модель представления.
Для начала вы создаете LiveData
в модели представления для представления статуса веб-запроса. Следует учитывать три состояния: загрузка, успех и сбой. Состояние загрузки происходит, когда вы ожидаете данных в вызове await()
.
- Откройте
overview/OverviewViewModel.kt
. В верхней части файла (после импорта, перед определением класса) добавьтеenum
для представления всех доступных статусов:
enum class MarsApiStatus { LOADING, ERROR, DONE }
- Переименуйте как внутренние, так и внешние определения оперативных данных
_response
в классеOverviewViewModel
в_status
. Поскольку ранее в этой лаборатории кода вы добавили поддержку_properties
LiveData
, полный ответ веб-службы не использовался. Здесь вам нужныLiveData
, чтобы отслеживать текущий статус, поэтому вы можете просто переименовать существующие переменные.
Кроме того, измените типы со String
на MarsApiStatus.
private val _status = MutableLiveData<MarsApiStatus>()
val status: LiveData<MarsApiStatus>
get() = _status
- Прокрутите вниз до
getMarsRealEstateProperties()
и также обновите_response
до_status
. Измените строку"Success"
на состояниеMarsApiStatus.DONE
, а строку"Failure"
— наMarsApiStatus.ERROR
. - Добавьте статус
MarsApiStatus.LOADING
в начало блокаtry {}
перед вызовомawait()
. Это начальный статус во время работы сопрограммы и ожидания данных. Полный блокtry/catch {}
теперь выглядит так:
try {
_status.value = MarsApiStatus.LOADING
var listResult = getPropertiesDeferred.await()
_status.value = MarsApiStatus.DONE
_properties.value = listResult
} catch (e: Exception) {
_status.value = MarsApiStatus.ERROR
}
- После состояния ошибки в блоке
catch {}
установите_properties
LiveData
в пустой список. Это очищаетRecyclerView
.
} catch (e: Exception) {
_status.value = MarsApiStatus.ERROR
_properties.value = ArrayList()
}
Шаг 2. Добавьте адаптер привязки для состояния ImageView.
Теперь у вас есть статус в модели представления, но это просто набор состояний. Как сделать так, чтобы он отображался в самом приложении? На этом шаге вы используете ImageView
, подключенный к привязке данных, для отображения значков состояний загрузки и ошибок. Когда приложение находится в состоянии загрузки или в состоянии ошибки, ImageView
должен быть виден. Когда приложение загружается, ImageView
должен быть невидимым.
- Откройте
BindingAdapters.kt
. Добавьте новый адаптер привязки с именемbindStatus()
, который принимает значенияImageView
иMarsApiStatus
в качестве аргументов. Импортируйтеcom.example.android.marsrealestate.overview.MarsApiStatus
по запросу.
@BindingAdapter("marsApiStatus")
fun bindStatus(statusImageView: ImageView,
status: MarsApiStatus?) {
}
- Добавьте
when {}
в методbindStatus()
для переключения между различными статусами.
when (status) {
}
- Внутри
when {}
добавьте случай для состояния загрузки (MarsApiStatus.LOADING
). Для этого состояния установите дляImageView
значение visible и назначьте ему анимацию загрузки. Это та же анимация, которую вы использовали для Glide в предыдущей задаче. Импортируйтеandroid.view.View
по запросу.
when (status) {
MarsApiStatus.LOADING -> {
statusImageView.visibility = View.VISIBLE
statusImageView.setImageResource(R.drawable.loading_animation)
}
}
- Добавьте случай для состояния ошибки
MarsApiStatus.ERROR
. Аналогично тому, что вы сделали для состоянияLOADING
, установите для состоянияImageView
значение visible и повторно используйте отрисовку ошибки подключения.
MarsApiStatus.ERROR -> {
statusImageView.visibility = View.VISIBLE
statusImageView.setImageResource(R.drawable.ic_connection_error)
}
- Добавьте кейс для состояния готовности, которым является
MarsApiStatus.DONE
. Здесь у вас есть успешный ответ, поэтому отключите видимость статусаImageView
, чтобы скрыть его.
MarsApiStatus.DONE -> {
statusImageView.visibility = View.GONE
}
Шаг 3: Добавьте статус ImageView в макет
- Откройте
res/layout/fragment_overview.xml
. Под элементомRecyclerView
внутриConstraintLayout
добавьтеImageView
, показанный ниже.
ЭтотImageView
имеет те же ограничения, что иRecyclerView
. Однако ширина и высота используютwrap_content
для центрирования изображения, а не растягивания изображения для заполнения представления. Также обратите внимание на атрибутapp:marsApiStatus
, в котором представление вызывает вашBindingAdapter
при изменении свойства состояния в модели представления.
<ImageView
android:id="@+id/status_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:marsApiStatus="@{viewModel.status}" />
- Включите режим полета в эмуляторе или устройстве, чтобы имитировать отсутствующее сетевое соединение. Скомпилируйте и запустите приложение, и обратите внимание, что появляется изображение ошибки:
- Нажмите кнопку «Назад», чтобы закрыть приложение и отключить режим полета. Используйте экран недавних, чтобы вернуть приложение. В зависимости от скорости вашего сетевого подключения вы можете увидеть очень короткий индикатор загрузки, когда приложение запрашивает веб-службу до того, как изображения начнут загружаться.
Проект Android Studio: MarsRealEstateGrid
- Чтобы упростить процесс управления изображениями, используйте библиотеку Glide для загрузки, буферизации, декодирования и кэширования изображений в вашем приложении.
- Glide нужны две вещи для загрузки изображения из Интернета: URL-адрес изображения и объект
ImageView
для размещения изображения. Чтобы указать эти параметры, используйте методыload()
иinto()
с Glide. - Адаптеры привязки — это методы расширения, которые располагаются между представлением и связанными данными этого представления. Адаптеры привязки обеспечивают настраиваемое поведение при изменении данных, например, для вызова Glide для загрузки изображения с URL-адреса в
ImageView
. - Адаптеры привязки — это методы расширения, помеченные аннотацией
@BindingAdapter
. - Чтобы добавить параметры к запросу Glide, используйте метод
apply()
. Например, используйтеapply()
сplaceholder()
, чтобы указать загрузку, и используйтеapply()
сerror()
, чтобы указать ошибку. - Чтобы создать сетку изображений, используйте
RecyclerView
сGridLayoutManager
. - Чтобы обновлять список свойств при его изменении, используйте адаптер привязки между
RecyclerView
и макетом.
Удасити курс:
Документация для разработчиков Android:
Другой:
В этом разделе перечислены возможные домашние задания для студентов, которые работают с этой кодовой лабораторией в рамках курса, проводимого инструктором. Инструктор должен сделать следующее:
- При необходимости задайте домашнее задание.
- Объясните учащимся, как сдавать домашние задания.
- Оценивайте домашние задания.
Преподаватели могут использовать эти предложения так мало или так часто, как они хотят, и должны свободно давать любые другие домашние задания, которые они считают подходящими.
Если вы работаете с этой кодовой лабораторией самостоятельно, не стесняйтесь использовать эти домашние задания, чтобы проверить свои знания.
Ответьте на эти вопросы
Вопрос 1
Какой метод Glide вы используете для указания ImageView
, который будет содержать загруженное изображение?
▢ into()
▢ with()
▢ imageview()
▢ apply()
вопрос 2
Как указать замещающее изображение, которое будет отображаться при загрузке Glide?
▢ Используйте метод into()
с чертежом.
▢ Используйте RequestOptions()
и вызовите метод placeholder()
с возможностью рисования.
▢ Назначьте свойство Glide.placeholder
объекту.
▢ Используйте RequestOptions()
и вызовите метод loadingImage()
с возможностью рисования.
Вопрос 3
Как указать, что метод является адаптером привязки?
▢ Вызовите метод setBindingAdapter()
для LiveData
.
▢ Поместите метод в файл Kotlin с именем BindingAdapters.kt
.
▢ Используйте атрибут android:adapter
в макете XML.
▢ Аннотируйте метод @BindingAdapter
.
Начать следующий урок:
Ссылки на другие лаборатории кода в этом курсе см. на целевой странице лаборатории кода Android Kotlin Fundamentals .