リサイクラービュー(RecyclerView)
参考1: RecyclerViewでのリスト作成
参考2: 研修14日目(CoordinatorLayout)
CoordinatorLayout
でスクロール連動させるために必要なNestedScrollingChild
インタフェースを実装したListView
。
リサイクラービュー
が保持するレイアウトマネージャ
を通じてリスト
のレイアウト
を決定し、
アダプタクラス
によって生成されるビューホルダ
を通じて、アイテム内ビュー
にリストデータ
が埋め込まれる。
RecyclerViewとListView
参考: 研修2日目
リスト
形式のビュー
を提供するRecyclerView
とListView
の違いは、以下の通り。
ビュー | スクロール連動 | レイアウトの柔軟性 | アダプタクラス | 区切り線 | リスナインタフェース |
---|---|---|---|---|---|
RecyclerView |
o | o | x | x | o |
ListView |
x | x | o | o | o |
レイアウトマネージャ
リサイクラービュー
のレイアウト配置
とアイテム管理
を行うクラス。
標準でLinearLayoutManager
・GridLayoutManager
・StaggeredGridLayoutManager
の3つのレイアウトマネージャ
が用意されている。
また、RecyclerView.LayoutManager
を実装することで、独自定義したレイアウトマネージャ
を作成することもできる。
LinearLayoutManager
アイテム
を縦のリスト
に並べるレイアウトマネージャ
。
GridLayoutManager
アイテム
を格子状
に並べるレイアウトマネージャ
。
StaggeredGridLayoutManager
アイテム
をスタッガード格子状
に並べるレイアウトマネージャ
。
アダプタ
参考: 研修2日目(Adapter)
アクティビティクラス
で記述したリストデータ
をビューホルダ
が管理するビュー
に紐づける(=バインド
)、
RecyclerView.Adapter
クラスの実装クラス
。
ListView
とは異なり、リサイクラービュー
にはアダプタクラス
が用意されていないため、
RecyclerView.Adapter
クラスを実装したアダプタクラス
を独自に定義する必要がある。
RecyclerView.Adapter
リサイクラービュー
のアダプタ
を定義する抽象クラス
。
アイテム内ビュー
を管理するビューホルダ
を生成するメソッド(=onCreateViewHolder()
)、
ビューホルダ
が管理するビュー
にリストデータ
をバインド
するメソッド(=onBindViewHolder()
)、
また、リサイクラービュー
が表示するリストデータ
の件数を取得するメソッド(=getItemCount()
)を抽象的に定義する。
ビューホルダ
リストデータ
を表示するリストアイテム
内のビュー
を管理する、RecyclerView.ViewHolder
クラスの実装クラス。
RecyclerView.ViewHolder
リストデータ
を表示するリストアイテム
内のビュー
を管理する抽象クラス
。
リサイクラービューの実装
リサイクラービュー
を実装する手順は、以下の通り。
リストデータ
を定義・返却するメソッドを記述- 「
リストデータ
を表示するビュー
」を定義するビューホルダクラス
の作成リストデータ
をリサイクラービュー
に表示するアダプタクラス
の作成リサイクラービュー
にレイアウトマネージャ
とアダプタ
をセット
リストデータの定義
リサイクラービュー
に表示するリストデータ
を定義する。
サンプルコード
// RecyclerViewのItemのリスト定義・作成
private fun createSetMealList(): MutableList<MutableMap<String, Any>> {
// MutableList
val menuList: MutableList<MutableMap<String, Any>> = mutableListOf()
// --- リストデータの定義開始 ---
// MutableMapの定義
var menu = mutableMapOf<String, Any>(
"name" to "item 1",
"price" to 750,
"desc" to "This description is about item 1."
)
// MutableListに追加
menuList.add(menu)
// MutableMapの定義
menu = mutableMapOf<String, Any>(
"name" to "item 2",
"price" to 900,
"desc" to "This description is about item 2."
)
// MutableListに追加
menuList.add(menu)
... // 以降繰り返し
// --- リストデータの定義終了 ---
// 定義したMutableListを返却
return menuList
}
ビューホルダの作成
リストデータ
を表示するビュー
を定義する、RecyclerView.ViewHolder
の実装クラス
(=ビューホルダ
)を作成する。
ただし、RecyclerView.ViewHolder
のクラスコンストラクタ
は引数にView
オブジェクトをとるため、
ビューホルダ
のクラスコンストラクタ
も引数にView
オブジェクトをとるように定義する必要がある。
なお、ここでのView
オブジェクトとは、リサイクラービュー
のアイテムビュー
を指す。
定義
RecyclerView.ViewHolder(@NonNull itemView: View)
サンプルコード
// アイテム内ビュー(=TextView)を保持するビューホルダ
private inner class RecyclerListViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {
// Item内に表示される1つ目のTextView
var _tvMenuNameRow: TextView
// Item内に表示される2つ目のTextView
var _tvMenuPriceRow: TextView
// 初期化時に呼び出される処理(=イニシャライザ)
init {
// Item内のTextView
// -> アダプタクラスのonBindViewHolder()メソッドによってリストデータが反映される
_tvMenuNameRow = itemView.findViewById(R.id.tvMenuNameRow)
_tvMenuPriceRow = itemView.findViewById(R.id.tvMenuPriceRow)
}
}
アダプタクラスの作成
ビューホルダ
が保持するビュー
にリストデータ
を紐づける、RecyclerView.Adapter
の実装クラス
(=アダプタクラス
)を作成する。
アイテム内ビュー
を管理するビューホルダ
を生成するonCreateViewHolder()
メソッドでは、
ビューホルダ
のクラスコンストラクタ
の引数にアイテムビュー
が必要となるため、
リサイクラービュー
のレイアウトファイル
のインフレート
を行う必要がある。
インフレート(inflate)
参考: 研修4日目
レイアウトファイル
(=XML
ファイル)をJavaオブジェクト
のビュー
として実体化
すること。
LayoutInflater
クラスのfrom()
メソッドを用いてコンテキスト
からLayoutInflater
オブジェクトを取得し、
inflate()
メソッドを用いてインフレート
を行う。
LayoutInflater
UI部品
をインフレートするメソッドを定義するクラス。
定義
// LayoutInflaterオブジェクトの取得
LayoutInflater.from(context: Context!): LayoutInflater!
// パラメータ
// context: LayoutInflaterを利用するコンテキスト
// レイアウトファイルのインフレート
LayoutInflater.inflate(
resource: Int,
root: ViewGroup?
attachToRoot: Boolean
): View!
// パラメータ
// resource: インフレートするレイアウトファイル名のID(R値)
// root: レイアウトファイルの実体化先となるビューグループ
// attachToRoot: rootパラメータのビューグループを親レイアウト部品として設定するかどうか
定義
// RecyclerViewによって最初に呼び出される処理
@NonNull
RecyclerView.Adapter.onCreateViewHolder(
@NonNull parent: ViewGroup,
viewType: Int
): <ビューホルダ>
// パラメータ
// parent: ビューホルダが保持するビューの親ビューグループ(=アイテムビュー)
// viewType: アイテムビューのビュータイプを識別する整数値
// RecyclerViewがアダプタからビューホルダを受け取った際にRecyclerViewから呼び出される処理
RecyclerView.Adapter.onBindViewHolder(
@NonNull holder: <ビューホルダ>,
position: Int
): Unit
// パラメータ
// holder: ビューホルダオブジェクト
// position: リストデータがバインドされるアイテムのポジション番号(≒行番号)
// リストデータの項目数を取得
RecyclerView.Adapter.getItemCount(): Int
サンプルコード
// 独自定義するアダプタクラス
// <- RecyclerView.Adapterクラスの実装クラス
// <- クラスコンストラクタは一度のみ呼ばれ、クラス内でしか利用しない(=機密性を持たせる)ため、
// "private val"を付与
private inner class RecyclerListAdapter(
private val _listData: MutableList<MutableMap<String, Any>>
): RecyclerView.Adapter<RecyclerListViewHolder>() {
// RecyclerViewによって最初に呼び出される処理
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerListViewHolder {
// LayoutInflaterオブジェクト
val inflater = LayoutInflater.from(this@MainActivity)
// レイアウトファイルのインフレート(=アイテムビュー)
val view = inflater.inflate(R.layout.row, parent, false)
// ビューホルダの生成
val holder = RecyclerListViewHolder(view)
// ビューホルダを返却
return holder
}
// RecyclerViewがアダプタからビューホルダを受け取った際にRecyclerViewから呼び出される処理
override fun onBindViewHolder(holder: RecyclerListViewHolder, position: Int) {
// リストデータのアイテム
val item = _listData[position]
// 指定したキーと一致するリストデータ
val menuName = item["name"] as String
val menuPrice = item["price"] as Int
// Int型 → String型 への変換
val menuPriceStr = menuPrice.toString()
// ビューホルダが保持するビューにデータを反映
holder._tvMenuNameRow.text = menuName
holder._tvMenuPriceRow.text = menuPriceStr
}
// リストデータの項目数を取得
override fun getItemCount(): Int {
// リストデータの項目数を返却
// <- リストデータはクラスコンストラクタの引数から取得
return _listData.size
}
}
レイアウトマネージャ・アダプタのセット
参考: 研修2日目
レイアウトマネージャ
はRecyclerView
オブジェクトのlayoutManager
プロパティ、
アダプタ
はRecyclerView
オブジェクトのadapter
プロパティにセットする。
定義
// LinearLayoutManagerオブジェクトの生成
LinearLayoutManager(context: Context!)
// パラメータ
// context: LinearLayoutManagerを利用するコンテキスト
サンプルコード
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
...
// RecyclerView
val lvMenu = findViewById<RecyclerView>(R.id.lvMenu)
// レイアウト配置とリストアイテムを管理するLinearLayoutManager(=レイアウトマネージャ)
val layout = LinearLayoutManager(this@MainActivity)
// RecyclerViewにLinearLayoutManagerをセット
lvMenu.layoutManager = layout
// リストデータ
val menuList = createSetMealList()
// リストデータを紐づけるRecyclerListAdapter
val adapter = RecyclerListAdapter(menuList)
// RecyclerViewにRecyclerListAdapterをセット
lvMenu.adapter = adapter
}
...
}
LinearLayoutManagerのリサイクラービューへの区切り線の追加
レイアウトマネージャ
にLinearLayoutManager
を採用するリサイクラービュー
は、
リストアイテム
間の区切り線
(=Divider
)を標準で用意していないため、
区切り線
を表示したい場合は、DividerItemDecoration
オブジェクトを利用する必要がある。
DividerItemDecoration
LinearLayoutManager
を採用するリサイクルビュー
への区切り線
を追加する、
RecyclerView.ItemDecoration
クラスの実装クラス
。
RecyclerView.ItemDecoration
アイテムビュー
の装飾スタイル
を定義する抽象クラス
。
定義
// DividerItemDecorationオブジェクトの生成
DividerItemDecoration(context: Context!, orientation: Int)
// パラメータ
// context: DividerItemDecorationを利用するコンテキスト
// orientation: 区切り線の方向(=垂直または水平方向)を表すDividerItemDecorationクラス定数
// <- LinearLayoutManagerのorientationプロパティを利用するのが一般的
// リサイクラービューへの区切り線の追加
RecyclerView.addItemDecoration(
@NonNull decor: RecyclerView.ItemDecoration
): Unit
// パラメータ
// decor: アイテムビューに追加するItemDecorationオブジェクト
区切り線の方向を表すDividerItemDecorationクラス定数
定数名 | 区切り線の方向 |
---|---|
HORIZONTAL |
水平 |
VERTICAL |
垂直 |
サンプルコード
// RecyclerView
val lvMenu = findViewById<RecyclerView>(R.id.lvMenu)
// 区切り線を表示するDividerItemDecorationオブジェクト
val decorator = DividerItemDecoration(this@MainActivity, layout.orientation)
// リサイクラービューへの区切り線の追加
lvMenu.addItemDecoration(decorator)
リサイクラービューへのリスナ定義
リサイクラービュー
はListView
とは異なり、専用のリスナインタフェース
が用意されていないため、
View.OnClickListener
インタフェースを実装したリスナクラス
を独自定義する必要がある。
また、アイテムビュー
に対してリスナ
としてリスナクラスオブジェクト
をセットする必要があるため、
アダプタクラス
のonCreateViewHolder()
メソッド内でインフレート
したレイアウトファイル
(=アイテムビュー
)に対して、リスナ定義
を行う。
サンプルコード
class MainActivity : AppCompatActivity() {
// アダプタクラス
// <- RecyclerView.Adapterクラスの実装クラス
private inner class RecyclerListAdapter(
private val _listData: MutableList<MutableMap<String, Any>>
): RecyclerView.Adapter<RecyclerListViewHolder>() {
// RecyclerViewによって最初に呼び出される処理
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerListViewHolder {
...
// LayoutInflaterオブジェクト
val inflater = LayoutInflater.from(this@MainActivity)
// レイアウトファイルのインフレート(=アイテムビュー)
val view = inflater.inflate(R.layout.row, parent, false)
// インフレートしたアイテムビューをリスナとしてセット
view.setOnClickListener(ItemClickListener())
...
}
...
}
// リサイクラービューのアイテムの"タップ"イベントを検知するリスナクラス
// <- View.OnClickListenerインタフェースの実装クラス
private inner class ItemClickListener: View.OnClickListener {
// "タップ"イベント検知時に呼び出される処理(イベントハンドラ)
override fun onClick(view: View?) {
... // タップイベント検知時の処理
}
}
}