この Codelab は、Android Kotlin の基礎コースの一部です。このコースを最大限に活用するには、Codelab を順番に進めることをおすすめします。コースのすべての Codelab は、Android Kotlin の基礎の Codelab のランディング ページに一覧表示されています。
はじめに
この Codelab では、RecyclerView を使用してアイテムのリストを表示する方法について説明します。前の Codelab シリーズの睡眠トラッカー アプリを基に、推奨アーキテクチャで RecyclerView を使用してデータを表示する、より優れた汎用性の高い方法を学びます。
前提となる知識
以下について把握しておく必要があります。
- アクティビティ、フラグメント、ビューを使用して基本的なユーザー インターフェース(UI)を構築する。
- フラグメント間の移動、
safeArgsを使用してフラグメント間でデータを渡す。 - ビューモデル、ビューモデル ファクトリ、変換、
LiveDataとそのオブザーバーを使用します。 Roomデータベースの作成、DAO の作成、エンティティの定義。- データベース タスクやその他の長時間実行タスクにコルーチンを使用する。
学習内容
RecyclerViewをAdapterおよびViewHolderとともに使用して、アイテムのリストを表示する方法。
演習内容
- 前のレッスンで作成した TrackMySleepQuality アプリを変更して、
RecyclerViewを使用して睡眠の質のデータを表示します。
この Codelab では、睡眠の質をトラッキングするアプリの RecyclerView 部分を構築します。このアプリは、Room データベースを使用して睡眠データを経時的に保存します。
スターターの睡眠トラッカー アプリには、下の図に示すように、フラグメントで表される 2 つの画面があります。

左側に表示されている最初の画面には、トラッキングの開始と停止を行うボタンがあります。この画面には、ユーザーのすべての睡眠データも表示されます。[消去] ボタンをクリックすると、アプリがユーザーのために収集したすべてのデータが完全に削除されます。右側の 2 つ目の画面は、睡眠の質の評価を選択するための画面です。
このアプリは、UI コントローラ、ViewModel、LiveData を使用する簡略化されたアーキテクチャを使用します。また、このアプリは Room データベースを使用して睡眠データを永続化します。

最初の画面に表示される睡眠のリストは機能しますが、見栄えはよくありません。アプリは複雑なフォーマッタを使用して、テキスト ビューのテキスト文字列と品質の数値を生成します。また、この設計はスケーリングできません。この Codelab でこれらの問題をすべて修正すると、最終的なアプリは同じ機能を持ち、メイン画面は次のようになります。

データのリストやグリッドを表示することは、Android の UI タスクとしては非常に一般的なものです。リストは、単純なものから非常に複雑なものまでさまざまです。テキスト ビューのリストには、買い物リストなどのシンプルなデータが表示されることがあります。休暇の目的地を注釈付きでリスト表示するような複雑なリストでは、ヘッダー付きのスクロール可能なグリッド内に多くの詳細情報が表示されることがあります。
Android では、こうしたユースケースをすべてサポートするために RecyclerView ウィジェットが用意されています。

RecyclerView の最大のメリットは、大きなリストに対して非常に効率的であることです。
- デフォルトでは、
RecyclerViewは、現時点で画面に表示されるアイテムの処理や描画のための作業のみを行います。たとえば、リストには 1,000 個のアイテムがあるものの、画面には 10 個しか表示されない場合は、RecyclerViewはその 10 個の項目を描画するのに十分な作業しか行いません。ユーザーがスクロールすると、RecyclerViewは画面に新たに表示されるアイテムを認識して、その描画に十分な作業のみを行います。 - アイテムが画面外にスクロールされると、アイテムのビューがリサイクルされます。具体的には、画面内にスクロールしてくる新たなコンテンツがその項目に設定されます。このような
RecyclerViewの動作により、処理時間が大幅に短縮され、リストをスムーズにスクロールできます。 - アイテムが変更された場合、
RecyclerViewはリスト全体を再描画するのではなく、そのアイテム 1 つを更新できます。複雑なアイテムのリストを表示する際に、効率が大幅に向上します。
次のシーケンスでは、1 つのビューにデータ ABC が設定されています。そのビューが画面外にスクロールされると、RecyclerView でそのビューが新しいデータ XYZ に再利用されます。
アダプター パターン
コンセントの形状が異なる国間を移動したことがある方は、アダプターを使用してデバイスをコンセントに接続する方法をご存じでしょう。アダプターを使用すると、プラグのタイプを変換できます。これは、インターフェースを別のインターフェースに変換することに相当します。
ソフトウェア エンジニアリングのアダプター パターンは、オブジェクトが別の API と連携するのに役立ちます。RecyclerView は、アプリがデータを保存して処理する方法を変更することなく、アダプタを使用してアプリデータを RecyclerView が表示できるものに変換します。睡眠トラッカー アプリでは、ViewModel を変更せずに、Room データベースのデータを RecyclerView が表示できる形式に変換するアダプタを構築します。
RecyclerView の実装

RecyclerView にデータを表示するには、次の部分が必要です。
- 表示するデータ。
- レイアウト ファイルで定義された
RecyclerViewインスタンス。ビューのコンテナとして機能します。 - 1 つのデータ項目のレイアウト。
リスト項目がすべて同じように見える場合は、すべてに同じレイアウトを使用できますが、必須ではありません。アイテム レイアウトは、一度に 1 つのアイテム ビューを作成してデータで埋めることができるように、フラグメントのレイアウトとは別に作成する必要があります。 - レイアウト マネージャー。
レイアウト マネージャーは、ビュー内の UI コンポーネントの構成(レイアウト)を処理します。 - ビューホルダー。
ビューホルダーはViewHolderクラスを拡張します。アイテムのレイアウトから 1 つのアイテムを表示するためのビュー情報が含まれています。ビューホルダーにより、RecyclerViewで画面での効率的なビューの移動に使用される情報も追加されます。 - アダプター。
アダプターは、データをRecyclerViewに接続します。ViewHolderに表示できるようにデータを適応させます。RecyclerViewは、アダプタを使用して、データを画面にどのように表示するかを決定します。
このタスクでは、レイアウト ファイルに RecyclerView を追加し、RecyclerView に睡眠データを公開する Adapter を設定します。
ステップ 1: LayoutManager を使用して RecyclerView を追加する
このステップでは、fragment_sleep_tracker.xml ファイルの ScrollView を RecyclerView に置き換えます。
- GitHub から RecyclerViewFundamentals-Starter アプリをダウンロードします。
- アプリをビルドして実行します。データがシンプルなテキストとして表示されることを確認します。
- Android Studio の [Design] タブで
fragment_sleep_tracker.xmlレイアウト ファイルを開きます。 - [Component Tree] ペインで、
ScrollViewを削除します。この操作を行うと、ScrollView内のTextViewも削除されます。 - [Palette] パネルで、左側のコンポーネント タイプのリストをスクロールして [Containers] を見つけ、選択します。
- [Palette] ペインから [Component Tree] ペインに
RecyclerViewをドラッグします。RecyclerViewをConstraintLayoutの内側に配置します。

- 依存関係を追加するかどうかを尋ねるダイアログが開いたら、[OK] をクリックして、Android Studio が
recyclerview依存関係を Gradle ファイルに追加できるようにします。数秒後にアプリが同期されます。

- モジュールの
build.gradleファイルを開き、最後までスクロールして、次のコードのような新しい依存関係をメモします。
implementation 'androidx.recyclerview:recyclerview:1.0.0'
fragment_sleep_tracker.xmlに戻します。- [テキスト] タブで、次の
RecyclerViewコードを探します。
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent" />RecyclerViewにsleep_listのidを指定します。
android:id="@+id/sleep_list"RecyclerViewをConstraintLayout内の画面の残りの部分を占めるように配置します。そのため、RecyclerViewの上端を [Start] ボタンに、下端を [Clear] ボタンに、両端を親に制約します。レイアウト エディタまたは XML で、次のコードを使用してレイアウトの幅と高さを 0 dp に設定します。
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/clear_button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/stop_button"RecyclerViewXML にレイアウト マネージャーを追加します。すべてのRecyclerViewには、リスト内のアイテムの配置方法を指示するレイアウト マネージャーが必要です。Android にはLinearLayoutManagerが用意されています。これはデフォルトで、アイテムを全幅の行の縦方向のリストにレイアウトします。
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"- [Design] タブに切り替えると、追加した制約によって
RecyclerViewが利用可能なスペースを埋めるように拡大されていることがわかります。

ステップ 2: リストアイテムのレイアウトとテキスト ビュー ホルダーを作成する
RecyclerView はコンテナにすぎません。このステップでは、RecyclerView 内に表示されるアイテムのレイアウトとインフラストラクチャを作成します。
できるだけ早く動作する RecyclerView を作成するため、最初は睡眠の質を数値で表示するだけの単純なリスト項目を使用します。これには、ビューホルダー TextItemViewHolder が必要です。データ用のビュー(TextView)も必要です。(後のステップでは、ビューホルダーとすべての睡眠データをレイアウトする方法について詳しく説明します)。
text_item_view.xmlというレイアウト ファイルを作成します。テンプレート コードは置き換えるため、ルート要素として何を使用しても構いません。text_item_view.xmlで、指定されたコードをすべて削除します。TextViewを追加し、左右に16dpのパディング、テキストサイズを24spに設定します。幅は親に合わせ、高さはコンテンツをラップします。このビューはRecyclerView内に表示されるため、ビューをViewGroup内に配置する必要はありません。
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:textSize="24sp"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:layout_width="match_parent"
android:layout_height="wrap_content" />Util.ktを開きます。末尾までスクロールして、次の定義を追加します。これにより、TextItemViewHolderクラスが作成されます。コードは、ファイルの末尾の最後の閉じ中かっこの後に配置します。このビューホルダーは一時的なもので、後で置き換えるため、コードはUtil.ktに記述します。
class TextItemViewHolder(val textView: TextView): RecyclerView.ViewHolder(textView)- プロンプトが表示されたら、
android.widget.TextViewとandroidx.recyclerview.widget.RecyclerViewをインポートします。
ステップ 3: SleepNightAdapter を作成する
RecyclerView を実装する際のコアタスクは、アダプターの作成です。アイテムビュー用のシンプルなビューホルダーと、各アイテムのレイアウトがあります。これで、アダプターを作成できます。アダプターはビューホルダーを作成し、RecyclerView で表示するデータを入力します。
sleeptrackerパッケージで、SleepNightAdapterという名前の新しい Kotlin クラスを作成します。SleepNightAdapterクラスがRecyclerView.Adapterを拡張するようにします。このクラスは、SleepNightオブジェクトをRecyclerViewが使用できるものに適合させるため、SleepNightAdapterと呼ばれます。アダプタは使用するビューホルダーを認識する必要があるため、TextItemViewHolderを渡します。プロンプトが表示されたら必要なコンポーネントをインポートします。実装する必要がある必須メソッドがあるため、エラーが表示されます。
class SleepNightAdapter: RecyclerView.Adapter<TextItemViewHolder>() {}SleepNightAdapterの最上位で、データを保持するlistOfSleepNight変数を作成します。
var data = listOf<SleepNight>()SleepNightAdapterで、getItemCount()をオーバーライドして、dataの睡眠日数のリストのサイズを返します。RecyclerViewは、アダプターが表示するアイテムの数を把握する必要があります。そのため、getItemCount()を呼び出します。
override fun getItemCount() = data.sizeSleepNightAdapterで、次のようにonBindViewHolder()関数をオーバーライドします。onBindViewHolder()関数は、指定された位置にある 1 つのリストアイテムのデータを表示するためにRecyclerViewによって呼び出されます。onBindViewHolder()メソッドは、ビューホルダーとバインドするデータの位置という 2 つの引数を取ります。このアプリでは、ホルダーはTextItemViewHolderで、位置はリスト内の位置です。
override fun onBindViewHolder(holder: TextItemViewHolder, position: Int) {
}onBindViewHolder()内で、データ内の指定された位置にある 1 つのアイテムの変数を作成します。
val item = data[position]- 作成した
ViewHolderには、textViewというプロパティがあります。onBindViewHolder()内で、textViewのtextを睡眠の質の数値に設定します。このコードは数字のリストのみを表示しますが、この簡単な例で、アダプターがデータをビューホルダーに取得して画面に表示する方法を確認できます。
holder.textView.text = item.sleepQuality.toString()SleepNightAdapterで、onCreateViewHolder()をオーバーライドして実装します。これは、RecyclerViewがアイテムを表すビューホルダーを必要とするときに呼び出されます。
この関数は 2 つのパラメータを受け取り、ViewHolderを返します。ビューホルダーを保持するビューグループであるparentパラメータは、常にRecyclerViewです。viewTypeパラメータは、同じRecyclerViewに複数のビューがある場合に使用されます。たとえば、テキスト ビュー、画像、動画のリストをすべて同じRecyclerViewに配置した場合、onCreateViewHolder()関数はどのタイプのビューを使用するかを認識する必要があります。
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TextItemViewHolder {
}onCreateViewHolder()で、LayoutInflaterのインスタンスを作成します。
レイアウト インフレーターは、XML レイアウトからビューを作成する方法を認識しています。contextには、ビューを正しく拡張する方法に関する情報が含まれます。リサイクラー ビューのアダプターでは、常にparentビューグループのコンテキスト(RecyclerView)を渡します。
val layoutInflater = LayoutInflater.from(parent.context)onCreateViewHolder()で、layoutinflaterにインフレートをリクエストしてviewを作成します。
ビューの XML レイアウトと、ビューのparentビューグループを渡します。3 番目のブール引数はattachToRootです。この項目はRecyclerViewによって自動的にビュー階層に追加されるため、この引数はfalseにする必要があります。
val view = layoutInflater
.inflate(R.layout.text_item_view, parent, false) as TextViewonCreateViewHolder()で、viewで作成されたTextItemViewHolderを返します。
return TextItemViewHolder(view)- アダプターは
dataが変更されたときにRecyclerViewに通知する必要があります。これは、RecyclerViewがデータについて何も知らないためです。アダプターから渡されたビューホルダーのみを認識します。
表示しているデータが変更されたときにRecyclerViewに通知するには、SleepNightAdapterクラスの先頭にあるdata変数にカスタム セッターを追加します。セッターでdataに新しい値を指定し、notifyDataSetChanged()を呼び出して新しいデータでリストの再描画をトリガーします。
var data = listOf<SleepNight>()
set(value) {
field = value
notifyDataSetChanged()
}ステップ 4: RecyclerView に Adapter を通知する
RecyclerView は、ビューホルダーの取得に使用するアダプターについて知る必要があります。
SleepTrackerFragment.ktを開きます。onCreateview()でアダプターを作成します。このコードは、ViewModelモデルの作成後、returnステートメントの前に配置します。
val adapter = SleepNightAdapter()adapterをRecyclerViewと関連付けます。
binding.sleepList.adapter = adapter- プロジェクトをクリーンアップして再ビルドし、
bindingオブジェクトを更新します。binding.sleepListまたはbinding.FragmentSleepTrackerBindingに関するエラーが引き続き表示される場合は、キャッシュを無効にして再起動します。([File] > [Invalidate Caches / Restart] を選択します)。
ここでアプリを実行すると、エラーは発生しませんが、[Start]、[Stop] の順にタップしてもデータは表示されません。
ステップ 5: アダプタにデータを取得する
ここまでの作業により、アダプターと、アダプターから RecyclerView にデータを取得する方法ができました。次に、ViewModel からアダプターにデータを取得する必要があります。
SleepTrackerViewModelを開きます。- すべての睡眠夜を保存する
nights変数を見つけます。これが表示するデータです。nights変数は、データベースでgetAllNights()を呼び出すことで設定されます。 - この変数にアクセスする必要があるオブザーバーを作成するため、
nightsからprivateを削除します。宣言は次のようになります。
val nights = database.getAllNights()databaseパッケージで、SleepDatabaseDaoを開きます。getAllNights()関数を見つけます。この関数は、SleepNight値のリストをLiveDataとして返します。つまり、nights変数にはRoomによって更新されるLiveDataが含まれており、nightsを監視して変更を把握できます。SleepTrackerFragmentを開きます。onCreateView()で、adapterの作成の下に、nights変数のオブザーバーを作成します。
フラグメントのviewLifecycleOwnerをライフサイクル オーナーとして指定することで、このオブザーバーがアクティブになるのはRecyclerViewが画面に表示されているときのみになります。
sleepTrackerViewModel.nights.observe(viewLifecycleOwner, Observer {
})- オブザーバー内で、null 以外の値(
nightsの場合)を取得するたびに、その値をアダプターのdataに割り当てます。オブザーバーとデータの設定の完成したコードは次のとおりです。
sleepTrackerViewModel.nights.observe(viewLifecycleOwner, Observer {
it?.let {
adapter.data = it
}
})- コードをビルドして実行します。
アダプタが動作している場合は、睡眠の質の数値が一覧表示されます。左側のスクリーンショットは、[開始] をタップした後の -1 を示しています。右側のスクリーンショットは、[停止] をタップして睡眠の質を選択した後に更新された睡眠の質の数値を示しています。

ステップ 6: ビューホルダーが再利用される仕組みを確認する
RecyclerView はビューホルダーをリサイクルします。つまり、ビューホルダーを再利用します。ビューが画面外にスクロールされると、RecyclerView はそのビューを画面上にスクロールされるビューに再利用します。
これらのビューホルダーは再利用されるため、onBindViewHolder() は、前のアイテムがビューホルダーに設定したカスタマイズをすべて設定またはリセットするようにしてください。
たとえば、睡眠の質が 1 以下で、睡眠の質が低いことを表すビューホルダーのテキストの色を赤に設定できます。
SleepNightAdapterクラスで、onBindViewHolder()の末尾に次のコードを追加します。
if (item.sleepQuality <= 1) {
holder.textView.setTextColor(Color.RED) // red
}- アプリを実行します。
- 睡眠の質の低いデータを追加すると、数値が赤色になります。
- 画面に赤い大きな数字が表示されるまで、睡眠の質の高い評価を追加します。
RecyclerViewはビューホルダーを再利用するため、最終的に赤いビューホルダーの 1 つを高品質の評価に再利用します。高い評価が誤って赤色で表示される。

- この問題を解決するには、品質が 1 以下でない場合に色を黒に設定する
elseステートメントを追加します。
両方の条件を明示的に指定することで、ビューホルダーは各アイテムに正しいテキストの色を使用します。
if (item.sleepQuality <= 1) {
holder.textView.setTextColor(Color.RED) // red
} else {
// reset
holder.textView.setTextColor(Color.BLACK) // black
}- アプリを実行すると、数字は常に正しい色で表示されます。
これで、これで、完全に機能する基本的な RecyclerView が完成しました。
このタスクでは、シンプルなビューホルダーを、睡眠に関するより多くのデータを表示できるビューホルダーに置き換えます。
Util.kt に追加した単純な ViewHolder は、TextView を TextItemViewHolder でラップするだけです。
class TextItemViewHolder(val textView: TextView): RecyclerView.ViewHolder(textView)では、RecyclerView が TextView を直接使用しないのはなぜでしょうか?この 1 行のコードで多くの機能が提供されます。ViewHolder は、アイテムビューと、RecyclerView 内のアイテムビューの位置に関するメタデータを記述します。RecyclerView は、この機能を利用して、リストのスクロール時にビューを正しく配置したり、Adapter でアイテムが追加または削除されたときにビューをアニメーション化するなどの面白い処理を行ったりします。
RecyclerView が ViewHolder に保存されているビューにアクセスする必要がある場合は、ビューホルダーの itemView プロパティを使用してアクセスできます。RecyclerView は、画面に表示するアイテムをバインドするとき、境界線などのビューの周りに装飾を描画するとき、ユーザー補助機能を実装するときに itemView を使用します。
ステップ 1: アイテム レイアウトを作成する
このステップでは、1 つのアイテムのレイアウト ファイルを作成します。レイアウトは、睡眠の質を表す ImageView、睡眠時間を表す TextView、質をテキストで表す TextView を含む ConstraintLayout で構成されています。レイアウトはすでに作成済みなので、提供された XML コードをコピーして貼り付けます。
- 新しいレイアウト リソース ファイルを作成し、
list_item_sleep_nightという名前を付けます。 - ファイル内のコードをすべて次のコードに置き換えます。作成したレイアウトを確認します。
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/quality_image"
android:layout_width="@dimen/icon_size"
android:layout_height="60dp"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:srcCompat="@drawable/ic_sleep_5" />
<TextView
android:id="@+id/sleep_length"
android:layout_width="0dp"
android:layout_height="20dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/quality_image"
app:layout_constraintTop_toTopOf="@+id/quality_image"
tools:text="Wednesday" />
<TextView
android:id="@+id/quality_string"
android:layout_width="0dp"
android:layout_height="20dp"
android:layout_marginTop="8dp"
app:layout_constraintEnd_toEndOf="@+id/sleep_length"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="@+id/sleep_length"
app:layout_constraintTop_toBottomOf="@+id/sleep_length"
tools:text="Excellent!!!" />
</androidx.constraintlayout.widget.ConstraintLayout>- Android Studio の [Design] タブに切り替えます。デザイン ビューでは、レイアウトは下の左側のスクリーンショットのようになります。ブループリント ビューでは、右側のスクリーンショットのようになります。

ステップ 2: ViewHolder を作成する
SleepNightAdapter.ktを開きます。SleepNightAdapter内にViewHolderというクラスを作成し、RecyclerView.ViewHolderを拡張するようにします。
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){}ViewHolder内で、ビューへの参照を取得します。このViewHolderが更新するビューへの参照が必要です。このViewHolderをバインドするたびに、画像と両方のテキスト ビューにアクセスする必要があります。(このコードは後でデータ バインディングを使用するように変換します)。
val sleepLength: TextView = itemView.findViewById(R.id.sleep_length)
val quality: TextView = itemView.findViewById(R.id.quality_string)
val qualityImage: ImageView = itemView.findViewById(R.id.quality_image)ステップ 3: SleepNightAdapter で ViewHolder を使用する
SleepNightAdapter定義で、TextItemViewHolderの代わりに、先ほど作成したSleepNightAdapter.ViewHolderを使用します。
class SleepNightAdapter: RecyclerView.Adapter<SleepNightAdapter.ViewHolder>() {onCreateViewHolder() を更新します。
ViewHolderを返すようにonCreateViewHolder()のシグネチャを変更します。- レイアウト インフレータを変更して、正しいレイアウト リソース
list_item_sleep_nightを使用します。 TextViewへのキャストを削除します。TextItemViewHolderを返すのではなく、ViewHolderを返します。
更新されたonCreateViewHolder()関数の完成形は次のとおりです。
override fun onCreateViewHolder(
parent: ViewGroup, viewType: Int): ViewHolder {
val layoutInflater =
LayoutInflater.from(parent.context)
val view = layoutInflater
.inflate(R.layout.list_item_sleep_night,
parent, false)
return ViewHolder(view)
}onBindViewHolder() を更新します。
onBindViewHolder()のシグネチャを変更して、holderパラメータがTextItemViewHolderではなくViewHolderになるようにします。onBindViewHolder()内で、itemの定義以外のすべてのコードを削除します。- このビューの
resourcesへの参照を保持するvalresを定義します。
val res = holder.itemView.context.resourcessleepLengthテキスト ビューのテキストを再生時間に設定します。スターター コードで提供されている書式設定関数を呼び出す次のコードをコピーします。
holder.sleepLength.text = convertDurationToFormatted(item.startTimeMilli, item.endTimeMilli, res)convertDurationToFormatted()を定義する必要があるため、エラーが発生します。Util.ktを開き、コードとその関連インポートのコメントを解除します。([Code] > [Comment with Line comments] を選択します)。onBindViewHolder()に戻り、convertNumericQualityToString()を使用して画質を設定します。
holder.quality.text= convertNumericQualityToString(item.sleepQuality, res)- これらの関数を手動でインポートする必要がある場合があります。
import com.example.android.trackmysleepquality.convertDurationToFormatted
import com.example.android.trackmysleepquality.convertNumericQualityToString- 品質の正しいアイコンを設定します。新しい
ic_sleep_activeアイコンは、スターター コードで提供されています。
holder.qualityImage.setImageResource(when (item.sleepQuality) {
0 -> R.drawable.ic_sleep_0
1 -> R.drawable.ic_sleep_1
2 -> R.drawable.ic_sleep_2
3 -> R.drawable.ic_sleep_3
4 -> R.drawable.ic_sleep_4
5 -> R.drawable.ic_sleep_5
else -> R.drawable.ic_sleep_active
})- 完成した更新後の
onBindViewHolder()関数を次に示します。ViewHolderのすべてのデータを設定しています。
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = data[position]
val res = holder.itemView.context.resources
holder.sleepLength.text = convertDurationToFormatted(item.startTimeMilli, item.endTimeMilli, res)
holder.quality.text= convertNumericQualityToString(item.sleepQuality, res)
holder.qualityImage.setImageResource(when (item.sleepQuality) {
0 -> R.drawable.ic_sleep_0
1 -> R.drawable.ic_sleep_1
2 -> R.drawable.ic_sleep_2
3 -> R.drawable.ic_sleep_3
4 -> R.drawable.ic_sleep_4
5 -> R.drawable.ic_sleep_5
else -> R.drawable.ic_sleep_active
})
}- アプリを実行します。下のスクリーンショットのように、睡眠の質を示すアイコンと、睡眠時間と睡眠の質を示すテキストが表示されます。

RecyclerView が完了しました。Adapter と ViewHolder を実装する方法を学び、それらを組み合わせて RecyclerView Adapter でリストを表示しました。
ここまでのコードは、アダプターとビューホルダーを作成するプロセスを示しています。ただし、このコードは改善できます。表示するコードとビューホルダーを管理するコードが混在しており、onBindViewHolder() は ViewHolder を更新する方法の詳細を知っています。
本番環境のアプリでは、複数のビューホルダー、より複雑なアダプター、複数のデベロッパーによる変更が行われる可能性があります。ビューホルダーに関連するものはすべてビューホルダーに含めるように、コードを構造化する必要があります。
ステップ 1: onBindViewHolder() をリファクタリングする
このステップでは、コードをリファクタリングし、すべてのビューホルダー機能を ViewHolder に移動します。このリファクタリングの目的は、アプリのユーザー インターフェースを変更することではなく、デベロッパーがコードをより簡単かつ安全に操作できるようにすることです。幸いなことに、Android Studio にはこうした作業に役立つツールがあります。
SleepNightAdapterのonBindViewHolder()で、変数itemを宣言するステートメント以外のすべてを選択します。- 右クリックして、[Refactor] > [Extract] > [Function] を選択します。
- 関数に
bindという名前を付け、推奨されるパラメータを受け入れます。[OK] をクリックします。bind()関数はonBindViewHolder()の下に配置されます。
private fun bind(holder: ViewHolder, item: SleepNight) {
val res = holder.itemView.context.resources
holder.sleepLength.text = convertDurationToFormatted(item.startTimeMilli, item.endTimeMilli, res)
holder.quality.text = convertNumericQualityToString(item.sleepQuality, res)
holder.qualityImage.setImageResource(when (item.sleepQuality) {
0 -> R.drawable.ic_sleep_0
1 -> R.drawable.ic_sleep_1
2 -> R.drawable.ic_sleep_2
3 -> R.drawable.ic_sleep_3
4 -> R.drawable.ic_sleep_4
5 -> R.drawable.ic_sleep_5
else -> R.drawable.ic_sleep_active
})
}bind()のholderパラメータのholderという単語にカーソルを合わせます。Alt+Enter(Mac の場合はOption+Enter)キーを押して、インテンション メニューを開きます。[Convert parameter to receiver] を選択して、次のシグネチャを持つ拡張関数に変換します。
private fun ViewHolder.bind(item: SleepNight) {...}bind()関数を切り取ってViewHolderに貼り付けます。bind()を公開しました。- 必要に応じて、
bind()をアダプターにインポートします。 ViewHolderになったため、署名のViewHolder部分を削除できます。ViewHolderクラスのbind()関数の最終的なコードは次のとおりです。
fun bind(item: SleepNight) {
val res = itemView.context.resources
sleepLength.text = convertDurationToFormatted(
item.startTimeMilli, item.endTimeMilli, res)
quality.text = convertNumericQualityToString(
item.sleepQuality, res)
qualityImage.setImageResource(when (item.sleepQuality) {
0 -> R.drawable.ic_sleep_0
1 -> R.drawable.ic_sleep_1
2 -> R.drawable.ic_sleep_2
3 -> R.drawable.ic_sleep_3
4 -> R.drawable.ic_sleep_4
5 -> R.drawable.ic_sleep_5
else -> R.drawable.ic_sleep_active
})
}ステップ 2: onCreateViewHolder をリファクタリングする
アダプタの onCreateViewHolder() メソッドは、現在 ViewHolder のレイアウト リソースからビューをインフレートしています。ただし、インフレーションはアダプターとは関係なく、ViewHolder と関係があります。インフレーションは ViewHolder で発生します。
onCreateViewHolder()で、関数の本体にあるすべてのコードを選択します。- 右クリックして、[Refactor] > [Extract] > [Function] を選択します。
- 関数に
fromという名前を付け、推奨されるパラメータを受け入れます。[OK] をクリックします。 - 関数名
fromにカーソルを置きます。Alt+Enter(Mac の場合はOption+Enter)キーを押して、インテンション メニューを開きます。 - [コンパニオン オブジェクトに移動] を選択します。
from()関数は、ViewHolderインスタンスではなくViewHolderクラスで呼び出せるように、コンパニオン オブジェクトに存在する必要があります。 companionオブジェクトをViewHolderクラスに移動します。from()を公開しました。onCreateViewHolder()で、returnステートメントを変更して、ViewHolderクラスのfrom()の呼び出し結果を返すようにします。
完成したonCreateViewHolder()メソッドとfrom()メソッドは次のようになります。コードはエラーなくビルドして実行できるはずです。
override fun onCreateViewHolder(parent: ViewGroup, viewType:
Int): ViewHolder {
return ViewHolder.from(parent)
}companion object {
fun from(parent: ViewGroup): ViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val view = layoutInflater
.inflate(R.layout.list_item_sleep_night, parent, false)
return ViewHolder(view)
}
}- コンストラクタが非公開になるように、
ViewHolderクラスのシグネチャを変更します。from()が新しいViewHolderインスタンスを返すメソッドになったため、ViewHolderのコンストラクタを呼び出す理由がなくなりました。
class ViewHolder private constructor(itemView: View) : RecyclerView.ViewHolder(itemView){- アプリを実行します。アプリは以前と同じようにビルドして実行されるはずです。これは、リファクタリング後の望ましい結果です。
Android Studio プロジェクト: RecyclerViewFundamentals
- データのリストやグリッドを表示することは、Android の UI タスクとしては非常に一般的なものです。
RecyclerViewは、非常に大きなリストを表示する場合でも効率的に動作するように設計されています。 RecyclerViewは、現時点で画面に表示されるアイテムの処理や描画に必要な作業のみを行います。- アイテムが画面外にスクロールされると、そのビューはリサイクルされます。具体的には、画面内にスクロールしてくる新たなコンテンツがその項目に設定されます。
- ソフトウェア エンジニアリングのアダプター パターンは、オブジェクトが別の API と連携するのに役立ちます。
RecyclerViewは、アダプタを使用して、アプリのデータの保存方法や処理方法を変更することなく、アプリのデータを表示可能な形式に変換します。
RecyclerView にデータを表示するには、次の部分が必要です。
- RecyclerView
RecyclerViewのインスタンスを作成するには、レイアウト ファイルで<RecyclerView>要素を定義します。 - LayoutManager
RecyclerViewはLayoutManagerを使用して、RecyclerView内のアイテムのレイアウトを整理します(グリッドや線形リストでのレイアウトなど)。
レイアウト ファイルの<RecyclerView>で、app:layoutManager属性をレイアウト マネージャー(LinearLayoutManagerやGridLayoutManagerなど)に設定します。RecyclerViewのLayoutManagerをプログラムで設定することもできます。(この手法については、後の Codelab で説明します)。 - 各アイテムのレイアウト
XML レイアウト ファイルで 1 つのデータアイテムのレイアウトを作成します。 - Adapter
データを準備し、ViewHolderでの表示方法を定義するアダプタを作成します。アダプタをRecyclerViewに関連付けます。RecyclerViewが実行されると、アダプタを使用して、データを画面にどのように表示するかを決定します。
アダプタでは、次のメソッドを実装する必要があります。
– アイテムの数を返すgetItemCount()。
– リスト内のアイテムのViewHolderを返すonCreateViewHolder()。
– リスト内のアイテムのビューにデータを適応させるonBindViewHolder()。 - ViewHolder
ViewHolderには、アイテムのレイアウトから 1 つのアイテムを表示するためのビュー情報が含まれています。 - アダプターの
onBindViewHolder()メソッドは、データをビューに適合させます。このメソッドは常にオーバーライドします。通常、onBindViewHolder()はアイテムのレイアウトを拡張し、レイアウト内のビューにデータを配置します。 RecyclerViewはデータについて何も知らないため、Adapterはデータが変更されたときにRecyclerViewに通知する必要があります。notifyDataSetChanged()を使用して、データが変更されたことをAdapterに通知します。
Udacity コース:
Android デベロッパー ドキュメント:
このセクションでは、インストラクター主導のコースの一環として、この Codelab に取り組んでいる生徒向けに考えられる宿題をいくつか示します。インストラクターは、以下のようなことを行えます。
- 必要に応じて宿題を与える
- 宿題の提出方法を生徒に伝える
- 宿題を採点する
インストラクターは、これらの提案を必要なだけ使用し、必要に応じて他の宿題も自由に与えることができます。
この Codelab に独力で取り組む場合は、これらの宿題を自由に使用して知識をテストしてください。
以下の質問に回答してください
問題 1
RecyclerView はアイテムをどのように表示しますか。該当するものをすべて選択してください。
▢ リストまたはグリッドにアイテムを表示する。
▢ 縦または横にスクロールする。
▢ タブレットなどの大きなデバイスでは斜めにスクロールする。
▢ リストやグリッドで不十分な場合はカスタム レイアウトを使用できる。
問題 2
RecyclerView を使用するメリットは何ですか。該当するものをすべて選択してください。
▢ 大きなリストを効率よく表示する。
▢ データを自動的に更新する。
▢ アイテムの更新、削除、またはリストへの追加の際に更新する必要性を最小限にする。
▢ 画面外にスクロールされるビューを再利用して、画面内にスクロールされる次のアイテムを表示します。
問題 3
アダプターを使用するメリットは何ですか。該当するものをすべて選択してください。
▢ 「関心の分離」によって、コードの変更とテストを容易にする。
▢ RecyclerView は表示されているデータに依存しません。
▢ データ処理レイヤをデータの表示方法から分離できる。
▢ アプリの動作が高速になる。
問題 4
ViewHolder について、正しいものは次のうちどれですか。該当するものをすべて選択してください。
▢ ViewHolder レイアウトは XML レイアウト ファイルで定義されます。
▢ データセット内のデータのユニットごとに ViewHolder が 1 つあります。
▢ RecyclerView 内の ViewHolder は複数あっても構いません。
▢ Adapter はデータを ViewHolder にバインドします。
次のレッスンに進む: