RecyclerViewをはじめて見た人

駅のホーム降りて、エスカレーターを降りるたとき、ふとRecyclerViewを実装したくなりました。

偶然にもAndroid Studioもインストールしてあったため、やってみることにしました。
どのサイトを見てもアホな私にはわかりにくかったため、これ見れば誰でもできる(少なくとも私は)というようなまとめをかいてみます。用語もわからないので感覚です。
間違いがあったらお教えください。

  • やりたいこと

1.Gmailみたいなリストを作る
 2.スワイプして削除
 3.ぐりぐり動かして並び替え

  • まず、作成したxmlビュー一覧です。
名前 役割
one_item 1行分のデータのレイアウトを決める
activity_main ここにRecyclerViewを入れる

 

  • まず、実装したクラスをすべてまとめました。MainActivityは除いてます。
クラス名(引数型) スーパークラス 役割
OneItem なし 1行分のデータの中身を決める
ViewHolder(View) RecyclerView.ViewHolder xmlで決めた1行分のデータViewを受け取って、ついでにその中の情報をもらう
RecyclerViewAdapter(MutableList) RecyclerView.Adapter 上で1行1行ちまちまつくってたのをまとめて、RecyclerView本体にセット!

クラスごとのコードです。必要最小限の実装にしてあるので、これをサンプルにして他の機能を追加してください。自分用のメモでもあるので、省略せず全部書きます。

1.one_item.xml・・・・・・textViewだけ実装しておきます。

<?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:orientation="vertical"
                                                   android:layout_width="match_parent"
                                                   android:layout_height="120dp">
    <TextView
            android:layout_width="100dp"
            android:layout_height="wrap_content" android:id="@+id/textview"
            app:layout_constraintBottom_toBottomOf="parent" android:layout_marginTop="8dp"
            app:layout_constraintTop_toTopOf="parent" app:layout_constraintEnd_toEndOf="parent"
            android:layout_marginEnd="8dp" app:layout_constraintStart_toStartOf="parent"
            android:layout_marginStart="8dp"/>
</androidx.constraintlayout.widget.ConstraintLayout>

2. activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        tools:showIn="@layout/activity_main"
        tools:context=".MainActivity">
    <androidx.recyclerview.widget.RecyclerView
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintHorizontal_bias="0.1"
            android:id="@+id/recyclerView"/>
</androidx.constraintlayout.widget.ConstraintLayout>


3.ViewHolderクラス

package com.(プロジェクト名).recyclertest

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

class ViewHolder(itemView : View): RecyclerView.ViewHolder(itemView){
        var text : TextView? = null

        init{   //コンストラクタ
            text = itemView.textview
        }
}


4. OneItemクラス

package com.(プロジェクト名).recyclertest

class OneItem() {
    var text:String = ""
}

5.CustomRecyclerViewAdapterクラス
勝手にオーバーライドしてきたメソッドたちです。こういうのが必要なんでしょうね。
・ミュータブルリストを受け取ります。MainActivityで作成しているのですが、ここに1つ1つの行のデータが入っているので、責任重大です。

メソッド名 役割
onCreateViewHolder ViewHolderを作成します。
getItemCount リストの中身の数を返します。結局使わなかったけど、全何件!とかを出すときには使えると思います。
onBindViewHolder リストの要素1つ1つとViewHolderを結び付けます。
package com.(プロジェクト名).recyclertest

import android.content.Intent
import android.graphics.Color
import android.text.format.DateFormat
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView

class CustomRecyclerViewAdapter(list : MutableList<OneItem>):RecyclerView.Adapter<ViewHolder>() {
    private val list:MutableList<OneItem> = list

    override fun onCreateViewHolder(parent: ViewGroup, position: Int): ViewHolder {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.one_item,parent,false)
        val viewholder = ViewHolder(view)   //ViewHolderを生成、それを返す
        return viewholder
    }

    override fun getItemCount(): Int {
        //サイズ
        return list.size
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val item = list[position]
        holder.text?.text = item.text
    }
}

6.MainActivityクラス

package com.(プロジェクト名).recyclertest

import android.media.MediaRouter
import android.os.Bundle
import com.google.android.material.snackbar.Snackbar
import androidx.appcompat.app.AppCompatActivity;
import android.view.Menu
import android.view.MenuItem
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView

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

class MainActivity : AppCompatActivity() {

    private lateinit var adapter:CustomRecyclerViewAdapter
    private lateinit var layoutManager: RecyclerView.LayoutManager
    private lateinit var itemDecoration: DividerItemDecoration

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

    override fun onStart() {
        super.onStart()

        itemDecoration = DividerItemDecoration(this,DividerItemDecoration.VERTICAL)
        layoutManager = LinearLayoutManager(this)
        recyclerView.layoutManager=layoutManager
        adapter = CustomRecyclerViewAdapter(makeRecyclerData())
        recyclerView.adapter = this.adapter
        recyclerView.addItemDecoration(itemDecoration)
    val itemTouchHelper = ItemTouchHelper(object : ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP or ItemTouchHelper.DOWN, ItemTouchHelper.LEFT) {

            override fun onMove(
                recyclerView: RecyclerView,
                viewHolder: RecyclerView.ViewHolder,
                target: RecyclerView.ViewHolder
            ): Boolean {
                val from = viewHolder.adapterPosition?:0
                val to = target.adapterPosition?:0
                recyclerView.adapter?.notifyItemMoved(from,to)
                return true
            }

            override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
               viewHolder.let{
                   recyclerView.adapter?.notifyItemRemoved(viewHolder.adapterPosition)
               }
            }})
        itemTouchHelper.attachToRecyclerView(recyclerView)
    }

    fun makeRecyclerData():MutableList<OneItem>{
       var recyclerData :MutableList<OneItem> = mutableListOf()

        for(i in 0..50){
            var oneitem:OneItem= OneItem()
            oneitem.text="アイテム"+i.toString()
            recyclerData.add(oneitem)
        }

        return recyclerData
    }
}

注意点1.RecyclerViewには区切り線をxmlで実装する方法がないようです。
そのため、MainActivityで

itemDecoration = DividerItemDecoration(this,DividerItemDecoration.VERTICAL)

注意点2.スワイプして削除、ドラッグして入れ替えしているのはこの部分。

val itemTouchHelper = ItemTouchHelper(object : ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP or ItemTouchHelper.DOWN, ItemTouchHelper.LEFT) {

            override fun onMove(
                recyclerView: RecyclerView,
                viewHolder: RecyclerView.ViewHolder,
                target: RecyclerView.ViewHolder
            ): Boolean {
                val from = viewHolder.adapterPosition?:0
                val to = target.adapterPosition?:0
                recyclerView.adapter?.notifyItemMoved(from,to)
                return true
            }

            override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
               viewHolder.let{
                   recyclerView.adapter?.notifyItemRemoved(viewHolder.adapterPosition)
               }
            }})
        itemTouchHelper.attachToRecyclerView(recyclerView)
    }

その他解説は少しずつ追加していきたいと思います。
醜いコードだとは思いますが・・・その辺ご指摘お願いいたします。