異なるアダプターを1つに繋げたいときのアプローチ

## はじめに
[athornz](https://github.com/athornz)氏がGistに[MergeRecyclerAdapter.java](https://gist.github.com/athornz/008edacd1d3b2f1e1836)というのをあげていたので使ってみたけど、便利だったので紹介します。
なお僕が使っているバージョンは少し手を入れています。
[MergeRecyclerAdapter.java(Cattaka ver)](https://github.com/cattaka/AdapterToolbox/blob/master/adapter-toolbox/src/main/java/net/cattaka/android/adaptertoolbox/thirdparty/MergeRecyclerAdapter.java)

## MergeRecyclerAdapterの用途
１つのリストに複数の属性のデータを表示したいとき、１つのアダプターのデータクラスをObjectなどにして、インスタンスの型に合わせて[RecyclerView.Adapter#getItemViewType(int)](https://developer.android.com/intl/ja/reference/android/support/v7/widget/RecyclerView.Adapter.html#getItemViewType(int))の戻り値を切り替えすなど、少々面倒なことをする必要がありました。この場合、表示するデータクラスの組み合わせが変わるたびにアダプターを作り変えることになり、結構なコストが掛かってしまいます。

MergeRecyclerAdapterを使えば、それぞれのデータクラス用のアダプターを個別に作り、それらを連結するということができるので、それらの煩わしさを解消することができます。また他のメリットとしてヘッダーやフッターを付けるのに使うこともできます。デメリットとしてはクリックイベントのハンドリングが少しコツがいるようになります。

## ヘッダーとフッターをつける
ヘッダー用、データ用、フッター用の３つのアダプターを作り、MergeRecyclerAdapterを用いて繋げます。
ここではヘッダーとフッターには単独のレイアウトを表示する[SingleViewAdapter](https://github.com/cattaka/AndroidSnippets/blob/master/snippets/src/main/java/net/cattaka/android/snippets/adapter/SingleViewAdapter.java)を使っています。

![Screenshot_20160506-160124.png](https://qiita-image-store.s3.amazonaws.com/0/55138/86e804a5-8927-2176-75c4-eb1336f9aa21.png)

### RecyclerViewHeaderExampleActivity.java より

```java
{   // prepare adapters
    mMergeRecyclerAdapter = new MergeRecyclerAdapter<>(this);
    {   // create header adapter
        mHeaderAdapter = new SingleViewAdapter(this, R.layout.view_header);
        mHeaderAdapter.setOnItemClickListener(this);
        mHeaderAdapter.setOnItemLongClickListener(this);
        mMergeRecyclerAdapter.addAdapter(mHeaderAdapter);
    }
    {   // create items adapter
        List<String> items = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            items.add("item " + i);
        }
        mItemsAdapter = new SimpleStringAdapter(this, items);
        mItemsAdapter.setOnItemClickListener(this);
        mItemsAdapter.setOnItemLongClickListener(this);
        mMergeRecyclerAdapter.addAdapter(mItemsAdapter);
    }
    {   // create footer adapter
        mFooterAdapter = new SingleViewAdapter(this, R.layout.view_footer);
        mFooterAdapter.setOnItemClickListener(this);
        mFooterAdapter.setOnItemLongClickListener(this);
        mMergeRecyclerAdapter.addAdapter(mFooterAdapter);
    }
    {
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
        mRecyclerView.setAdapter(mMergeRecyclerAdapter);
    }
}
```
[サンプルコード全体はこちら](https://github.com/cattaka/AndroidSnippets/blob/master/example/src/main/java/net/cattaka/android/snippets/example/RecyclerViewHeaderExampleActivity.java#L36)


## 複数のデータ型を表示する
次の例はStringのデータとNumberのデータを１つのRecyclerViewに表示しています。
Stringのデータを表示するために[SimpleStringAdapter](https://github.com/cattaka/AndroidSnippets/blob/master/example/src/main/java/net/cattaka/android/snippets/example/adapter/SimpleStringAdapter.java)を、Numberのデータを表示するために[SimpleNumberAdapter](https://github.com/cattaka/AndroidSnippets/blob/master/example/src/main/java/net/cattaka/android/snippets/example/adapter/SimpleNumberAdapter.java)を使っています。それぞれのヘッダーには[SingleViewAdapter](https://github.com/cattaka/AndroidSnippets/blob/master/snippets/src/main/java/net/cattaka/android/snippets/adapter/SingleViewAdapter.java)を使っています。

![Screenshot_20160506-151636.png](https://qiita-image-store.s3.amazonaws.com/0/55138/6727b495-e118-0794-3df1-5189f694b4a6.png)

### MultiAdapterExampleActivity.java より

```java
{   // prepare adapters
    mMergeRecyclerAdapter = new MergeRecyclerAdapter<>(this);
    {   // create strings header adapter
        mStringsHeaderAdapter = new SingleViewAdapter(this, R.layout.view_header_string);
        mStringsHeaderAdapter.setOnItemClickListener(this);
        mStringsHeaderAdapter.setOnItemLongClickListener(this);
        mMergeRecyclerAdapter.addAdapter(mStringsHeaderAdapter);
    }
    {   // create strings adapter
        List<String> items = new ArrayList<>();
        for (int i = 0; i < 3; i++) {
            items.add("item " + i);
        }
        mStringsAdapter = new SimpleStringAdapter(this, items);
        mStringsAdapter.setOnItemClickListener(this);
        mStringsAdapter.setOnItemLongClickListener(this);
        mMergeRecyclerAdapter.addAdapter(mStringsAdapter);
    }
    {   // create numbers header adapter
        mNumbersHeaderAdapter = new SingleViewAdapter(this, R.layout.view_header_number);
        mNumbersHeaderAdapter.setOnItemClickListener(this);
        mNumbersHeaderAdapter.setOnItemLongClickListener(this);
        mMergeRecyclerAdapter.addAdapter(mNumbersHeaderAdapter);
    }
    {   // create numbers adapter
        List<Number> items = new ArrayList<>();
        for (int i = 0; i < 3; i++) {
            items.add(i);
        }
        mNumbersAdapter = new SimpleNumberAdapter(this, items);
        mNumbersAdapter.setOnItemClickListener(this);
        mNumbersAdapter.setOnItemLongClickListener(this);
        mMergeRecyclerAdapter.addAdapter(mNumbersAdapter);
    }
    {
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
        mRecyclerView.setAdapter(mMergeRecyclerAdapter);
    }
}
```
[サンプルコード全体はこちら](https://github.com/cattaka/AndroidSnippets/blob/master/example/src/main/java/net/cattaka/android/snippets/example/MultiAdapterExampleActivity.java#L39)


## クリックイベントのハンドリング
MergeRecyclerAdapterは性質上、複数のアダプターが同居することになるので「何番目の要素がクリックされたときのイベント」をハンドリングするときに、どのアダプターの要素なのか識別することが必要です。その仕組みは[MergeRecyclerAdapter#getAdapterOffsetForItem(int)](https://github.com/cattaka/AndroidSnippets/blob/master/snippets/src/main/java/net/cattaka/android/snippets/thirdparty/MergeRecyclerAdapter.java#L215)として提供されています。このメソッドは戻り値として[LocalAdapter](https://github.com/cattaka/AndroidSnippets/blob/master/snippets/src/main/java/net/cattaka/android/snippets/thirdparty/MergeRecyclerAdapter.java#L91)を返します。LocalAdapterからはオリジナルのアダプターへの参照とそのアダプター内のポジション(インデックス)が取得できます。

僕がクリックイベントをハンドリングするときは自作の[CustomRecyclerAdapter.java](https://github.com/cattaka/AndroidSnippets/blob/master/snippets/src/main/java/net/cattaka/android/snippets/adapter/CustomRecyclerAdapter.java)と組み合わせて、以下のようなコードでハンドリングしています(CustomRecyclerAdapter.javaについては後述の余談で解説します)。

### MultiAdapterExampleActivity.java より

```java
@Override
public void onItemClick(RecyclerView parent, CustomRecyclerAdapter adapter, int position,
                        int id, RecyclerView.ViewHolder vh) {
    if (parent.getId() == R.id.recycler) {
        MergeRecyclerAdapter.LocalAdapter la = mMergeRecyclerAdapter.getAdapterOffsetForItem(position);
        if (la.mAdapter == mStringsHeaderAdapter) {
            Toast.makeText(this, "Strings Header is clicked.", Toast.LENGTH_SHORT).show();
        } else if (la.mAdapter == mStringsAdapter) {
            String item = mStringsAdapter.getItemAt(la.mLocalPosition);
            Toast.makeText(this, item + " is clicked.", Toast.LENGTH_SHORT).show();
        } else if (la.mAdapter == mNumbersHeaderAdapter) {
            Toast.makeText(this, "Numbers Header is clicked.", Toast.LENGTH_SHORT).show();
        } else if (la.mAdapter == mNumbersAdapter) {
            Number item = mNumbersAdapter.getItemAt(la.mLocalPosition);
            Toast.makeText(this, item + " is clicked.", Toast.LENGTH_SHORT).show();
        } else if (la.mAdapter == mFooterAdapter) {
            Toast.makeText(this, "Footer is clicked.", Toast.LENGTH_SHORT).show();
        }
    }
}
```
[サンプルコード全体はこちら](https://github.com/cattaka/AndroidSnippets/blob/master/example/src/main/java/net/cattaka/android/snippets/example/MultiAdapterExampleActivity.java#L81)

## 余談
### ヘッダーやフッターにわざわざSingleViewAdapterを使う件
僕がヘッダーやフッターを作るときは、わざわざレイアウトXMLを作らなくてはいけない[SingleViewAdapter](https://github.com/cattaka/AndroidSnippets/blob/master/snippets/src/main/java/net/cattaka/android/snippets/adapter/SingleViewAdapter.java)を使っています。正直なところ面倒です。文字列を渡したら表示するだけのアダプターを作っておいて、それを使えばいちいちレイアウトXMLを作らなくても済みます。
でもデザイナさんに外観の調整をお願いしてると、外観を個別に調整したいという声が出てきます。そういった声に対応できるように意図してヘッダーやフッターのレイアウトXMLを分けています。

### CustomRecyclerAdapter.javaを噛ませてる件
RecyclerViewには作りの都合上、ListView(というかAdapterView)の[OnItemClickListener](http://developer.android.com/intl/ja/reference/android/widget/AdapterView.OnItemClickListener.html)や[OnItemLongClickListener](http://developer.android.com/intl/ja/reference/android/widget/AdapterView.OnItemLongClickListener.html)がありません。だからといって個別にListener用のinterfaceを定義して、そのイベントの引き回しを実装していると面倒くさいです。その辺りの兼ね合いから[CustomRecyclerAdapter](https://github.com/cattaka/AndroidSnippets/blob/master/snippets/src/main/java/net/cattaka/android/snippets/adapter/CustomRecyclerAdapter.java)を使うようにしています。

ただ正直使うときに若干クセがあり、onCreateViewHolderの実装に注意する必要があるので、万人にお薦めできるものでは無いです。onCreateViewHolder内でクリックイベントをハンドリングしたいViewに対して、setTag/setOnClickListenerを呼んであげないといけないです。

- 参考
 - [SimpleStringAdapterの実装](https://github.com/cattaka/AndroidSnippets/blob/master/example/src/main/java/net/cattaka/android/snippets/example/adapter/SimpleStringAdapter.java#L28)
 - [SimpleNumberAdapterの実装](https://github.com/cattaka/AndroidSnippets/blob/master/example/src/main/java/net/cattaka/android/snippets/example/adapter/SimpleNumberAdapter.java#L28)
 - [ComplexStringAdapterの実装](https://github.com/cattaka/AndroidSnippets/blob/master/example/src/main/java/net/cattaka/android/snippets/example/adapter/ComplexStringAdapter.java#L28)


## サンプルアプリ
https://github.com/cattaka/AndroidSnippets