Эта практическая работа входит в курс «Основы Android Kotlin». Вы получите максимальную пользу от этого курса, если будете выполнять практические работы последовательно. Все практические работы курса перечислены на целевой странице практической работы «Основы Android Kotlin» .
Введение
В предыдущей работе вы узнали, как получать данные из веб-сервиса и преобразовывать ответ в объект данных. В этой работе вы, используя эти знания, загрузите и отобразите фотографии с веб-адреса. Вы также повторите, как создать RecyclerView и использовать его для отображения сетки изображений на странице обзора.
Что вам уже следует знать
- Как создавать и использовать фрагменты.
- Как использовать компоненты архитектуры, включая модели представлений, фабрики моделей представлений, преобразования и
LiveData. - Как извлечь JSON из веб-сервиса REST и преобразовать эти данные в объекты Kotlin с помощью библиотек Retrofit и Moshi .
- Как построить сетку с помощью
RecyclerView. - Как работают
Adapter,ViewHolderиDiffUtil.
Чему вы научитесь
- Как использовать библиотеку Glide для загрузки и отображения изображения с веб-URL.
- Как использовать
RecyclerViewи адаптер сетки для отображения сетки изображений. - Как обрабатывать потенциальные ошибки при загрузке и отображении изображений.
Что ты будешь делать?
- Измените приложение MarsRealEstate так, чтобы оно получало URL-адрес изображения из данных о недвижимости Mars, и используйте Glide для загрузки и отображения этого изображения.
- Добавьте в приложение анимацию загрузки и значок ошибки.
- Используйте
RecyclerViewдля отображения сетки изображений объектов недвижимости на Марсе. - Добавьте статус и обработку ошибок в
RecyclerView.
В этой практической работе (и связанных с ней практических работах) вы будете работать с приложением MarsRealEstate, которое показывает недвижимость, выставленную на продажу на Марсе. Приложение подключается к интернет-серверу для получения и отображения данных о недвижимости, включая такие сведения, как цена и доступность недвижимости для продажи или аренды. Изображения каждого объекта недвижимости — это реальные фотографии с Марса, сделанные марсоходами NASA.

Версия приложения, которую вы создадите в этой лабораторной работе, заполняет страницу обзора, отображающую сетку изображений. Изображения являются частью данных о недвижимости, которые ваше приложение получает из веб-сервиса недвижимости Mars. Ваше приложение будет использовать библиотеку Glide для загрузки и отображения изображений, а также RecyclerView для создания сетки изображений. Ваше приложение также будет корректно обрабатывать сетевые ошибки.
Отображение фотографии с веб-ссылки может показаться простым, но для его корректной работы требуется немало инженерных решений. Изображение необходимо загрузить, буферизировать и декодировать из сжатого формата в формат, подходящий для Android. Изображение должно быть кэшировано в памяти, в хранилище или в обоих. Всё это должно происходить в низкоприоритетных фоновых потоках, чтобы пользовательский интерфейс оставался отзывчивым. Кроме того, для оптимальной производительности сети и процессора может потребоваться загрузка и декодирование нескольких изображений одновременно. Изучение того, как эффективно загружать изображения из сети, само по себе может стать отдельной лабораторной работой.
К счастью, вы можете использовать разработанную сообществом библиотеку Glide для загрузки, буферизации, декодирования и кэширования изображений. Glide значительно упрощает задачу, чем если бы вам пришлось делать всё это с нуля.
По сути, Glide нуждается в двух вещах:
- URL-адрес изображения, которое вы хотите загрузить и отобразить.
- Объект
ImageViewдля отображения этого изображения.
В этом задании вы научитесь использовать Glide для отображения одного изображения из веб-сервиса недвижимости. Вы отображаете изображение, представляющее первый объект недвижимости на Марсе в списке объектов, возвращаемых веб-сервисом. Вот скриншоты «до» и «после»:


Шаг 1: Добавьте зависимость Glide
- Откройте приложение MarsRealEstate из последнего практического занятия. (Если у вас нет приложения, вы можете скачать MarsRealEstateNetwork здесь.)
- Запустите приложение и посмотрите, что оно делает. (Оно отображает текстовую информацию о недвижимости, которая предположительно доступна на Марсе.)
- Откройте build.gradle (Модуль: app) .
- В разделе
dependenciesдобавьте эту строку для библиотеки Glide:
implementation "com.github.bumptech.glide:glide:$version_glide"
Обратите внимание, что номер версии уже определен отдельно в файле Gradle проекта.
- Нажмите «Синхронизировать сейчас» , чтобы перестроить проект с новой зависимостью.
Шаг 2: Обновите модель представления
Затем вы обновляете класс OverviewViewModel , чтобы включить в него актуальные данные для одного свойства Mars.
- Откройте
overview/OverviewViewModel.kt. Чуть нижеLiveDataдля_responseдобавьте внутренние (изменяемые) и внешние (неизменяемые) данные Live для одного объекта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, этот тест устанавливает значение_propertyLiveDataравным первому свойству в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для привязки к компонентуimgSrcUrlpropertyLiveData:
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и перейдите на вкладку «Предварительный просмотр» справа. Для изображения ошибки используется значок «Broken Image» из встроенной библиотеки значков. Этот векторный объект использует атрибут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: Обновите модель представления
Сейчас в модели представления есть свойство LiveData _property , которое содержит один объект MarsProperty — первый в списке ответов веб-сервиса. На этом этапе вы изменяете это LiveData так, чтобы оно содержало весь список объектов MarsProperty .
- Откройте
overview/OverviewViewModel.kt. - Измените частную переменную
_propertyна_properties. Измените тип на список объектовMarsProperty.
private val _properties = MutableLiveData<List<MarsProperty>>()- Замените внешние
propertylive data на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по запросу.
Для привязкиMarsPropertyк макету вам понадобится переменнаяGridViewItemBinding, поэтому передайте её в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. Поскольку ранее в этой практической работе вы добавили поддержку_propertiesLiveData, полный ответ веб-сервиса остался неиспользованным. Здесь вам понадобится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 {}установите_propertiesLiveDataпустой список. Это очистит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видимым и назначьте ему анимацию загрузки. Это тот же анимационный объект, который вы использовали для Glide в предыдущей задаче. Импортируйтеandroid.view.Viewпо запросу.
when (status) {
MarsApiStatus.LOADING -> {
statusImageView.visibility = View.VISIBLE
statusImageView.setImageResource(R.drawable.loading_animation)
}
}- Добавьте случай для состояния ошибки —
MarsApiStatus.ERROR. Аналогично тому, как вы сделали для состоянияLOADING, установите дляImageViewстатус visible и повторно используйте отрисовываемый объект connection-error.
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при изменении свойства status в модели представления.
<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и макетом.
Курс Udacity:
Документация для разработчиков 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 .