Help us understand the problem. What is going on with this article?

【kotlin】RecyclerViewの簡単な使い方【初心者向け】

More than 1 year has passed since last update.

みなさんRecyclerView使ってますか?
私はまだちゃんと使ったことがありません。

RecyclerViewはListViewの代替じゃないとかonClickListenerがないから面倒とか難しそうな印象の記事が多かったのですが、試しに使ってみたら基本的な使い方はそんなに難しくなかったので忘れないように記します。
ちゃんと整理しきれてないのでちょっと煩雑です。気をつけてください。

環境

PC:MacBookPro
OS:MacOSSierra
AndroidStudio:3.1.1
kotlin_version:1.2.30
gradle:3.1.1
buildToolsVersion:27.0.3

参考

RecyclerViewの基本
【Android】RecyclerViewのクリックイベント

特にRecyclerViewの基本は図もあってめちゃくちゃわかりやすいのでオススメです。
(ありがとうございます。)

できるもの

適当なテキストをリスト表示してタップすると画面下部に適当なメッセージを出すアプリ。のリスト部分
image.png

手順

1.リストの一つのアイテムを表すレイアウトとクラスを作る

list_item.xml

アイテム一つを表すレイアウト。とりあえず固定のアイコンとタイトル、詳細をテキストで並べます。
特別なことはないですが強いていうなら一番親のレイアウトの高さをmatch_parentではなくwrap_contentや任意の高さにするように気をつけましょう。


<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">


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

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:contentDescription="@string/icon_desc"
            android:src="@mipmap/ic_launcher" />

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

            <TextView
                android:id="@+id/row_title"
                android:layout_width="wrap_content"
                android:layout_height="0dp"
                android:layout_weight="0.5"
                tools:text="titleです" />

            <TextView
                android:id="@+id/row_detail"
                android:layout_width="wrap_content"
                android:layout_height="0dp"
                android:layout_weight="0.5"
                tools:text="詳細ですよ詳細ですよ詳細ですよ詳細ですよ詳細ですよ" />

        </LinearLayout>

    </LinearLayout>


</android.support.constraint.ConstraintLayout>

RowModel.kt

リストのアイテム一つを表すクラス。Modelとつけたけどdataとかの方がいい可能性もある。
今回はタイトルと詳細をStringで持たせます。
アイテムと同じ数だけ存在することになるはず。
特に注意はない。

package your.package.name

class RowModel {
    var title: String = ""
    var detail: String = ""
}

2.ViewHolderを作る

1クラスだけです

ViewHolder.kt

アイテムのViewを保持します。
アイテムと対になって存在するイメージ。(実際はリサイクルされているようなので同じ数ではない?)
よく考えると名前そのまんまですね。

今回はタイトルと詳細のViewを生成して保持してます。
ここ本当にあってるか自信ないです。
あとで調べます…
追記:ViewHolderの目的がfindViewByIdの結果をキャッシュすることでコストを減らすことのようなのでこれでよさそう。
値とデータをバインドをするメソッドをここに書いても良さそう。

package your.package.name

import android.support.v7.widget.RecyclerView
import android.view.View

class HomeViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
    val titleView: TextView = itemView.findViewById(R.id.row_title)
    val detailView: TextView = itemView.findViewById(R.id.row_detail)
}

3.ViewAdapterを作る

これも一ファイルです。

ViewAdapter.kt

ViewHolderを生成したりに生成したViewHolderにViewModelをセットしたりする。
一つのListに対して一つ存在する。
今回はクリックのリスナーも一緒にセットしています。
ここが難しそうに見えてたんですが実際書いてみると全然難しくないです。

コンストラクタにリスト表示するRowModelのリストとタップを検出するためのリスナーを渡されるようにします。
(リスナーはこのクラスの下部にinterfaceを定義してあります)

onCreateViewHolder()でlist_itemのviewを作ってそれを元に先ほどのViewHolderを生成しreturnします。

onBindViewHolder()ではpositionをListのindexとして、ViewHolderに値をセットします。
よく考えたらここでViewHolderにRowModelを渡してあげてもいいかもしれません。
また、今回はここでクリックリスナーもセットしています。リスナーについては公式の例をまだ見つけられてないので何が正しいのかあんまわかっていませんがとりあえずこれが楽だと思いました。

getItemCount()ではlistのサイズを返してあげる必要があるようです。

package your.package.name

import android.support.v7.widget.RecyclerView
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import your.package.name.R


class ViewAdapter(private val list: List<RowModel>, private val listener: ListListener) : RecyclerView.Adapter<ViewHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        Log.d("Adapter", "onCreateViewHolder")
        val rowView: View = LayoutInflater.from(parent.context).inflate(R.layout.list_item, parent, false)
        return ViewHolder(rowView)
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        Log.d("Adapter", "onBindViewHolder")
        holder.titleView.text = list[position].title
        holder.detailView.text = list[position].detail
        holder.itemView.setOnClickListener {
            listener.onClickRow(it, list[position])
        }
    }

    override fun getItemCount(): Int {
        Log.d("Adapter", "getItemCount")
        return list.size
    }

    interface ListListener {
        fun onClickRow(tappedView: View, rowModel: RowModel)
    }
}

4.レイアウトにRecyclerViewを追加

main_activity.xml

に以下のようなRecyclerViewを追加します。(自分のmain_activityが散らかっていたので部分だけですいません)

 <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

5.activityやfragmentで使う

activityならOnCreate、fragmentならonViewCreatedとかで呼びます(fragmentの方、そこで呼ぶのが正しいかはちょっとまだわかってないです)
今回はfragmentで呼んだのでonViewCreatedだけ抜粋。
this@myFragmentにしてあるのはmyFragmentのonClickRow()をリスナーのそれと同じ名前で宣言したからです。
別の名前にしても良かったんですが初心者が引っかかりがちなのであえて残してあります。

あとはKotlin Android Extensionsを使っているのでfindViewByIdがないです。めちゃくちゃ便利。好き。
みなさんも使いましょう。

    override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        Log.d("lifeCycle", "onViewCreated")

        val recyclerView = recycler_list
        val adapter = ViewAdapter(createDataList(), object : ViewAdapter.ListListener {
            override fun onClickRow(tappedView: View, rowModel: RowModel) {
                this@myFragment.onClickRow(tappedView, rowModel)
            }
        })

        recyclerView.setHasFixedSize(true)
        recyclerView.layoutManager = LinearLayoutManager(activity)
        recyclerView.adapter = adapter
    }

  private fun createDataList(): List<RowModel> {

        val dataList = mutableListOf<RowModel>()
        for (i in 0..49) {
            val data: RowModel = RowModel().also {
                it.title = "タイトル" + i + "だよ"
                it.detail = "詳細" + i + "個目だよ"
            }
            dataList.add(data)
        }
        return dataList
    }


   fun onClickRow(tappedView: View, rowModel: RowModel) {
        Snackbar.make(tappedView, "Replace with your own action tapped ${rowModel.title}", Snackbar.LENGTH_LONG)
                .setAction("Action", null).show()
    }

以上!

ということで、順を追って作っていけば基本的な使い方はそんなに難しくないよって話でした。
断片的になってしまったのでそのうちサンプルプロジェクト作って上げられたらいいなあと思っております。
あと追加と削除もやりたい。
今思いましたがログとか入っててすいません。あとで消しときます。

ではまた。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした