Java
Android
view
RecyclerView

RecyclerViewの基本

検証環境

この記事の内容は、以下の環境で検証した。
* 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