Recycler View (1) 全体像とKotlinでの最単純実装

2019年12月28日土曜日

RecyclerView

t f B! P L

概要

Recycler Viewはリスト表示の基本パターン。
ここでは、RecyclerViewの型を理解したことをまとめる。
ついで、Fragment + ViewModelで最単純なアプリを作成する。ViewModelはここでは使はない。

RecyclerViewの全体像

 RecyclerViewの型の全体像は下図のとほり。クラス名やファイル名は、後に使ふプログラムに合はせてゐる。

レイアウト

 図中右側の破線の枠を入れ物として、図中左側の一行ぶんのレイアウトを 順にはめ込んでゆく。この仕組みと、その入れ物が RecyclerView。

LayoutManager

 破線の入れ物に、各行をどのようにはめ込んで行くかを決めるのが LayoutManager。通常は、それを継承する LinearLayoutManagerをもちゐる場合が多い。中身を一列または一行に並べる。生成するときに、VERTCALかHORIZONTALを指定する。

ViewHolder 

 一行ごとのレイアウトに表示するデータを保持する。

Adapter

 3つのコールバック関数を用意して、システムからの求めに応じて動作する。コールバック関数は次のとほり。


onCreateViewHolder 一行ぶんのレイアウトを RecyclerViewに送り込むことと、その一行に対応する ViewHolderの生成をおこなふ。
getItemCount 表示するデータ全数の個数を返す
onBindViewHolder 引数で受け取つた表示番目とそのViewHolderを用ひて、一行分の表示のViewを作る。


 

仕様

 以上の理解のもと、次の仕様のRecyclerViewサンプルアプリケーションを作る。

  • MainFragment のレイアウトとしてmain_fragment.xmlを持ち、その中に RecyclerViewを持つ。
  • 縦にスクロールする50行の文字列とする。
  • 各行は2つの文字列を TextViewとして持つ。
  • 表示する文字列は"AAA"、"BBB"の固定文字列に行番号を附したものとする。

環境

 Android Studio: 3.5.3
 Kotlin: ver 1.3

プロジェクト生成

プロジェクト生成

以下の通常手順でプロジェクトを生成する。
  1. Fragment + ViewModel テンプレート選択 → Next 
  2. Configure your projectで以下を設定
    • Name: "RecyclerViewTemplate" 
    • Language: Kotlin
    • Use androidx.* artifacts にチェック
  3. Finish でおわり

以下の5ファイルができていることを確認する。
  • man_fragment.xml
  • MainFragment.kt
  • MainViewModel.kt
  • main_activity.xml
  • MainActivity.kt

ビルドして動作確認。

ここではViewModelは使はぬので、MainViewModel.ktは一切編集せぬ。

ファイル一覧

最終的に作成するファイルの一覧は以下の通り。


ファイル名 説明
each_row.xml 一行分のレイアウト。2つのTextViewを横に並べたものとする。
main_fragment.xml MainFragmentが表示するレイアウト。LinearLayoutとし、その中に、RecyclerView 部品を配置する。
EachRowViewHolder.kt ViewHolder。一行分のViewを保持する。RecyclerVIew.ViewHolderを継承する。
CustomAdapter.kt Adapter。所定の最低限の三つの関数を overrideする。その中で、各行のレイアウトをRecyclerVIewに吹込み(inflate)、ViewHolderの実体を生成し、各行に表示するデータをViewHolderが持つ各行の中のViewにくべる。
MainActivity.kt 自動生成される。編集しない。Fragmentの入れ物として機能する。

以降、それぞれのファイルを上記の順番で作る。

一行分のレイアウトの作成 - each_row.xml

ファイル生成

一行ぶんのレイアウトはLinearLayout (horizontal)とし、その中に TextViewを2つ入れることにする。Designエディタで次の手順で作成する。
  1. app → res → layout → 右クリック New → Layout resource file を選択
  2. New Resource Fileダイアログで以下を設定
    • File name: each_row 
    • Root element: LinearLayout
  3. OK

ファイル編集

 一行に2つの TextViewを並べるレイアウトにする。


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1" />

    <TextView
        android:id="@+id/textView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1" />
</LinearLayout>


  • 3行目:包含する部品を横に並べるので horizontal を指定。
  • 5行目:heightを"match_parent"から"wrap_content"に変へる。かうせぬと、一行が画面縦一杯に広がる。
  • 8行目、14行目:idを textView1, textView2 とする。
  • 11行目、17行目:横に並べた部品を均等に配置するのでそれぞれ同じ数字を設定。

RecyclerView の配置 - main_fragment.xml 

ファイル編集

  1. はじめからある TextView "MainFragment"を削除
  2. Paletteから Containers → RecyclerView を選択し 貼り付ける。
  3. IDは "recyclerView" とする
その後、手作業で以下の編集を行ふ。


<?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:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ui.main.MainFragment">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginStart="1dp"
        android:layout_marginTop="1dp"
        android:layout_marginEnd="1dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>


  • 11行目:idは recyclerVIew とする

ViewHolder クラスの作成 - EachRowViewHolder.kt

ファイル 生成 

一行ぶんのレイアウトとデータを保持するクラスを作る。
  1. appjavacom... → 右クリック → NewKotlin FIle/Class 選択
  2. New Resource Fileダイアログで以下を設定
    • File name: EachRowViewHolder
    • Kind: Class
  3. OK

ファイル編集

生成した  EachRowVIewHolder.ktを以下のやうにする。


import android.view.View
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.each_row.view.*

class EachRowViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    val rowView1 = itemView.textView1
    val rowView2 = itemView.textView2
}


  • 5行目:RecyclerVIew.ViewHolderを継承する。パラメターの itemViewは、一行分のレイアウトの each_row.xml。
  • 6行目、7行目: 文字列のViewへの参照を保持する変数を用意。

Adapter クラスの作成 - MainFragment.kt

ファイル生成

Adapterクラスを定義する
  1. app java com... → 右クリック → NewKotlin FIle/Class 選択
  2. New Resource Fileダイアログで以下を設定
    • File name: CustomAdapter
    • Kind: Class
  3. OK

ファイル編集

コードは以下のやうになる。3つのコールバック関数を定義する。 Classのコンストラクタは、ここでは単純を旨とし設定しない。



class CustomAdapter(): RecyclerView.Adapter() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EachRowViewHolder {

        val layoutInflater = LayoutInflater.from(parent.context)
        val view = layoutInflater.inflate(R.layout.each_row, parent, false)

        val viewHolder =EachRowViewHolder(view)

        return viewHolder
    }

    override fun getItemCount(): Int {
        return 50
    }

    override fun onBindViewHolder(holder: EachRowViewHolder, position: Int) {
        holder.rowView1.text = "AAA" + position.toString()
        holder.rowView2.text = "BBB" + position.toString()
    }
}
  • 1行目:RecyclerView.Adapterの派生としてゐる
  • 3行目〜8行目: コールバック関数の onCreateViewHolderの定義。
  • 10行目〜12行目: コールバック関数の getItemCountの定義。固定値50としてゐる。
  • 14行目〜16行目:コールバック関数の onBindViewHolder の定義。引数で渡された positionの番号と固定文字列を、引数で渡された ViewHolderの中の変数 rowView (each_row.xmlで定義された textInRow)の中の rowViewに格納してゐる。

MainFragment - MainFragment.kt

最後に、MainFragmentを編集する。

  • RecyclerViewの送り込み(inflate)、
  • Adapterの実体の生成、
  • LayoutManagerの実体の生成、
  • それら実体を RecyclerViewに格納する。
最終的なコードは次のやうになる。 16行目〜21行目が変更箇所。

class MainFragment : Fragment() {

    companion object {
        fun newInstance() = MainFragment()
    }

    private lateinit var viewModel: MainViewModel

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        val view = inflater.inflate(R.layout.main_fragment, container, false)


        val adapter = CustomAdapter()

        val recyclerView = view.findViewById(R.id.recyclerView)

        recyclerView.adapter = adapter
        recyclerView.layoutManager = LinearLayoutManager(context,LinearLayoutManager.VERTICAL, false)

        return view
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
        // TODO: Use the ViewModel
    }

ビルドと動作確認

 ビルドして動作確認する。
(了)

アーカイブ