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](https://qiita-image-store.s3.amazonaws.com/0/99960/fa09c36f-266b-2074-81ee-f7fe03a50675.png)



# 今回作成したアプリ

ものすごく単純なアプリを作成した。1行内に画像とタイトル、その下に詳細な情報を表示するだけの内容。
今回の目的は複雑な表示ではなく、RecyclerViewの基本的な実装方法を理解するため、このような簡単なアプリにしている。

![SmallScreenshot_1493098981.png](https://qiita-image-store.s3.amazonaws.com/0/99960/6384a222-ab6b-2dea-7fab-70951be211f8.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の参照を取得し、<b>public</b>フィールドで保持する。

``` 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内の各処理の説明

``` .java
 @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』の①と②に該当。

``` .java
@Override
    public void onBindViewHolder(CasarealViewHolder holder, int position) {
        holder.titleView.setText(list.get(position).getTitle());
        holder.detailView.setText(list.get(position).getDetail());
    }
```
『図で見るRecyclerView』の③と④に該当。


``` .java
    @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;
    }
}
```

#### 各処理の説明

``` .java
 LinearLayoutManager llm = new LinearLayoutManager(this);
 rv.setLayoutManager(llm);
```
ListViewにはない便利な機能の一つでレスポンシブにデータを表示するために使用するLayoutManagerを設定している。
今回はLinerLayoutを使用しているが、Gridなどもある。

``` .java
 rv.setHasFixedSize(true);
```
表示するデータの数が固定長であれば、リソースを有効活用し、パフォーマンスを向上させる設定。

``` .java
  CasarealRecycleViewAdapter adapter = new CasarealRecycleViewAdapter(this.createDataset());
  rv.setAdapter(adapter);
```
Adapterを設定。

``` .java
  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://qiita.com/naoi/items/f8004f906db16d6b38da)

# 参考
リストとカードの作成
https://developer.android.com/training/material/lists-cards.html?hl=ja#CardView

API
https://developer.android.com/reference/android/support/v7/widget/RecyclerView.html