この Codelab は、Android Kotlin の基礎コースの一部です。このコースを最大限に活用するには、Codelab を順番に進めることをおすすめします。コースのすべての Codelab は、Android Kotlin の基礎の Codelab のランディング ページに一覧表示されています。
はじめに
前回の Codelab では、ウェブサービスからデータを取得し、レスポンスを解析してデータ オブジェクトに変換する方法を学習しました。この Codelab では、その知識に基づいて、ウェブ URL から写真を読み込んで表示します。また、RecyclerView を作成して概要ページに画像のグリッドを表示する方法もおさらいします。
前提となる知識
- フラグメントの作成方法と使用方法。
- ビューモデル、ビューモデル ファクトリ、変換、
LiveDataなどのアーキテクチャ コンポーネントの使用方法。 - REST ウェブサービスから JSON を取得する方法と、Retrofit ライブラリおよび Moshi ライブラリを使用してそのデータを解析し、Kotlin オブジェクトに変換する方法。
RecyclerViewを使用してグリッド レイアウトを作成する方法。Adapter、ViewHolder、DiffUtilの機能。
学習内容
- Glide ライブラリを使用してウェブ URL から画像を読み込んで表示する方法。
RecyclerViewとグリッド アダプターを使用して画像のグリッドを表示する方法。- 画像をダウンロードして表示する際に発生するエラーの処理方法。
演習内容
- MarsRealEstate アプリを変更して、火星の不動産物件データから画像 URL を取得し、Glide を使用してその画像を読み込んで表示します。
- 読み込み中のアニメーションとエラーアイコンをアプリに追加します。
RecyclerViewを使用して火星の物件画像のグリッドを表示します。- ステータス処理とエラー処理を
RecyclerViewに追加します。
この Codelab(および関連する Codelab)では、火星で販売されている不動産物件を表示する MarsRealEstate というアプリを使用します。アプリはインターネット サーバーに接続して、価格や販売または賃貸が可能かどうかなどの詳細を含む不動産データを取得して表示します。各物件を表す画像は、NASA の火星探査機が撮影した火星の実際の写真です。

この Codelab で作成するバージョンのアプリは概要ページにデータを書き込みます。概要ページでは画像のグリッドを表示します。これらの画像は、アプリが Mars 不動産ウェブサービスから取得した物件データの一部です。アプリは Glide ライブラリを使用して画像の読み込みと表示を行い、RecyclerView を使用して画像のグリッド レイアウトを作成します。また、アプリはネットワーク エラーを適切に処理します。
ウェブ URL からの写真を表示するのは簡単に思えますが、適切に処理するにはかなりの作業が必要です。画像をダウンロードしてバッファリングし、圧縮形式から Android が使用できる形式にデコードする必要があります。メモリ内キャッシュとストレージベースのキャッシュの両方またはいずれかに画像を保存する必要があります。これらすべてを優先度の低いバックグラウンド スレッドで行いつつ、UI の応答性を維持する必要があります。また、ネットワークと CPU のパフォーマンスを最適化するため、複数の画像を一度に取得してデコードする必要もあります。ネットワークから画像を効果的に読み込む方法を学ぶことは、それ自体が Codelab になり得ます。
幸いなことに、コミュニティで開発された Glide というライブラリを使用して、画像のダウンロード、バッファリング、デコード、キャッシュ保存を行うことができます。Glide を使用すると、すべてをゼロから行うよりもはるかに手間が省けます。
Glide は、基本的に次の 2 つのものを必要とします。
- 読み込んで表示する画像の URL。
- 画像を表示するための
ImageViewオブジェクト。
このタスクでは、Glide を使用して、不動産ウェブサービスから取得した単一の画像を表示する方法を学びます。ウェブサービスから返された不動産物件のリストに含まれる最初の火星の不動産物件を表す画像を表示します。画像の表示前と表示後のスクリーンショットを次に示します。


ステップ 1: Glide の依存関係を追加する
- 前回の Codelab で作成した MarsRealEstate アプリを開きます。(アプリをお持ちでない場合は、MarsRealEstateNetwork をこちらからダウンロードできます)。
- アプリを実行して動作を確認します(火星で利用可能な仮想の物件のテキストの詳細が表示されます)。
- build.gradle(モジュール: app)を開きます。
dependenciesセクションで、Glide ライブラリ用に次の行を追加します。
implementation "com.github.bumptech.glide:glide:$version_glide"
バージョン番号は、プロジェクトの Gradle ファイルで個別に定義されています。
- [Sync Now] をクリックし、新しい依存関係でプロジェクトを再ビルドします。
ステップ 2: ビューモデルを更新する
次に、OverviewViewModel クラスを更新して、1 つの Mars プロパティのライブデータを含めます。
overview/OverviewViewModel.ktを開きます。_responseのLiveDataのすぐ下に、単一のMarsPropertyオブジェクトの内部(可変)ライブデータと外部(不変)ライブデータの両方を追加します。
リクエストされたら、MarsPropertyクラス(com.example.android.marsrealestate.network.MarsProperty)をインポートします。
private val _property = MutableLiveData<MarsProperty>()
val property: LiveData<MarsProperty>
get() = _propertygetMarsRealEstateProperties()メソッドの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を変更してpropertyLiveDataのimgSrcUrlコンポーネントにバインドします。
android:text="@{viewModel.property.imgSrcUrl}"- アプリを実行します。
TextViewには、最初の火星プロパティの画像の URL のみが表示されます。ここまでの作業では、その URL 用にビューモデルとライブデータを設定しただけです。

ステップ 3: バインディング アダプターを作成して Glide を呼び出す
表示する画像の URL が取得できたので、Glide を使用してその画像を読み込む作業を開始します。このステップでは、バインディング アダプターを使用して、ImageView に関連付けられた XML 属性から URL を取得し、Glide を使用して画像を読み込みます。バインディング アダプターは、ビューとバインドされたデータをつなげる拡張メソッドで、データが変更されたときのカスタム動作を提供します。この場合、カスタム動作は Glide を呼び出して URL から ImageView に画像を読み込むことです。
BindingAdapters.ktを開きます。このファイルは、アプリ全体で使用するバインディング アダプターを保持します。- パラメータとして
ImageViewとStringを受け取るbindImage()関数を作成します。関数に@BindingAdapterアノテーションを付けます。@BindingAdapterアノテーションは、XML アイテムにimageUrl属性がある場合にこのバインディング アダプターを実行するようデータ バインディングに指示します。
リクエストされたら、androidx.databinding.BindingAdapterとandroid.widget.ImageViewをインポートします。
@BindingAdapter("imageUrl")
fun bindImage(imgView: ImageView, imgUrl: String?) {
}bindImage()関数内で、imgUrl引数にlet {}ブロックを追加します。
imgUrl?.let {
}let {}ブロック内に次の行を追加し、URL 文字列(XML から)をUriオブジェクトに変換します。リクエストされたらandroidx.core.net.toUriをインポートします。
最終的なUriオブジェクトで HTTPS スキームを使用します。これは、画像の取得元サーバーでこのスキームが必要になるためです。HTTPS スキームを使用するには、buildUpon.scheme("https")をtoUriビルダーの末尾に追加します。toUri()メソッドは Android KTX Core ライブラリの Kotlin 拡張関数であるため、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を開きます。これは、この Codelab の後半でRecyclerViewの各アイテムに使用するレイアウト リソース ファイルです。ここでは、単一の画像のみを表示するために一時的に使用します。<ImageView>要素の上にデータ バインディング用の<data>要素を追加して、OverviewViewModelクラスにバインドします。
<data>
<variable
name="viewModel"
type="com.example.android.marsrealestate.overview.OverviewViewModel" />
</data>- 新しい画像読み込みバインディング アダプターを使用するため、
ImageView要素にapp:imageUrl属性を追加します。
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()の呼び出しを更新して、load()とinto()の間でapply()関数を呼び出すようにします。リクエストされたら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 がプロパティ画像をダウンロードして表示する間、一瞬だけ読み込み中の画像が表示されます。しかし、ネットワークを切断しても、破損した画像のアイコンは表示されません。この問題は、Codelab の最後の部分で修正します。
以上で、アプリはインターネットから物件情報を読み込むようになりました。最初の MarsProperty リストアイテムからのデータを使用して、ビューモデルに LiveData プロパティを作成し、そのプロパティ データからの画像 URL を使用して ImageView にデータを入力しました。しかし、アプリの目標は画像のグリッドを表示することです。そこで、GridLayoutManager で RecyclerView を使用します。
ステップ 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 = listResulttry/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: レイアウトとフラグメントを更新する
次のステップでは、アプリのレイアウトとフラグメントを変更して、単一の画像ビューを使用する代わりに、RecyclerView とグリッド レイアウトを使用するようにします。
res/layout/gridview_item.xmlを開きます。データ バインディングをOverviewViewModelからMarsPropertyに変更し、変数の名前を"property"に変更します。
<variable
name="property"
type="com.example.android.marsrealestate.network.MarsProperty" /><ImageView>内でapp:imageUrl属性を変更して、MarsPropertyオブジェクトに含まれる画像 URL を参照するようにします。
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オブジェクトは、比較したいオブジェクト(MarsProperty)の型を使ってDiffUtil.ItemCallbackを拡張します。
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 の参照の等価性演算子(===)を使用します。oldItemとnewItemのオブジェクト参照が同じ場合はtrueを返します。
override fun areItemsTheSame(oldItem: MarsProperty,
newItem: MarsProperty): Boolean {
return oldItem === newItem
}areContentsTheSame()の場合は、oldItemとnewItemの ID に対して標準の等価演算子を使用します。
override fun areContentsTheSame(oldItem: MarsProperty,
newItem: MarsProperty): Boolean {
return oldItem.id == newItem.id
}PhotoGridAdapterクラス内で、コンパニオン オブジェクトの下に、RecyclerView.ViewHolderを拡張するMarsPropertyViewHolderの内部クラス定義を追加します。
必要に応じて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内で、MarsPropertyオブジェクトを引数として受け取り、binding.propertyをそのオブジェクトに設定するbind()メソッドを作成します。プロパティを設定した後、executePendingBindings()を呼び出します。これにより、更新が直ちに実行されます。
fun bind(marsProperty: MarsProperty) {
binding.property = marsProperty
binding.executePendingBindings()
}onCreateViewHolder()で、TODO を削除して下記の行を追加します。リクエストされたら、android.view.LayoutInflaterをインポートします。onCreateViewHolder()メソッドは新しいMarsPropertyViewHolderを返す必要があります。このビューホルダーは、GridViewItemBindingをインフレートし、親ViewGroupコンテキストからのLayoutInflaterを使用することにより、作成されます。
return MarsPropertyViewHolder(GridViewItemBinding.inflate(
LayoutInflater.from(parent.context)))onBindViewHolder()メソッドで、TODO を削除して下記の行を追加します。ここでgetItem()を呼び出し、現在のRecyclerViewの位置に関連付けられているMarsPropertyオブジェクトを取得して、そのプロパティをMarsPropertyViewHolderのbind()メソッドに渡します。
val marsProperty = getItem(position)
holder.bind(marsProperty)ステップ 4: バインディング アダプターを追加して要素を接続する
最後に、BindingAdapter を使用して、MarsProperty オブジェクトのリストで PhotoGridAdapter を初期化します。BindingAdapter を使用して RecyclerView のデータを設定すると、LiveData に MarsProperty オブジェクトのリストがあるかどうかがデータ バインディングによって自動的に監視されるようになります。MarsProperty リストが変更されると、バインディング アダプターが自動的に呼び出されます。
BindingAdapters.ktを開きます。- ファイルの末尾に、
RecyclerViewとMarsPropertyオブジェクトのリストを引数として受け取るbindRecyclerView()メソッドを追加します。そのメソッドに@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を開きます。RecyclerView要素にapp:listData属性を追加し、データ バインディングを使用してそれをviewmodel.propertiesに設定します。
app:listData="@{viewModel.properties}"overview/OverviewFragment.ktを開きます。onCreateView()のsetHasOptionsMenu()の呼び出しの直前で、binding.photosGridのRecyclerViewアダプターを新しいPhotoGridAdapterオブジェクトに初期化します。
binding.photosGrid.adapter = PhotoGridAdapter()- アプリを実行します。
MarsProperty画像のグリッドが表示されます。スクロールして新しい画像を表示すると、画像自体が表示される前に、読み込みが進行中であることを示すアイコンが表示されます。機内モードをオンにすると、まだ読み込まれていない画像が、破損した画像のアイコンとして表示されます。

MarsRealEstate アプリは、画像を取得できない場合、破損した画像のアイコンを表示します。しかし、ネットワーク接続がない場合は、空白の画面を表示します。

これはユーザー エクスペリエンスとしては不適切です。このタスクでは、基本的なエラー処理を追加して、何が起こっているかをユーザーが理解できるようにします。インターネットが利用できない場合、アプリは接続エラーアイコンを表示します。MarsProperty リストの取得中は、読み込み中のアニメーションを表示します。
ステップ 1: ビューモデルにステータスを追加する
まず、ウェブ リクエストのステータスを表す LiveData をビューモデルに作成します。考慮する必要があるステータスは、読み込み中、成功、失敗の 3 つです。「読み込み中」ステータスは、await() の呼び出しでデータを待機している状態を示します。
overview/OverviewViewModel.ktを開きます。ファイルの先頭部分(インポートの後、クラス定義の前)に、すべての使用可能なステータスを表すenumを追加します。
enum class MarsApiStatus { LOADING, ERROR, DONE }OverviewViewModelクラス全体で、内部と外部の両方の_responseライブデータ定義の名前を_statusに変更します。この Codelab の前半で_propertiesLiveDataのサポートを追加したため、ウェブサービス レスポンス全体が未使用になっています。現在のステータスを追跡するにはLiveDataが必要です。既存の変数の名前を変更するだけで済みます。
また、型を String から MarsApiStatus. に変更します。
private val _status = MutableLiveData<MarsApiStatus>()
val status: LiveData<MarsApiStatus>
get() = _status- 下にスクロールして
getMarsRealEstateProperties()メソッドを表示し、ここでも_responseを_statusに更新します。"Success"文字列をMarsApiStatus.DONE状態に変更し、"Failure"文字列をMarsApiStatus.ERRORに変更します。 await()の呼び出しの前に、try {}ブロックの先頭にMarsApiStatus.LOADINGステータスを追加します。これは、コルーチンを実行してデータを待機しているときの初期ステータスです。完全な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を開きます。ImageViewとMarsApiStatus値を引数として受け取るbindStatus()という名前の新しいバインディング アダプターを追加します。リクエストされたら、com.example.android.marsrealestate.overview.MarsApiStatusをインポートします。
@BindingAdapter("marsApiStatus")
fun bindStatus(statusImageView: ImageView,
status: MarsApiStatus?) {
}- ステータスに応じて処理を切り替えるため、
bindStatus()メソッド内にwhen {}を追加します。
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を「表示」に設定し、接続エラー ドローアブルを再利用します。
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を開きます。ConstraintLayout内のRecyclerView要素の下に、下記の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オブジェクトの 2 つが必要です。これらのオプションを指定するには、Glide でload()メソッドとinto()メソッドを使用します。 - バインディング アダプターは、ビューとビューのバインドされたデータをつなげる拡張メソッドです。バインディング アダプターは、データが変更されたときのカスタム動作を提供します。たとえば、Glide を呼び出して URL から
ImageViewに画像を読み込むことができます。 - バインディング アダプターは、
@BindingAdapterアノテーション付きの拡張メソッドです。 - Glide リクエストにオプションを追加するには、
apply()メソッドを使用します。たとえば、placeholder()とともにapply()を使用して読み込み可能なドローアブルを指定し、error()とともにapply()を使用してエラー ドローアブルを指定します。 - 画像のグリッドを生成するには、
GridLayoutManagerでRecyclerViewを使用します。 - プロパティが変更されたときにプロパティのリストを更新するには、
RecyclerViewとレイアウトの間でバインディング アダプターを使用します。
Udacity コース:
Android デベロッパー ドキュメント:
その他:
このセクションでは、インストラクター主導のコースの一環として、この Codelab に取り組んでいる生徒向けに考えられる宿題をいくつか示します。インストラクターは、以下のようなことを行えます。
- 必要に応じて宿題を与える
- 宿題の提出方法を生徒に伝える
- 宿題を採点する
インストラクターは、これらの提案を必要なだけ使用し、必要に応じて他の宿題も自由に与えることができます。
この Codelab に独力で取り組む場合は、これらの宿題を自由に使用して知識をテストしてください。
以下の質問に回答してください
問題 1
読み込まれた画像を含む ImageView を示すには、どの Glide メソッドを使用しますか?
▢ into()
▢ with()
▢ imageview()
▢ apply()
問題 2
Glide の読み込み時に表示するプレースホルダ画像を指定するには、どうすればよいですか。
▢ ドローアブルで into() メソッドを使用する。
▢ RequestOptions() を使用し、ドローアブルで placeholder() メソッドを呼び出す。
▢ Glide.placeholder プロパティをドローアブルに割り当てます。
▢ RequestOptions() を使用し、ドローアブルで loadingImage() メソッドを呼び出す。
問題 3
メソッドがバインディング アダプターであることを示すには、どうすればよいですか。
▢ LiveData で setBindingAdapter() メソッドを呼び出します。
▢ メソッドを BindingAdapters.kt という Kotlin ファイルに配置する。
▢ XML レイアウトで android:adapter 属性を使用します。
▢ メソッドに @BindingAdapter アノテーションを付けます。
次のレッスン(
このコースの他の Codelab へのリンクについては、Android Kotlin の基礎の Codelab のランディング ページをご覧ください。