RecyclerView は、ListView をさらに進化させて柔軟にしたものだそうです。
cellをドラッグ&ドロップさせたり追加・消去などに強みを発揮するようですが、ここでは簡単な基本設定を試してみます。
2021.2.1
RecyclerView
わかりやすい説明がGoogle Developerのリストとカードの作成にあります。最初にこのサンプルコードを元にして実際に動かしてみたいと思います。
Ref: 図 1 RecyclerView ウィジェット
基本的にはListViewの進化形なので、ListViewがわかっていると理解しやすいです。
RecyclerView にはLayoutManagerはアイテムの位置調整を行い、 ビューの再利用(Recycle)する機能をもっています。
ListViewでもconvertViewを駆使すればできていた事でしたが、あえて記述しなくても良くなったということでしょう。
シンプルなサンプルコード
最初にRecyclerViewのライブラリーをbuild.gradleに追加しますが、どこかのバージョンから無くてもいいようです
ただ、ItemTouchHelperを使う場合では必要です
AndroidX:androidx.recyclerview:recyclerview:x.x.x
このバージョンはビルドツールのバージョンと合っていないとエラーになることもありますので、それぞれの環境で調整してください。
使っている環境に合わせる必要があります
build.gradle
1 2 3 4 |
dependencies { ... implementation 'androidx.recyclerview:recyclerview:1.2.1' } |
以下、リストとカードの作成にあるサンプルコードを元に簡単なサンプルを作っててみます。
レイアウトでは、Recyckerview のタグを使います
1 2 3 |
<androidx.recyclerview.widget.RecyclerView ... /> |
RecyclerView オブジェクトのハンドルを取得してレイアウト マネージャーに接続し、アダプターにデータセットを渡します。
データセットとして20個の要素を持つ配列で設定してみます。
また、データセットも追加・削除・移動を前提にするためArrayListを使います。
MainActivity.kt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
//package your.package.name import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import java.util.* class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val recyclerView = findViewById<RecyclerView>(R.id.my_recycler_view) // use this setting to improve performance if you know that changes // in content do not change the layout size of the RecyclerView recyclerView.setHasFixedSize(true) // use a linear layout manager val rLayoutManager: RecyclerView.LayoutManager = LinearLayoutManager(this) recyclerView.layoutManager = rLayoutManager val dataSet: ArrayList<String> = arrayListOf() var i = 0 while (i < 20) { val str: String = java.lang.String.format(Locale.US, "Data_0%d", i) dataSet.add(str) i++ } recyclerView.adapter = MyAdapter(dataSet) } } |
Adapterですが、別ファイルとして作成しました。
MyAdapter.kt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
//package your.package.name import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.TextView import androidx.recyclerview.widget.RecyclerView class MyAdapter(private val dataSet: ArrayList<String>): RecyclerView.Adapter<MyAdapter.ViewHolder>(){ // Provide a reference to the type of views // that you are using (custom ViewHolder). class ViewHolder(view: View) : RecyclerView.ViewHolder(view) { val textView: TextView init { // Define click listener for the ViewHolder's View. textView = view.findViewById(R.id.text_view) } } // Create new views (invoked by the layout manager) override fun onCreateViewHolder( viewGroup: ViewGroup, viewType: Int): ViewHolder { // Create a new view, which defines the UI of the list item val itemView = LayoutInflater.from(viewGroup.context) .inflate(R.layout.my_text_view, viewGroup, false) return ViewHolder(itemView) } // Replace the contents of a view (invoked by the layout manager) override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) { // Get element from your dataset at this position and replace the // contents of the view with that element viewHolder.textView.text = dataSet[position] } // Return the size of your dataset (invoked by the layout manager) override fun getItemCount() = dataSet.size } |
レイアウトのコード
activity_main.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<?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:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <!-- A RecyclerView with some commonly used attributes --> <androidx.recyclerview.widget.RecyclerView android:id="@+id/my_recycler_view" android:scrollbars="vertical" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout> |
cellとなる部分はListViewと同じように要素のレイアウトで作ります
my_text_view.xml
1 2 3 4 5 6 7 8 9 10 11 |
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:id="@+id/text_view" android:layout_width="wrap_content" android:layout_height="50dp" /> </LinearLayout> |
build.gradle
1 2 3 4 |
dependencies { ... implementation 'androidx.recyclerview:recyclerview:1.2.1' } |
ここで注意するところは、layout_heightを限定しないとセルの高さがとんでもなく広がってしまうことがあります。
これでリストがスクロールするRecyclerViewの簡単な例ができたと思います。
しかし、これは境界ラインも無いしカスタマイズで作り込む必要があります。
逆にいうとListViewのカスタマイズには限界があったところを柔軟に拡張したということでしょうか。
画像とテキストのサンプルコード
いままでの例を元に、画像と文字列を組み合わせた構成されるcellを作ってみたいと思います。
また双方向連結リストのLinkedListを使用すれば削除・追加などが早くなるようです。
画像を8枚程度適当に用意してdrawable-v24に入れます。
MainActivity.kt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
//package your.package.name import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView class MainActivity : AppCompatActivity() { private val names: ArrayList<String> = arrayListOf( "Bellflower", "Bougainvillea", "Cosmos", "Cosmos field", "Delphinium", "Flowers", "Lotus", "Spring Flowers" ) private val photos: ArrayList<Int> = arrayListOf( R.drawable.bellflower, R.drawable.bougainvillea, R.drawable.cosmos, R.drawable.cosmos_field, R.drawable.delphinium, R.drawable.flowers, R.drawable.lotus, R.drawable.spring_flowers ) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val recyclerView = findViewById<RecyclerView>(R.id.my_recycler_view) // use this setting to improve performance if you know that changes // in content do not change the layout size of the RecyclerView recyclerView.setHasFixedSize(true) // use a linear layout manager val rLayoutManager: RecyclerView.LayoutManager = LinearLayoutManager(this) recyclerView.layoutManager = rLayoutManager recyclerView.adapter = MyAdapter(photos, names) } } |
MyAdapter.kt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
//package your.package.name import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.ImageView import android.widget.TextView import androidx.recyclerview.widget.RecyclerView class MyAdapter(private val iImages: ArrayList<Int>, private val iNames: ArrayList<String>): RecyclerView.Adapter<MyAdapter.ViewHolder>(){ // Provide a reference to the type of views // that you are using (custom ViewHolder). class ViewHolder(view: View) : RecyclerView.ViewHolder(view) { val textView: TextView val imageView: ImageView init { // Define click listener for the ViewHolder's View. textView = view.findViewById(R.id.text_view) imageView = view.findViewById(R.id.image_view) } } // Create new views (invoked by the layout manager) override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder { // Create a new view, which defines the UI of the list item val itemView = LayoutInflater.from(viewGroup.context) .inflate(R.layout.my_text_view, viewGroup, false) return ViewHolder(itemView) } // Replace the contents of a view (invoked by the layout manager) override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) { // Get element from your dataset at this position and replace the // contents of the view with that element viewHolder.imageView.setImageResource(iImages.get(position)) viewHolder.textView.text = iNames[position] } // Return the size of your dataset (invoked by the layout manager) override fun getItemCount() = iNames.size } |
activity_main.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<?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:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <!-- A RecyclerView with some commonly used attributes --> <androidx.recyclerview.widget.RecyclerView android:id="@+id/my_recycler_view" android:scrollbars="vertical" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout> |
cellのレイアウト
my_text_view.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingStart="20dp" android:paddingTop="5dp" android:baselineAligned="false" tools:ignore="RtlSymmetry,UseCompoundDrawables"> <ImageView android:id="@+id/image_view" android:layout_margin="2dp" android:layout_width="120dp" android:layout_height="120dp" android:contentDescription="@string/description"/> <TextView android:id="@+id/text_view" android:textSize="24sp" android:paddingStart="20dp" android:paddingEnd="10dp" android:paddingTop="50dp" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout> |
strings.xml
1 2 3 4 |
<resources> <string name="app_name">Your App Name</string> <string name="description">picture</string> </resources> |
バージョンなどは自分の環境に合わせてください
build.gradle
1 2 3 4 |
dependencies { ... implementation 'androidx.recyclerview:recyclerview:1.2.1' } |
これで実行してみると
関連ページ:
- RecyclerView の基本的な設定
- RecyclerViewとItemTouchHelperでドラッグ&ドロップとSwipe操作
Reference:
リストとカードの作成 | Android Developers
RecyclerView | Android Developers