【はじめてのKotlinプログラミング(34)】RecyclerView(前編:リストを表示してみる)

kotlinでRecyclerViewを使って、簡単なリストを表示させたり、タップ、いわゆるクリックしたときのクリック処理について解説したいと思います。今回は1行だけのレイアウトを作って、それをリストで表示させてみたいと思います。

で、今回は表示だけ。

次回は、これをクリックしたときにトーストで表示させる、というクリック処理について解説します。以前ListViewを使ってリスト表示について解説しましたが、RecyclerViewっていうのは、一言でいうと、ListViewの進化版だと思っていただいて結構です。

ListViewっていうのは、このように、シンプルな縦方向のリストしか表示できないわけですがRecyclerViewっていうのは

縦方向にもできますし、カード上にもできますし、パネル状にならべたり、ということもできてだいぶ表現の自由度が上がります。

それから名前のとおりViewをリサイクルすることで、めちゃめちゃ大量のリストを作るときにも向いています。

動画

目次

タイトル 再生時間
[1]activity_main.xml 03:15~
[2]1行だけのレイアウト(xml) 10:40~
[3]ViewHolder(kt) 14:55~
[4]Adapter(kt)準備 19:00~
 -Adapterの1つ目 23:55~
[5]Adapterの2つ目 27:50~
[6]Adapterの3つ目 30:50~
[7][8]表示 37:00~
[おまけ]区切り線 42:10~

コード

▼activity_main.xml

<?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="match_parent"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:layout_editor_absoluteX="1dp"
        tools:layout_editor_absoluteY="1dp" />

</androidx.constraintlayout.widget.ConstraintLayout>

▼one_layout.xml

<?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">

    <TextView
        android:id="@+id/tv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginTop="16dp"
        android:text="TextView"
        android:textSize="24sp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <View
        android:id="@+id/divider"
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="?android:attr/listDivider"
        app:layout_constraintBottom_toBottomOf="@+id/tv" />
</androidx.constraintlayout.widget.ConstraintLayout>

▼ViewHolderItem.kt

package com.example.simplerecyclerview

import android.view.View
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView

class ViewHolderItem(itemView:View) :RecyclerView.ViewHolder(itemView){
    //View(xml)の方から、指定のidを見つけてくる(ここではtv)
    val itemName:TextView =itemView.findViewById(R.id.tv)
}

▼RecyclerAdapter.kt

package com.example.simplerecyclerview

import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView

class RecyclerAdapter :RecyclerView.Adapter<ViewHolderItem>(){
    //5)表示するリストを用意
    private val animalList = listOf(
        "ライオン","クマ","キリン","ゾウ","パンダ","コアラ","キリン","サル","ヒョウ","ウサギ",
        "ゴリラ","カバ","カピバラ","リス","ワニ","イルカ","ヒツジ","ネコ","ラッコ","カメ","クジラ"
    )

    //4)ここで1行分のレイアウト(View)を生成
    //(「2」と「3」を紐づける作業)
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolderItem {
        //「2」のレイアウトを取得(インフレート)
        val itemXml = LayoutInflater.from(parent.context)
            .inflate(R.layout.one_layout,parent,false)
        return ViewHolderItem(itemXml)
    }

    //5)position番目のデータをレイアウト(xml)に表示するようセット
    override fun onBindViewHolder(holder: ViewHolderItem, position: Int) {
        holder.itemName.text = animalList[position]

    }

    //6)データが何件あるかをカウントする
//    override fun getItemCount(): Int {
//        return animalList.size
//    }
    override fun getItemCount(): Int = animalList.size

}

▼MainActivity.kt

package com.example.simplerecyclerview

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView

class MainActivity : AppCompatActivity() {
    //7)recyclerViewの変数を用意
    private lateinit var recyclerView:RecyclerView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        //8)recyclerView表示の呪文
        recyclerView =findViewById(R.id.rv) //idの取得
        recyclerView.adapter = RecyclerAdapter() //アダプターをセット
        recyclerView.layoutManager = LinearLayoutManager(this) //各アイテムを縦に並べてください(見せ方の指示)

    }
}

テキスト

それでははじめていきましょう。AndroidStudioを起動してください。

[1]activity_main.xml

まずは、メインxmlに今回の主役RecyclerViewを設置しましょう。

詳細はコードご参照

[2]1行だけのレイアウト(xml)

それでは、1行だけのレイアウトを作っていきましょう。
ListViewの解説の時にはですね、
SimpleListItem1とか、2とか、あらかじめ用意されていたわけですが
今回はそれを自作する、という感じになります。

レイアウトはres リソースの中のlayoutの中に保存されていますので
このlayoutを右クリック⇒new⇒layout resourse fileというのがありますので
これを選択してください。

名前は適当に
one_layoutとかにしてあげて、OK。
これで、layoutファイルの中に、xmlが1つできました。

(詳細はコードご参照)

[3]ViewHolder(kt)

それではViewHolderのkotlinクラスを作っていきましょう

————————————–
xmlはlayoutの方で作りましたがktの方は、MainActivityと同じならびにしたいので
1つ上のパッケージを右クリック⇒new⇒kotlin classで
名前を適当に「ViewHolderItem」
とかにしてあげて、OK。

これで新しく、ktクラスが新しくできました。
————————————–

今はクラスに名前だけついていますが
この人が「RecyclerViewのViewHolder」っていう一族であることを明示してあげなければいけません。
その一族に属する、というのをプログラミング的には、「継承」と言いますが
この人が、RecyclerViewのViewHolderの一族を継承していますよ、
っていうのを書く書き方は、フラグメントのところでも軽く説明しましたが
名前の隣に : としてあげて、それに続けて、お父さんお母さんの名前を書いていきます。
今回でいうとRecyclerViewのViewHolderの一族を継承していきたいので

RecyclerView.ViewHolder

これでViewHolderListは、RecyclerView.ViewHolderの一族ですよっていう血のつながりができました。
親子関係ができました。
なのでこの波カッコの中でViewHolder一族の呪文が使えるようになると、こういう訳です。

————————————–
それから、ViewHolderっていうのは、引数にViewを付けてあげる必要があります。
なので名前の隣に丸カッコとしてあげて適当に名前を付けてあげましょう。
ま、わかりやすくitemView。とか。
で型は、View
ViewHolderList(itemView:View)

で、今つくったこの名前を、RecyclerView.ViewHolderの方にも渡してあげてください。
丸カッコでitemView
RecyclerView.ViewHolder(itemView)
つまり、ここ(子)と、ここ(親)は、同じ引数を共有させておいてください。

———————————–
これで準備ができましたので、
波カッコの中を書いていきます。
波カッコの中は、先ほど作ったViewの、idを取得するだけです。
viewのidというのは、one_layoutで作ったtvこれですよ。これを取得します。

//View(xml)の方から、指定のidを見つけてくる(ここではtv)

valとしてあげて、変数の名前を、まあ、そのままtvと付けてもいいんですが
アイテムの名前ということでitemName。とかにしてあげて、型はTextView
val itemName : TextView =

で、findViewByIdといきたいところですが、今回、findViewByIdが出てきてくれません。
今回、RecyclerView.ViewHolderを継承しているので、
この中はRecyclerView.ViewHolder一族の呪文に従う必要があります。

で、どうするかってことで、
今つけた、これ(itemView)もとをたどれば
これ(itemView:View) ですが。
これがViewだって話をしました。

なのでViewから見つけてくる、ということで
itemViewとしてあげると、xmlのidを取得できるようになります。
rvっていうのはメインxmlのRecyclerViewのことですし
tvっていうのはtem_recycler xmlのTextViewのidですよね。
で今回はこっちの方をHoldしたいのでtvを選択

val itemName : TextView =itemView.findViewById(R.id.tv)

これでですね、
xmlの1行分のレイアウトを保持できました。

今③ができましたので、あとは、これらを、アダプターというものにセットしていきます。アダプターはktなので、もう1つ、kotlinクラスを作っていきましょう。

[4]Adapter(kt)準備

それでは、1行だけのレイアウトのxmlと、
それを保持するktが出来たので
それらをアダプターに差し込む、という作業をしていきましょう。

アダプターはListViewのところでも出てきたので
名前についてはさほど抵抗はないと思います

が、RecyclerViewの場合は、Adapter専用のkotlinクラスを作って、
その中に書いていきます。

で、なんやかんやの部品があるわけですが
それらをなんやかんやで最終的に3つくらいにまとめる、という作業をしていきますので
なんとなくこの図を頭に入れながらはじめて行きましょう。。

—————————————–
まずはkotlinクラスを作るので、パッケージを右クリック⇒new⇒kotlin class
名前を適当に、RecyclerAdapter とかにしてあげましょう。

そうすると、また新しくktのクラスが出来ました。
さっきはRecyclerViewのViewHolderを継承したわけですが
今回はRecyclerViewのAdapterを継承させていきます。

名前につづけて : 
:RecyclerView.Adapter

引数として、ViewHolderの型が必要なので<>としてあげて
先ほど作ったクラスの名前をそのまま入れてあげましょう。
<ViewHolderList>
で、この続きで特に何も指定しない場合は()で、
一旦閉めてあげます。

で、クラス名のところに何やら波線が出ています。
マウスを合わせると、赤い電球が出てくると思いますので
▼マークをクリック。

imprement members

これ、以前カウントダウンタイマーのところでも似たようなことをしたと思うんですけど
imprementっていうのは埋め込むって言う意味で、
埋め込むメンバーがありますよっていう意味になります。
指示に従って進んでいくと
3つ出てきます。
これが先ほど説明の、なんやかんやで3つにまとめますってやつなのでので全部選択して、OKボタン。

これで、必要なひな型が全て入って、赤い波線は消えました。

******************************************************
これら3つについて軽く説明しておきます。

1つ目のonCreateViewHolder。
これは
//4)ここで1行分のレイアウト(View)を生成
//(「2」と「3」を紐づける作業)

この図でいうと、これと、これをアダプターにセットして個別の部品を1個にまとめるという作業ですね。
これは1行だけの表示。

で、それを受けて、2つ目は、何番目のやつを表示させますか、っていう指示になります。
//5)position番目のデータをレイアウト(xml)に表示するようセット

当然、1番目のテキストと2番目のテキストは違うものを表示させなければいけません。
その何番目かっていうのをpositionっていうのを使って指定します。

最後、3つ目は、それらのデータが一体何件あるかを把握します。
//6)データが何件あるかをカウントする

という三部構成になっています。

それからこの「todoなんちゃらかんちゃら」っていうのは、
不要なので全部削っておいてください。

[4]続き-Adapterの1つ目

それでは上から1つずつやっていきましょう。

それではまず最初は1行分のレイアウトをアダプター内で生成する作業。
波カッコの中に

//「2」のレイアウトを取得(インフレート)

まずは②で作ったレイアウトを変数に流し込んでいきます。
ではvalとしてあげて、適当に名前を itemXml とかにしておきましょう。
val itemXml =

でインフレートのところは以前、フラグメントのところでもサラっと出てきましたが
ほぼ完全に決まり文句なので、このとおり書いてください。

LayoutInflater.from(parent.context)
.inflate()
で、この中に、流し込むxmlを指定します。今回でいうとone_layoutですね
R.layout.one_layout

それから、この続きは、何も考えずに、このまま書いてください。
(R.layout.one_layout,parent,false)

これは決まり文句なので丸暗記は禁物です。
検索すればみなさん、同じひな型を書いていますので、ひな型に従ってください。
こういうのを一言一句覚えようとすると挫折します。
ひな形を見ながら、自分の必要なところだけ書き換えられるような力を付けるようにしてください。

val itemXml =LayoutInflater.from(parent.context)
.inflate(R.layout.item_recycler_list,parent,false)
—————
で、この次ですが。
まあひな形通りなんで、あまり細かい解説は省略してもいいんですけど
一応右上のワーニングをクリックしてみましょう。

そうするとですね。
14行目。ここですね。returnしなさいよと書いてあります。

で、何をリターンするかというと、この波カッコ閉じるの、最初がココなので、
この型、つまり今回でいうとViewHolderItem を返すということなので、
return と書いて、
ViewHolderItemとかいて丸カッコの中に今つくったitemXmlを入れてあげる。
return ViewHolderItem(itemXml)

これでインフレートした後に、
⇒ViewHolderにセット
つまり「2」と「3」を紐づけた、ということになります。

繰り返しますが、今は解説上、1つ1つ説明しながらやっていますけど
これは一言一句覚えないようにしてください。

検索するとみなさん同じことを書いていますので、丸暗記は禁物です。
なんとなく意味だけ理解しておいて、自分の必要なところだけ書き換えられるようにしておいてください。

 

これで1つめが完了です。

//「2」のレイアウトを取得(インフレート)⇒ViewHolderにセット
val itemXml =LayoutInflater.from(parent.context)
.inflate(R.layout.one_layout,parent,false)
return ViewHolderItem(itemXml)

[5]Adapterの2つ目

つづいて2つ目。
先ほどは1行だけのレイアウトをセットしたわけですが
実際、何番目にはこれを表示する、というデータをセットしていきます。

で、今回表示させるデータも、この中で用意しますので。
4番の上にですね。
この波カッコの下に、同じく5番でリストを用意していきましょう。

//5)表示するリストを用意
private val animalList =listOf(
“ライオン”,”クマ”,”キリン”,”ゾウ”,”パンダ”,”コアラ”,”キリン”,”サル”,”ヒョウ”,”ウサギ”,
“ゴリラ”,”カバ”,”カピバラ”,”リス”,”ワニ”,”イルカ”,”ヒツジ”,”ネコ”,”ラッコ”,”カメ”,”クジラ”
)

ちなみにarrayOfの配列でも
今回はただ表示するだけなので別にどっちでも動くと思います。

————————————————-
それではこのanimalListの0行目にはライオン
1行目にはクマ、3行目、4行目、pojition行目には●●、という風なセッティングをしていきます。

 

ViewHolderItemの変数にはholderという名前が割り当てられていますので
これを使います。
holderの中の(隣のページを見ながら)itemNameということで
holder.itemName.text =

アニマルリストの
animalList[]

何番目かというのが、この position で用意されていますのでposition番目をセット
holder.itemName.text =animalList[position]

これでpositionが0のところには、ライオンが表示されますし
positionが1のところには、クマが表示されますし
positionが10のところには、0123456789・・・10、ゴリラが表示されるようにセットされました。position番目のリストをitemName.textに表示させなさい、という設計図ができました

[6]Adapterの3つ目

それでは最後、3つ目ですが、ここには何件データがあるかをカウントするための関数を書いておきます。

で、ここもですね、波カッコ閉じるのところに
赤いにょろにょろが出ていますが
右上のワーニングをクリックするとですね
returnで返しなさいよと、いうことでヒントをくれています。

なのでひとまずreturn  と書いて。
何を返すかというと、ここはリストの件数が何件かっていう話をしました。
で、今回のデータというのはこのanimalListのことなので
animalListと書いて「.」って入れると「size」って出ますのでこれを選択。

これでリストの中に5件あれば、5という数字が返されますし、
100件あれば、100という数字が返されます。
この「size」という呪文を使うことで、このリストの中の部品の数を調べてくれる、というものになっています。


で、過去の動画をご覧の方はですね、以前キャッチコピー自動生成アプリを作った時には
listにつづけてカウント丸カッコっていうのを紹介しました。
animalList.count()

実はこれも同じ意味で、リストの数を3件なら3,100件なら100と、カウントしてくれます。
なので、こっちでも同じように動くとは思いますが、RecyclerViewの場合は
ほぼみなさん.sizeを使います。

なんでかって言うと、理由はものすごく単純で。
このテンプレート、ひな形を一番最初に作った人が
ここを .size にしたので、次に使う人も、それにならって .size を使うことが一般的になったと
こういうわけです。

で、これ、実はプログラミングの世界ではよくあることで
同じ書き方なんだけど、最初に書いた人がこう書いたんなら、
それにならってこっちを使う、みたいなことはよくします。

なので、一言一句覚えるのは無駄ですよっていうのは、結局どんな人でもひな形を見ながら書いているんで
覚えなくてもいいですよっていう意味です。

てことで、1,2、3.3つ埋まりました。

———————————————
で、これで完成なんですが。
リターンについての豆知識を1つ。

リターンっていうのは、この値を、波カッコの一番最初に返す、という話をしました。
波カッコ閉じるの、一番最初。
今回でいうと
getItemCount() ここに返すということです。今回の場合は型はInt型ですってことですが。
返すというのは、別の言い方をすると、この値をここに代入するっていう言い方もできます。

てことはですよ。
returnに至るまでに、たくさん処理がある場合は基本この書き方でいいんですけども。

今回のようにreturnが1個しかない場合は、普通にイコールで代入するという風に省略することも可能です。

これらを一回コピーして下に複製して、上をコメントで消します。
複数行を一気にコメントにするときにはctrlを押しながらスラッシュでいけるので覚えておくとよいでしょう。
macの場合はcommand + スラッシュ、だったかな。

return list.count() もいらないので消します。
で、このように、波カッコの中が1個しかない場合は、波カッコを消して
return も消して、イコールで この値を代入すると。

これでこの値をリターンで、ここに返す、と同じ意味ですよね。
この値を代入する。型はリストの個数、つまり数字なのでInt型。

という書き方もあるので、豆知識として覚えておいてください。

******************************
以上で、いろんな要素を1か所にまとめる、という
アダプターの作業が完成です。

それでは最後の仕上げ、メインktに、表示させるコードを書いていきましょう。

[7][8]表示

それでは1行分のレイアウトと、それを保持するクラス。
そして、それら部品をアダプターにもろもろセットできましたので

最後の仕上げ、メインktに、
表示の呪文を書いていきましょう。

メインktを開いてください。

でまずは、onCreate波カッコの上に、あるいはクラスの下、という言い方もできますが。
RecyclerViewを扱うことのできる変数を用意します

//7)recyclerViewの変数を用意

private としてあげて、中身はonCreateの中で書くので
ここは名前だけの宣言になります。
中身は後からっていうのはlateinitでしたね。
で、lateinit は決まりがあって val ではなくって var 。
変数名を、まあそのままですけど recyclerView とかにしてあげて、型は:RecyclerView
private lateinit var recyclerView:RecyclerView

これでRecyclerViewがインポートされたので、この人はrecyclerViewとして扱えるようになります。
こっちの変数名の方は、今勝手につけた名前なんで、最初の文字を小文字にしていますが、
こっちの型の方は本家の方なのでちゃんと大文字にしないと使えませんので大文字小文字、注意してください。
変数名の方は、なんならみなさんご自由に好きな名前を付けていただいてOKです。

それでは今、名前(recyclerView)だけ付けたのでこれの中身をonCreateの中で書いていきましょう
******************************************
onCreate波カッコの中に、表示の呪文を書いていきます

で、まずやることの1つ目はご存知、xmlのidの取得ですね。
(xmlを見ながら)
表示させるメインxmlの、RecyclerViewのidが今回rvなので、これを取得しましょう。

recyclerView = findViewById(R.id.rv) //idの取得

******************************************
次にアダプターをセットします。
recyclerViewのadapterに、
recyclerView.adapter =

先ほど作ったアダプタークラスなので、これですね。RecyclerAdapterなので
recyclerView.adapter = RecyclerAdapter()//アダプターをセット

(PS)
今、これをやった感じですね。このクラスのコンセントを、こっちに差し込んだみたいな。

******************************************

で、最後にlayoutManagerっていうのを使って、アイテムの並べ方を決めてあげます。
具体的には
タテに並べていく場合には、LinearLayoutManager
パネルみたいにな表示にしたい場合にはGridLayoutManagerみたいなのが用意されているので
そういうのを使っていきます。
今回は、普通に、いわゆるスタンダードなタテに並べるようにしたいので
LinearLayoutManagerを使用します。

recyclerView.layoutManager =

としてあげて、LinearLayoutManagerを選択。
()の中は、この画面というか、自分自身ということで this 

recyclerView.layoutManager = LinearLayoutManager(this) //各アイテムを縦に並べてください(見せ方の指示)

これで、完成です。
xmlからidを取得して、
そこにアダプターをセット。
各アイテムを縦に並べてください、というそれぞれの命令を入れました。

ではエミュレータを起動して、リストが縦に配置されているかどうか確認してみましょう。

******************************************

はい、こうですね。
ちゃんと用意したリストが縦並びに配置されているのがわかります。

スクロールもちゃんといけているようです。

で、今回の動画はこれで終了なんですが。

1つだけ小さい補足で。

アイテムごとに区切り線、横線が引かれてないですよね。

最後に、アイテムごとに区切り線を引いてあげて、完成としましょう。

【参考】
RecyclerView in Android Studio [Kotlin 2020]
android Kotlin RecyclerViewの使い方

    コメントを残す