プログラミング

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)
}

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