65
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

Organization

KotlinでRecyclerViewを使ったリスト表示を行う

はじめに

1年ぶりのAndroid開発復帰を機に、Kotlinを勉強中です。
登場する機会が多いであろう、RecyclerViewを使ったリスト表示で感覚を取り戻そうと試みました。

Screenshot_20180110-222533.png

(2020/01/08追記)
Support LibraryからAndoridXへ移行する必要があるため、過去のソースを残しつつ、変更点を追記していこうと思います。

作成手順

CardViewとRecyclerViewを使う準備

build.gradle
dependencies {
    // 以下を追記してsync。(バージョンは適宜合わせてください)
    compile 'com.android.support:recyclerview-v7:26.1.0'
    compile 'com.android.support:cardview-v7:26.1.0'
}

(2020/01/08追記)

build.gradle
dependencies {
    // compileではなくimplementation書く必要があります
    implementation 'androidx.recyclerview:recyclerview:1.1.0'
    implementation 'androidx.cardview:cardview:1.0.0'
}

リストアイテム用のレイアウトを作成する

上に載せているスクショの通り、画像とテキストのみのシンプル構成。(dp直接指定なのは許して^^;)

list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.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" />

        <TextView
            android:id="@+id/itemTextView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

    </LinearLayout>
</android.support.v7.widget.CardView>

(2020/01/08追記)
android.support.v7.widget.CardViewから、androidx.cardview.widget.CardViewに変更する必要があります。

ちなみに、タップした時の波紋みたいなアニメーションを、以下の1行で付けています。

android:foreground="?android:attr/selectableItemBackground"

ViewHolderを作成

RecyclerViewにはOnItemClickListenerがset出来ないので、自分で作ります。

RecyclerViewHolder.kt
class RecyclerViewHolder(view: View) : RecyclerView.ViewHolder(view) {

    // 独自に作成したListener
    interface ItemClickListener {
        fun onItemClick(view: View, position: Int)
    }

    val itemTextView: TextView = view.findViewById(R.id.itemTextView)
    val itemImageView: ImageView = view.findViewById(R.id.itemImageView)

    init {
        // layoutの初期設定するときはココ
    }

}

Adapterを作成

ViewのonClick時に、タップされたViewがRecyclerViewの何番目にあたるかを取得して返します。

RecyclerAdapter.kt
class RecyclerAdapter(private val context: Context, private val itemClickListener: RecyclerViewHolder.ItemClickListener, private val itemList:List<String>) : RecyclerView.Adapter<RecyclerViewHolder>() {

    private var mRecyclerView : RecyclerView? = null

    override fun onAttachedToRecyclerView(recyclerView: RecyclerView?) {
        super.onAttachedToRecyclerView(recyclerView)
        mRecyclerView = recyclerView
    }

    override fun onDetachedFromRecyclerView(recyclerView: RecyclerView?) {
        super.onDetachedFromRecyclerView(recyclerView)
        mRecyclerView = null

    }

    override fun onBindViewHolder(holder: RecyclerViewHolder?, position: Int) {
        holder?.let {
            it.itemTextView.text = itemList.get(position)
            it.itemImageView.setImageResource(R.mipmap.ic_launcher)
        }
    }

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

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

        val layoutInflater = LayoutInflater.from(context)
        val mView = layoutInflater.inflate(R.layout.list_item, parent, false)

        mView.setOnClickListener { view ->
            mRecyclerView?.let {
                itemClickListener.onItemClick(view, it.getChildAdapterPosition(view))
            }
        }

        return RecyclerViewHolder(mView)
    }

}

Kotlinでも、SwiftのOptional型をif letで扱うような書き方ができるのはいいですね。

表示してみる

activity_main.xml
<?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="match_parent"
    tools:context="jp.todate.kotlinrecyclerviewsample.MainActivity">

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

</android.support.constraint.ConstraintLayout>

(2020/01/08追記)
android.support.v7.widget.RecyclerViewから、androidx.recyclerview.widget.RecyclerViewに変更する必要があります。

Android Studioでプロジェクトを立ち上げると、自動でKotlin Android Extensionsのプラグインが使える設定になる模様。
layout.xmlの方でViewに付けたIDが、そのまま変数として使用できるのは便利ですね!
(昔の感覚でfindViewByIdを書いてしまっていた自分、恥ずかしい...)

MainActivity.kt
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity(), RecyclerViewHolder.ItemClickListener {

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

        val hoges = resources.getStringArray(R.array.hoges).toMutableList()

        mainRecyclerView.adapter = RecyclerAdapter(this, this, hoges)
        mainRecyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)

    }

    override fun onItemClick(view: View, position: Int) {
        Toast.makeText(applicationContext, "position $position was tapped", Toast.LENGTH_SHORT).show()
    }
}

(2020/01/08追記)
AdapterとManagerを設定する方法が変わりました。

MainActivity.kt
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity(), RecyclerViewHolder.ItemClickListener {

    private lateinit var recyclerView: RecyclerView
    private lateinit var viewAdapter: RecyclerView.Adapter<*>
    private lateinit var viewManager: RecyclerView.LayoutManager

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

        val hoges = resources.getStringArray(R.array.hoges).toMutableList()

        viewAdapter = RecyclerAdapter(this, this, hoges)
        viewManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)

        recyclerView = findViewById<RecyclerView>(R.id.mainRecyclerView).apply {
            // use this setting to improve performance if you know that changes
            // in content do not change the layout size of the RecyclerView
            setHasFixedSize(true)

            // use a linear layout manager
            layoutManager = viewManager

            // specify an viewAdapter (see also next example)
            adapter = viewAdapter
        }
    }

    override fun onItemClick(view: View, position: Int) {
        Toast.makeText(applicationContext, "position $position was tapped", Toast.LENGTH_SHORT).show()
    }
}

おわりに

GitHubに上げました。(過去版のみ、修正版は追ってアップします)
https://github.com/Todate/KotlinRecyclerViewSample

どうやらAdapter/ViewHolderの方でも、Kotlin Android Extensionsの恩恵を受けられるらしいんですが、時間の関係で断念。後日、機会があればチャレンジしたいと思います。

Register as a new user and use Qiita more conveniently

  1. You can follow users and tags
  2. you can stock useful information
  3. You can make editorial suggestions for articles
What you can do with signing up
65
Help us understand the problem. What are the problem?