RecyclerView は、ListView をさらに進化させて柔軟にしたものだそうです。
cellをドラッグ&ドロップさせたり追加・消去などに強みを発揮するようですが、ここでは簡単な基本設定を試してみます。
2021.2.1
RecyclerView
わかりやすい説明がGoogle Developerのリストとカードの作成にあります。最初にこのサンプルコードを元にして実際に動かしてみたいと思います。
Ref: 図 1 RecyclerView ウィジェット
基本的にはListViewの進化形なので、ListViewがわかっていると理解しやすいです。
LayoutManagerはアイテムの位置調整を行い、 ビューの再利用(Recycle)する機能をもっているそうですが、ListViewでもconvertViewを駆使すればできていた事でしたが、あえて記述しなくても良くなったということでしょう。
シンプルなサンプルコード
最初にRecyclerViewのライブラリーをbuild.gradleに追加しないといけません
旧:com.android.support:recyclerview-v7:x.x.x
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 ... /> |
レイアウトのコード
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> |
RecyclerView オブジェクトのハンドルを取得してレイアウト マネージャーに接続し、アダプターにデータセットを渡します。
データセットとして20個の要素を持つ配列で設定してみます。
MainActivity.java
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 |
//package com.example.testrecyclerview; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.LinearLayoutManager; import java.util.Locale; public class MainActivity extends AppCompatActivity { private final String[] dataset = new String[20]; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); RecyclerView recyclerView = findViewById(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 RecyclerView.LayoutManager rLayoutManager = new LinearLayoutManager(this); recyclerView.setLayoutManager(rLayoutManager); int i = 0; while (i < 20) { dataset[i] = String.format(Locale.ENGLISH, "Data_0%d", i); i++; } MyAdapter adapter = new MyAdapter(dataset); recyclerView.setAdapter(adapter); } } |
Adapterですが、別ファイルとして作成しました。
MyAdapter.java
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 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
//package com.example.testrecyclerview; import androidx.recyclerview.widget.RecyclerView; import androidx.annotation.NonNull; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> { private final String[] localDataSet; /** * Provide a reference to the type of views that you are using * (custom ViewHolder). */ static class ViewHolder extends RecyclerView.ViewHolder { private final TextView textView; public ViewHolder(View view) { super(view); textView = view.findViewById(R.id.text_view); } public TextView getTextView(){ return textView; } } /** * Initialize the dataset of the Adapter. * * @param dataSet String[] containing the data to populate views to be used * by RecyclerView. */ public MyAdapter(String[] dataSet) { localDataSet = dataSet; } // Create new views (invoked by the layout manager) @NonNull @Override public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { // Create a new view, which defines the UI of the list item View view = LayoutInflater.from(viewGroup.getContext()) .inflate(R.layout.my_text_view, viewGroup, false); return new ViewHolder(view); } // Replace the contents of a view (invoked by the layout manager) @Override public void onBindViewHolder(ViewHolder viewHolder, final int position) { // Get element from your dataset at this position and replace the // contents of the view with that element viewHolder.getTextView().setText(localDataSet[position]); } // Return the size of your dataset (invoked by the layout manager) @Override public int getItemCount() { return localDataSet.length; } } |
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> |
ここで注意するところは、layout_heightを限定しないとセルの高さがとんでもなく広がってしまうことがあります。
これでリストがスクロールするRecyclerViewの簡単な例ができたと思います。
しかし、これは境界ラインも無いしカスタマイズで作り込む必要があります。
逆にいうとListViewのカスタマイズには限界があったところを柔軟に拡張したということでしょうか。
画像とテキストのサンプルコード
いままでの例を元に、画像と文字列を組み合わせた構成されるcellを作ってみたいと思います。
また、データセットも追加・削除・移動を前提にするためArrayListを使います。
また双方向連結リストのLinkedListを使用すれば削除・追加などが早くなるようです。
120x120pix程度の画像を10枚用意してdrawable-v24に入れます。
バージョンなどは自分の環境に合わせてください
build.gradle
1 2 3 4 5 6 7 8 9 10 |
apply plugin: 'com.android.application' android { ... } dependencies { ... implementation 'androidx.recyclerview:recyclerview:1.2.1' } |
MainActivity.java
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 50 51 52 53 54 55 56 57 58 59 60 61 62 |
//package com.example.testrecyclerview; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.LinearLayoutManager; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Locale; public class MainActivity extends AppCompatActivity { private static final String[] names = { "katakuriko", "kurumi", "mai", "miki", "nagi", "saya", "toko", "yuka", "yumiko", "yuyu" }; // それぞれの画像ファイルをdarawableに入れます // ArrayListにコピーするためintからInteger型にしました private static final Integer[] photos = { R.drawable.katakuriko, R.drawable.kurumi, R.drawable.mai, R.drawable.miki, R.drawable.nagi, R.drawable.saya, R.drawable.toko, R.drawable.yuka, R.drawable.yumiko, R.drawable.yuyu }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); RecyclerView recyclerView = findViewById(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 RecyclerView.LayoutManager rLayoutManager = new LinearLayoutManager(this); recyclerView.setLayoutManager(rLayoutManager); // 配列をArrayListにコピー List<String> itemNames = new ArrayList<>(Arrays.asList(names)); List<Integer> itemImages = new ArrayList<>(Arrays.asList(photos)); List<String> itemEmails = new ArrayList<>(); for(String s : itemNames){ String str = String.format(Locale.ENGLISH, "%s@gmail.com", s); itemEmails.add(str); } MyAdapter adapter = new MyAdapter(itemImages, itemNames, itemEmails); recyclerView.setAdapter(adapter); } } |
MyAdapter.java
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 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
//package com.example.testrecyclerview; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; import java.util.List; public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> { private final List<Integer> iImages; private final List<String> iNames; private final List<String> iEmails; // Provide a reference to the views for each data item // Complex data items may need more than one view per item, and // you provide access to all the views for a data item in a view holder static class ViewHolder extends RecyclerView.ViewHolder { // each data item is just a string in this case ImageView imageView; TextView textView; TextView emailView; ViewHolder(View v) { super(v); imageView = v.findViewById(R.id.image_view); textView = v.findViewById(R.id.text_view); emailView = v.findViewById(R.id.email_view); } } // Provide a suitable constructor (depends on the kind of dataset) MyAdapter(List<Integer> itemImages, List<String> itemNames, List<String> itemEmails) { this.iImages = itemImages; this.iNames = itemNames; this.iEmails = itemEmails; } // Create new views (invoked by the layout manager) @Override @NonNull public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { // create a new view View view = LayoutInflater.from(parent.getContext()) .inflate(R.layout.my_text_view, parent, false); // set the view's size, margins, paddings and layout parameters return new ViewHolder(view); } // Replace the contents of a view (invoked by the layout manager) @Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { // - get element from your dataset at this position // - replace the contents of the view with that element holder.imageView.setImageResource(iImages.get(position)); holder.textView.setText(iNames.get(position)); holder.emailView.setText(iEmails.get(position)); } // Return the size of your dataset (invoked by the layout manager) @Override public int getItemCount() { return 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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:paddingLeft="20dp" android:paddingTop="5dp" android:paddingRight="20dp" android:paddingBottom="5dp" android:background="#fac" android:baselineAligned="false" android:layout_height="wrap_content"> <RelativeLayout android:layout_width="wrap_content" android:layout_height="wrap_content"> <!-- 枠を入れるだけで深い意味はありません --> <ImageView android:background="#f44" android:layout_width="100dp" android:layout_height="100dp" android:contentDescription="@string/description1"/> <ImageView android:id="@+id/image_view" android:layout_margin="2dp" android:layout_width="96dp" android:layout_height="96dp" android:contentDescription="@string/description2"/> </RelativeLayout> <LinearLayout android:orientation="vertical" android:background="#cef" android:layout_weight="1" android:layout_width="0dp" android:layout_height="match_parent"> <TextView android:id="@+id/text_view" android:textSize="24sp" android:textColor="#00f" android:paddingStart="20dp" android:paddingEnd="10dp" android:paddingTop="20dp" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:id="@+id/email_view" android:textSize="18sp" android:paddingStart="20dp" android:paddingEnd="10dp" android:layout_gravity="center_vertical" android:autoLink="email" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout> </LinearLayout> |
strings.xml
1 2 3 4 5 |
<resources> <string name="app_name">Your App Name</string> <string name="description1">background</string> <string name="description2">picture</string> </resources> |
サンプル動画
少しはましになったでしょうか
ガールズバーの紹介アプリみたいになりましたが (汗…
関連ページ:
- RecyclerView の基本的な設定
- RecyclerViewとItemTouchHelperでドラッグ&ドロップ
Reference:
リストとカードの作成 | Android Developers
RecyclerView | Android Developers