検証環境
この記事の内容は、以下の環境で検証した。
- Java:8
- Android studio 2.3
- CompileSdkVersion:25
- MinSdkVersion:19
- TargetSdkVersion:25
- BuildToolsVersion:25.0.2
RecyclerViewについて
特徴
- ListViewをさらに進化させて柔軟にしたもの
- 大きなデータセットを表示できるコンテナ
- 限られたビューを維持して効率的にスクロール可能
- ヘッダー、フッターは存在しない
- 区切り線は自身で実装する必要がある
全体像
RecyclerViewで登場するクラス一覧
クラス名 | 説明 | ListViewだとどこにあたるか |
---|---|---|
RecyclerView | RecyclerViewそのもの。ウィジェット。 | ListViewそのものに相当 |
RecyclerView.Adapter | 1行分のデータを1行分のViewに設定して生成するもの | ListViewのAdapterに相当 |
RecyclerView.ViewHolder | 1行分のView(ウィジェット)の参照を保持するもの | ListViewではgetItemメソッドでレイアウトをインフレートして行っていた。RecyclerViewでは、1行分のレイアウトのViewをViewHolderのサブクラスで保持しておく。 |
RecyclerView.LayoutManager | 1つ分のデータのサイズなどを考慮して、レスポンシブにレイアウトを管理するクラス | ListViewには存在しない。 |
図で見るRecyclerView
最も簡単なRecyclerViewの全体像を図で表すと以下のようになる。
ListViewとの大きな違いとして、ListViewではgetItemメソッド内でレイアウトファイルからインフレート、データの取得、
Viewに格納を行っている。RecyclerViewではそれぞれが独立した処理(メソッドやクラス)として定義する必要がある。
今回作成したアプリ
ものすごく単純なアプリを作成した。1行内に画像とタイトル、その下に詳細な情報を表示するだけの内容。
今回の目的は複雑な表示ではなく、RecyclerViewの基本的な実装方法を理解するため、このような簡単なアプリにしている。
実装
以下の順で作成する子により、入力補完を最大限に活かせる。
サポートライブラリの追加
RecyclerViewを使用するにはサポートライブラリを追加する必要がある。
dependencies {
compile 'com.android.support:recyclerview-v7:25.3.1'
}
1行分のレイアウト
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher"/>
<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="0.5"/>
<TextView
android:id="@+id/detail"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="0.5"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
1行分のデータモデル
package jp.co.casareal.sample.recyclerviewsample.model;
/**
* Created by naoi on 2017/04/25.
*/
public class RowData {
private String title;
private String detail;
・・・setter/getter省略・・・
}
ViewHolderの定義
Adapterがインフレートした1行分のレイアウトからViewの参照を取得し、publicフィールドで保持する。
package jp.co.casareal.sample.recyclerviewsample.viewholder;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.TextView;
import jp.co.casareal.sample.recyclerviewsample.R;
/**
* Created by naoi on 2017/04/25.
*/
public class CasarealViewHolder extends RecyclerView.ViewHolder {
public TextView titleView;
public TextView detailView;
public CasarealViewHolder(View itemView) {
super(itemView);
titleView = (TextView) itemView.findViewById(R.id.title);
detailView = (TextView) itemView.findViewById(R.id.detail);
}
}
Adapterの定義
package jp.co.casareal.sample.recyclerviewsample.adapter;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import java.util.List;
import jp.co.casareal.sample.recyclerviewsample.R;
import jp.co.casareal.sample.recyclerviewsample.model.RowData;
import jp.co.casareal.sample.recyclerviewsample.viewholder.CasarealViewHolder;
/**
* Created by naoi on 2017/04/25.
*/
public class CasarealRecycleViewAdapter extends RecyclerView.Adapter<CasarealViewHolder> {
private List<RowData> list;
public CasarealRecycleViewAdapter(List<RowData> list) {
this.list = list;
}
@Override
public CasarealViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View inflate = LayoutInflater.from(parent.getContext()).inflate(R.layout.row, parent,false);
CasarealViewHolder vh = new CasarealViewHolder(inflate);
return vh;
}
@Override
public void onBindViewHolder(CasarealViewHolder holder, int position) {
holder.titleView.setText(list.get(position).getTitle());
holder.detailView.setText(list.get(position).getDetail());
}
@Override
public int getItemCount() {
return list.size();
}
}
Adapter内の各処理の説明
@Override
public CasarealViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View inflate = LayoutInflater.from(parent.getContext()).inflate(R.layout.row, parent,false);
CasarealViewHolder vh = new CasarealViewHolder(inflate);
return vh;
}
『図で見るRecyclerView』の①と②に該当。
@Override
public void onBindViewHolder(CasarealViewHolder holder, int position) {
holder.titleView.setText(list.get(position).getTitle());
holder.detailView.setText(list.get(position).getDetail());
}
『図で見るRecyclerView』の③と④に該当。
@Override
public int getItemCount() {
return list.size();
}
このメソッドは表示するアイテムの総数を返す必要がある。
そのほか、コンストラクタなどで表示するデータセットを渡している。
Activityの定義
全体のレイアウトファイル
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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="jp.co.casareal.sample.recyclerviewsample.MainActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/casarealRecyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
ListView同様、あとでAdapterの設定が必要となるので、RecyclerViewにidを指定しておく必要がある。
Activityの処理
package jp.co.casareal.sample.recyclerviewsample;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;
import jp.co.casareal.sample.recyclerviewsample.adapter.CasarealRecycleViewAdapter;
import jp.co.casareal.sample.recyclerviewsample.model.RowData;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RecyclerView rv = (RecyclerView) findViewById(R.id.casarealRecyclerView);
CasarealRecycleViewAdapter adapter = new CasarealRecycleViewAdapter(this.createDataset());
LinearLayoutManager llm = new LinearLayoutManager(this);
rv.setHasFixedSize(true);
rv.setLayoutManager(llm);
rv.setAdapter(adapter);
}
private List<RowData> createDataset() {
List<RowData> dataset = new ArrayList<>();
for (int i = 0; i < 50; i++) {
RowData data = new RowData();
data.setTitle("カサレアル 太郎" + i + "号");
data.setDetail("カサレアル 太郎は" + i + "個の唐揚げが好き");
dataset.add(data);
}
return dataset;
}
}
各処理の説明
LinearLayoutManager llm = new LinearLayoutManager(this);
rv.setLayoutManager(llm);
ListViewにはない便利な機能の一つでレスポンシブにデータを表示するために使用するLayoutManagerを設定している。
今回はLinerLayoutを使用しているが、Gridなどもある。
rv.setHasFixedSize(true);
表示するデータの数が固定長であれば、リソースを有効活用し、パフォーマンスを向上させる設定。
CasarealRecycleViewAdapter adapter = new CasarealRecycleViewAdapter(this.createDataset());
rv.setAdapter(adapter);
Adapterを設定。
private List<RowData> createDataset() {
List<RowData> dataset = new ArrayList<>();
for (int i = 0; i < 50; i++) {
RowData data = new RowData();
data.setTitle("カサレアル 太郎" + i + "号");
data.setDetail("カサレアル 太郎は" + i + "個の唐揚げが好き");
dataset.add(data);
}
return dataset;
}
createDatasetメソッドで表示するデータセットを生成している。
より機能を充実させるには
Kotlin版ですが、ドラッグ・アンド・ドロップやスワイプによる操作方法を知りたい方は下記の記事を参考にしてください。
RecyclerViewでドラッグアンドドロップの移動とスワイプの削除
参考
リストとカードの作成
https://developer.android.com/training/material/lists-cards.html?hl=ja#CardView
API
https://developer.android.com/reference/android/support/v7/widget/RecyclerView.html