RecyclerViewで簡単なリストを作る場合の登場人物
- Adapter
- ViewHolder
- リストの要素に表示する情報のクラス
- 一つ一つのリストの要素の雛形を生成する
- 生成されたリストの要素にテキスト等の表示したい内容を追加して順番に表示していく
ミニマム実装してみよう
RecyclerView自体が複雑なため、ミニマム実装でも結構コード量が多くなってしまいました🙉
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.nemo.githubrepositories.databinding.FragmentMainBinding
import com.nemo.githubrepositories.databinding.MyCellBinding
class MainFragment : Fragment(R.layout.fragment_main) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val binding: FragmentMainBinding = FragmentMainBinding.bind(view)
// 注: 実際のコードではStringをベタ書きすることはなく、string.resourceというところに切り出します。
// (これをハードコーディングの回避と言います。今回の話題と関係ないので説明は省きますが気になる方は調べてみてください。)
val itemList: List<Item> = listOf(
Item("マリオ"), Item("ルイージ"), Item("ヨッシー"), Item("クッパ")
)
val adapter = MyAdapter()
adapter.submitList(itemList)
binding.root.layoutManager = LinearLayoutManager(requireContext())
binding.root.adapter = adapter
}
private data class Item(
val text: String
)
// ①
private class MyAdapter : ListAdapter<Item, MyViewHolder>(MyDiffUtil()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder = MyViewHolder(
binding = MyCellBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val item: Item = currentList[position]
holder.bind(item)
}
}
// ②
private class MyViewHolder(
private val binding: MyCellBinding
) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: Item) {
binding.myText.text = item.text
}
}
// ③
private class MyDiffUtil : DiffUtil.ItemCallback<Item>() {
override fun areItemsTheSame(oldItem: Item, newItem: Item): Boolean = oldItem == newItem
override fun areContentsTheSame(oldItem: Item, newItem: Item): Boolean = oldItem == newItem
}
}
fragment_main.xml (MainFragmentのレイアウトファイル)
<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto" />
my_cell.xml (リストに表示する要素一つに対するレイアウトファイル)
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto">
<TextView
android:id="@+id/my_text"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="h,10:2"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:drawableStartCompat="@drawable/ic_launcher_foreground"
app:drawableTint="@color/black"
android:text="@string/hello_world"
android:gravity="start|center_vertical"
style="@style/TextAppearance.AppCompat.Large"/>
</androidx.constraintlayout.widget.ConstraintLayout>
上のミニマム実装において登場人物の対応は、
- Adapter -> MyAdapter
- ViewHolder -> MyViewHolder
- リストの要素に表示する情報のクラス -> Item
のようになっています🙌
Adapterではいろんな関数がoverrideされているのでぱっと見すごいややこしいですね笑
Adapterの内部では、
- セルの雛形を生成する
- 生成された雛形に情報を追加していく
ということをしています。
例えるなら、RecyclerView工場内部のベルトコンベアで流れてくる製品の一つ一つに
色付けしていく部分という感じになります。
RecyclerViewの内部では、表示するセルの個数分だけ
onCreateViewHolder -> onBindViewHolder
という順番で関数がよばれていきます。
onCreateViewHolderが1の部分
onBindViewHolderが2の部分
を担当しているという形になります。
そのためonCreateViewHolderが呼ばれていてまだonBindViewHolderが呼ばれていない
タイミングでは
のようになっていてonBindViewHolderが呼ばれ
binding.myText.text = item.text
の部分が実行されると
というふうにテキストが表示され、要素が一つ完成するというイメージになっています。
後編でまだ解説していない部分を扱って、リストの表示が完成となります🎉🎉
この記事を書いた人