【Android】画面コンポーネント / RecyclerView ~ 入門編 ~

■ はじめに

https://dk521123.hatenablog.com/entry/2013/10/10/005010
https://dk521123.hatenablog.com/entry/2018/09/02/130212

で、ListView を扱ったが、Android Studio の Palette をみると
Legacy(レガシー)の中にあったので、古いのかなーっと。
(ほかにも、次に勉強しようと思っていた「GridView」も)

で、 RecyclerView ってのがあるらしいので、勉強してみる

目次

【1】特徴
【2】準備
【3】作成手順・概要
【4】サンプル

【1】特徴

https://developer.android.com/guide/topics/ui/layout/recyclerview?hl=ja

* ListView を拡張して柔軟性をもたせた改良版

【2】準備

以下 CardView と RecyclerView を追加し、
Android Studioの[Build]-[Rebuild Project]を選択し、ビルドする

build.gradle (Module:app)

dependencies {
    implementation 'androidx.recyclerview:recyclerview:1.1.0'
    implementation 'androidx.cardview:cardview:1.0.0'
}

【3】作成手順・概要

1)リストの各要素のレイアウトを作成
2)Adapter / ViewHolderを作成
3)RecyclerView を画面に張る
4)画面の実装

【4】サンプル

* 簡単なリスト表示と
 クリックイベントのハンドリング処理まで、実装する

1)リストの各要素のレイアウトを作成

* [app]-[res]-[layout] を右クリックし、[New]-[Layout Resource File]を選択し、
 レイアウトファイルを追加する(今回は「main_list_item.xml」)

main_list_item.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="8dp"
    card_view:cardElevation="2dp"
    android:foreground="?android:attr/selectableItemBackground">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal"
        android:gravity="center_vertical">

        <ImageView
            android:id="@+id/itemImageView"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:layout_margin="8dp" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:orientation="vertical">

            <TextView
                android:id="@+id/titleTextView"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical"
                android:layout_marginLeft="5dip"
                android:textSize="18sp"
                android:text="Title"/>

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="horizontal">
                <TextView
                    android:id="@+id/subtitleTextView"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginLeft="7dip"
                    android:textSize="12sp"
                    android:text="Subtitle"/>
            </LinearLayout>
        </LinearLayout>
    </LinearLayout>
</androidx.cardview.widget.CardView>

2)Adapter / ViewHolderを作成

SampleRecyclerViewAdapter.kt

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.main_list_item.view.*

class SampleRecyclerViewAdapter(private val targetList: Array<Pair<String, String>>) :
    RecyclerView.Adapter<SampleRecyclerViewAdapter.SampleViewHolder>() {

    lateinit var listener: OnItemClickListener

    class SampleViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
        val image = view.itemImageView
        val title = view.titleTextView
        val subtitle = view.subtitleTextView
    }

    interface OnItemClickListener{
        fun onItemClickListener(
            view: View,
            position: Int,
            targetItem: Pair<String, String>)
    }
    fun setOnItemClickListener(listener: OnItemClickListener){
        this.listener = listener
    }

    override fun onCreateViewHolder(
        parent: ViewGroup,
        viewType: Int
    ): SampleRecyclerViewAdapter.SampleViewHolder {
        val layoutInflater = LayoutInflater.from(parent.context)
        val item = layoutInflater.inflate(R.layout.main_list_item, parent, false)
        return SampleViewHolder(item)
    }

    override fun getItemCount(): Int {
        return targetList.size
    }

    override fun onBindViewHolder(
        holder: SampleRecyclerViewAdapter.SampleViewHolder,
        position: Int
    ) {
        holder.view.itemImageView.setImageResource(
            R.mipmap.ic_launcher_round
        )
        holder.view.titleTextView.text = targetList[position].first
        holder.view.subtitleTextView.text = targetList[position].second
        // For Click event
        holder.view.setOnClickListener {
            listener.onItemClickListener(it, position, targetList[position])
        }
    }
}

3)RecyclerView を画面に張る

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

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/mainRecyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="vertical"
        tools:layout_editor_absoluteY="2dp" />
</androidx.constraintlayout.widget.ConstraintLayout>

補足:縦スクロールができなかった問題について

* RecyclerView において、縦スクロールができなかった問題が発生したが
 以下のサイトを参考に修正したら、スクロールできるようになった。

https://freakycoder.com/android-notes-51-how-to-enable-scrollbar-in-recyclerview-4ef553f8be40

4)画面の実装

MainActivity.kt

import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

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

        val list = arrayOf(
            Pair("streamline", "(仕事を)合理化する"),
            Pair("attribute", "…に原因があると考える"),
            Pair("mandatory", "義務的な"),
            Pair("apprenticeship", "見習い期間"),
            Pair("applause", "拍手/賞賛"),
            Pair("overwhelming", "圧倒的な"))
        val adapter = SampleRecyclerViewAdapter(list)
        val layoutManager = LinearLayoutManager(this)
        sampleRecyclerView.layoutManager = layoutManager
        sampleRecyclerView.adapter = adapter
        sampleRecyclerView.setHasFixedSize(true)

        adapter.setOnItemClickListener(
            object:SampleRecyclerViewAdapter.OnItemClickListener{
            override fun onItemClickListener(
                view: View,
                position: Int,
                targerItem: Pair<String, String>) {
                Toast.makeText(
                    applicationContext,
                    "Clicked - ${targerItem.first} ${targerItem.second}",
                    Toast.LENGTH_LONG).show()
            }
        })
    }
}

参考文献

https://qiita.com/Todate/items/297bc3e4d0f3d2477ed3
https://qiita.com/naoi/items/f8004f906db16d6b38da
https://qiita.com/kkt_yu/items/baacbee06a8868c08c94

関連記事

レイアウト ~ 入門編 ~
https://dk521123.hatenablog.com/entry/2015/08/23/165632
画面コンポーネント / RecyclerView ~ スワイプして項目削除 ~
https://dk521123.hatenablog.com/entry/2020/08/21/000000
画面コンポーネント / ListView ~ 入門編 ~
https://dk521123.hatenablog.com/entry/2013/10/10/005010
画面コンポーネント / ListView ~ Realm データを表示 ~
https://dk521123.hatenablog.com/entry/2018/09/02/130212
Kotlin / Realm で英単語帳を作る
https://dk521123.hatenablog.com/entry/2020/07/20/232009