【Android】RecyclerViewの基本的な実装

# RecyclerViewとは

主に、動的な件数のViewを繰り返し表示するためのWidget。
サポートライブラリのcom.android.support:recyclerview-v7で利用可能。

# ListViewとの違い

RecyclerViewはListViewと異なり

* HeaderViewとFooterViewが無い
* 区切り線等のセル装飾は自分で実装しないといけない
* ListItemのクリックイベントが無いから自分で実装しないといけない
* ViewHolderが標準で組み込まれている
* addやremoveのアニメーションが簡単に付与できる
* 横スクロールやグリッド表示等が簡単に設定できる

等、様々な違いがある。

# 実装方法

通常の縦スクロールリストの簡単な作り方を記載。
セルには一行のテキストを表示するだけ。

## Layout

まず画面にRecyclerViewを設置

```xml:fragment.xml
〜省略〜

<android.support.v7.widget.RecyclerView
    android:id="@+id/recycler_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

〜省略〜
```

次にリストのセルとなるレイアウトを作成(この辺りはListViewと一緒)

```xml:list_item.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">

    <TextView
        android:id="@+id/list_item_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</LinearLayout>
```

## Fragment

初期化

```java:Fragment
public class RecyclerFragment extends Fragment implements OnRecyclerListener {

    private Activity mActivity = null;
    private View mView;
    private RecyclerFragmentListener mFragmentListener = null;

    // RecyclerViewとAdapter
    private RecyclerView mRecyclerView = null;
    private RecyclerAdapter mAdapter = null;

    public interface RecyclerFragmentListener {
        void onRecyclerEvent();
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        if (!(activity instanceof RecyclerFragmentListener)) {
            throw new UnsupportedOperationException(
                    "Listener is not Implementation.");
        } else {
            mFragmentListener = (RecyclerFragmentListener) activity;
        }
        mActivity = activity;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        mView = inflater.inflate(R.layout.fragment, container, false);

        // RecyclerViewの参照を取得
        mRecyclerView = (RecyclerView) mView.findViewById(R.id.recycler_view);
        // レイアウトマネージャを設定(ここで縦方向の標準リストであることを指定)
        mRecyclerView.setLayoutManager(new LinearLayoutManager(mActivity));

        return mView;
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        // 適当にデータ作成
        ArrayList<String> array = new ArrayList<>();
        array.add("A");
        array.add("B");
        array.add("C");

        // この辺りはListViewと同じ
        // 今回は特に何もしないけど、一応クリック判定を取れる様にする
        mAdapter = new RecyclerAdapter(mActivity, array, this);
        mRecyclerView.setAdapter(mAdapter);
    }

    @Override
    public void onRecyclerClicked(View v, int position) {
        // セルクリック処理
    }

```

## Listener

OnItemClickListenerとかが無いので、自分が取りたい情報が取れる様に勝手に作る。

```java:OnRecyclerListener.java

public interface OnRecyclerListener {

    void onRecyclerClicked(View v, int position);

}

```

## Adapter

ListViewと同様にAdapterも実装する

```java:RecyclerAdapter.java

public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.ViewHolder> {

    private LayoutInflater mInflater;
    private ArrayList<String> mData;
    private Context mContext;
    private OnRecyclerListener mListener;

    public RecyclerAdapter(Context context, ArrayList<String> data, OnRecyclerListener listener) {
        mInflater = LayoutInflater.from(context);
        mContext = context;
        mData = data;
        mListener = listener;
    }

    @Override
    public RecyclerAdapter.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
        // 表示するレイアウトを設定
        return new ViewHolder(mInflater.inflate(R.layout.list_item, viewGroup, false));
    }

    @Override
    public void onBindViewHolder(ViewHolder viewHolder, final int i) {
        // データ表示
        if (mData != null && mData.size() > i && mData.get(i) != null) {
            viewHolder.textView.setText(mData.get(i));
        }
        
        // クリック処理
        viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mListener.onRecyclerClicked(v, i);
            }
        });

    }

    @Override
    public int getItemCount() {
        if (mData != null) {
            return mData.size();
        } else {
            return 0;
        }
    }

    // ViewHolder(固有ならインナークラスでOK)
    class ViewHolder extends RecyclerView.ViewHolder {

        TextView textView;

        public ViewHolder(View itemView) {
            super(itemView);
            textView = (TextView) itemView.findViewById(R.id.list_item_text);
        }
    }

}

```

ここまででひとまずリスト表示までは完成。

![recycler_sample_img_01.png](https://qiita-image-store.s3.amazonaws.com/0/87641/938dd0d5-e80e-45b7-217f-d15a867ab0ed.png)


# Tips

## セルのレイアウトを切り替えたい

getItemViewTypeでデータによって異なるViewTypeを返却し、AdapterのonCreateViewHolderで
returnするViewHolderを変更すればOK。
まったく異なるレイアウトを切り替えることになるなら、ViewHolderも分けた方が良い。

Stringリストで
「Header」が入っていればヘッダ
「Footer」が入っていればフッタ
それ以外はデータ

という切替を行う場合

```java:RecyclerAdapter.java

    private final static int VIEWTYPE_HEADER = 1;
    private final static int VIEWTYPE_FOOTER = 2;

    @Override
    public RecyclerAdapter.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {

        switch (i) {
            case VIEWTYPE_HEADER:
                return new HeaderViewHolder(mLayoutInflater
                        .inflate(R.layout.list_item_header, viewGroup, false));
            case VIEWTYPE_FOOTER:
                return new FooterViewHolder(mLayoutInflater
                        .inflate(R.layout.list_item_footer, viewGroup, false));
            default:
                return new DataViewHolder(mLayoutInflater
                        .inflate(R.layout.list_item_data, viewGroup, false));
        }

    }

    @Override
    public int getItemViewType(int position) {
        if (mData.get(position).equals("Header")) {
            return VIEWTYPE_HEADER;
        } else if (mData.get(position).equals("Footer")) {
            return VIEWTYPE_FOOTER;
        } else {
            return 0;
        }
    }

    class ViewHolder extends RecyclerView.ViewHolder {
        // 基底になるViewHolder
    }
    class HeaderViewHolder extends RecyclerAdapter.ViewHolder {
        // ヘッダのViewHolder
    }

    class FooterViewHolder extends RecyclerAdapter.ViewHolder {
        // フッタのViewHolder
    }

    class DataViewHolder extends RecyclerAdapter.ViewHolder {
        // データのViewHolder
    }

```

こんな感じで切り替えられる

## 横スクロールリストを作りたい

setLayoutManagerでセットするレイアウトマネージャの設定を変更する

```java:Fragment
        LinearLayoutManager manager = new LinearLayoutManager(mActivity);
        manager.setOrientation(LinearLayoutManager.HORIZONTAL); // ここで横方向に設定
        mRecyclerView.setLayoutManager(manager);

```

## グリッド表示リストを作りたい

setLayoutManagerでセットするレイアウトマネージャの種類を変更する

```java:Fragment
        // GridLayoutManagerの第二引数は、X方向の件数
        mRecyclerView.setLayoutManager(new GridLayoutManager(mActivity, 2));

```

![device-2015-08-18-104111.png](https://qiita-image-store.s3.amazonaws.com/0/87641/2e6dd7a1-ac87-16d6-4e27-b80544eaa370.png)