4
2

More than 3 years have passed since last update.

【Kotlin研修15日目】リサイクラービューの実装

Posted at

リサイクラービュー(RecyclerView)

参考1: RecyclerViewでのリスト作成
参考2: 研修14日目(CoordinatorLayout)
CoordinatorLayoutスクロール連動させるために必要なNestedScrollingChildインタフェースを実装したListView

リサイクラービューが保持するレイアウトマネージャを通じてリストレイアウトを決定し、
アダプタクラスによって生成されるビューホルダを通じて、アイテム内ビューリストデータが埋め込まれる。

リサイクラービュー.png

RecyclerViewとListView

参考: 研修2日目
リスト形式のビューを提供するRecyclerViewListViewの違いは、以下の通り。

ビュー スクロール連動 レイアウトの柔軟性 アダプタクラス 区切り線 リスナインタフェース
RecyclerView o o x x o
ListView x x o o o

レイアウトマネージャ

リサイクラービューレイアウト配置アイテム管理を行うクラス。
標準でLinearLayoutManagerGridLayoutManagerStaggeredGridLayoutManagerの3つのレイアウトマネージャが用意されている。

また、RecyclerView.LayoutManagerを実装することで、独自定義したレイアウトマネージャを作成することもできる。

LayoutManager.png

LinearLayoutManager

アイテム縦のリストに並べるレイアウトマネージャ

GridLayoutManager

アイテム格子状に並べるレイアウトマネージャ

StaggeredGridLayoutManager

アイテムスタッガード格子状に並べるレイアウトマネージャ

アダプタ

参考: 研修2日目(Adapter)
アクティビティクラスで記述したリストデータビューホルダが管理するビューに紐づける(=バインド)、
RecyclerView.Adapterクラスの実装クラス

ListViewとは異なり、リサイクラービューにはアダプタクラスが用意されていないため、
RecyclerView.Adapterクラスを実装したアダプタクラスを独自に定義する必要がある。

RecyclerView.Adapter

リサイクラービューアダプタを定義する抽象クラス

アイテム内ビューを管理するビューホルダを生成するメソッド(=onCreateViewHolder())、
ビューホルダが管理するビューリストデータバインドするメソッド(=onBindViewHolder())、
また、リサイクラービューが表示するリストデータの件数を取得するメソッド(=getItemCount())を抽象的に定義する。

ビューホルダ

リストデータを表示するリストアイテム内のビューを管理する、RecyclerView.ViewHolderクラスの実装クラス。

RecyclerView.ViewHolder

リストデータを表示するリストアイテム内のビューを管理する抽象クラス


リサイクラービューの実装

リサイクラービューを実装する手順は、以下の通り。

  1. リストデータを定義・返却するメソッドを記述
  2. リストデータを表示するビュー」を定義するビューホルダクラスの作成
  3. リストデータリサイクラービューに表示するアダプタクラスの作成
  4. リサイクラービューレイアウトマネージャアダプタをセット

リストデータの定義

リサイクラービューに表示するリストデータを定義する。

サンプルコード

MainActivity.kt
// 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)

サンプルコード

MainActivity.kt
// アイテム内ビュー(=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オブジェクトの取得
LayoutInflater.from(context: Context!): LayoutInflater!
// パラメータ
// context: LayoutInflaterを利用するコンテキスト

// レイアウトファイルのインフレート
LayoutInflater.inflate(
    resource: Int, 
    root: ViewGroup?
    attachToRoot: Boolean
): View!
// パラメータ
// resource: インフレートするレイアウトファイル名のID(R値)
// root: レイアウトファイルの実体化先となるビューグループ
// attachToRoot: rootパラメータのビューグループを親レイアウト部品として設定するかどうか

定義

RecyclerView.Adapterの抽象メソッド
// 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

サンプルコード

MainActivity.kt
// 独自定義するアダプタクラス
// <- 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を利用するコンテキスト

サンプルコード

MainActivity.kt
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 垂直

サンプルコード

MainActivity.kt
// RecyclerView
val lvMenu = findViewById<RecyclerView>(R.id.lvMenu)

// 区切り線を表示するDividerItemDecorationオブジェクト
val decorator = DividerItemDecoration(this@MainActivity, layout.orientation)

// リサイクラービューへの区切り線の追加
lvMenu.addItemDecoration(decorator)

リサイクラービューへのリスナ定義

リサイクラービューListViewとは異なり、専用のリスナインタフェースが用意されていないため、
View.OnClickListenerインタフェースを実装したリスナクラスを独自定義する必要がある。

また、アイテムビューに対してリスナとしてリスナクラスオブジェクトをセットする必要があるため、
アダプタクラスonCreateViewHolder()メソッド内でインフレートしたレイアウトファイル(=アイテムビュー)に対して、リスナ定義を行う。

サンプルコード

MainActivity.kt
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?) {
            ...   // タップイベント検知時の処理
        }
    }
}
4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2