Edited at

RecyclerViewの基本

More than 1 year has passed since last update.


検証環境

この記事の内容は、以下の環境で検証した。

* 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ではそれぞれが独立した処理(メソッドやクラス)として定義する必要がある。

RecyclerView_001.png


今回作成したアプリ

ものすごく単純なアプリを作成した。1行内に画像とタイトル、その下に詳細な情報を表示するだけの内容。

今回の目的は複雑な表示ではなく、RecyclerViewの基本的な実装方法を理解するため、このような簡単なアプリにしている。

SmallScreenshot_1493098981.png


実装

以下の順で作成する子により、入力補完を最大限に活かせる。


サポートライブラリの追加

RecyclerViewを使用するにはサポートライブラリを追加する必要がある。


build.gradle

dependencies {

compile 'com.android.support:recyclerview-v7:25.3.1'
}


1行分のレイアウト


row.xml


<?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行分のデータモデル


RowData.java

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フィールドで保持する。


CasarealViewHolder.java

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の定義


CasarealRecycleViewAdapter.java

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の定義


全体のレイアウトファイル


activity_main.xml

<?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の処理


MainActivity.java

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