RecyclerViewでOnItemClickイベントを実装する


#OnItemClickListenerをセットできない
RecyclerViewはListViewと違って`OnItemClickListener`をセットできません。
`OnItemTouchListener`をセットすることはできるんですが、こいつはタップしたViewのpositionを返すメソッドを持っていないのでタップ後のアクションを実装するのが難しいです。

```java
// MotionEventしかわからない
mRecyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
    @Override
    public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
        return false;
    }

    @Override
    public void onTouchEvent(RecyclerView rv, MotionEvent e) {
    }
});
```

色々見てみたところ、AdapterにOnClickListenerを実装してるコードが多いみたいでした。

* [[Android] RecyclerViewでonClickを実装する](http://wasabeef.jp/recyclerview-onclick/)
* [RecyclerView item onClick](https://gist.github.com/grantland/cd70814fe4ac369e3e92)
* [Android RecyclerView – Simple List](http://www.jayway.com/2014/12/23/android-recyclerview-simple-list/)

ただ、ModelとViewのAdapterであるはずのクラスにイベントをセットするのはやはり違和感あるというか気持ち悪いです。

# 独自のListenerを作る
[twoway-view](https://github.com/lucasr/twoway-view)というライブラリでは、独自に[ItemClickSupport](https://github.com/lucasr/twoway-view/blob/master/core/src/main/java/org/lucasr/twowayview/ItemClickSupport.java)というクラスを作って色々やってるみたいですが、ここまで色々いらないので単純にListViewの`setOnItemClickListener`みたいなのがほしいなぁと思っていたらありました。

[sticky-headers-recyclerview](https://github.com/timehop/sticky-headers-recyclerview)の[RecyclerItemClickListener](https://github.com/timehop/sticky-headers-recyclerview/blob/master/sample/src/main/java/com/timehop/stickyheadersrecyclerview/sample/RecyclerItemClickListener.java)です。

少し問題があったので、これに少し手を加えたコードを採用しました。

```RecyclerItemClickListener.java
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;

public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListener {

    GestureDetector mGestureDetector;
    private OnItemClickListener mListener;

    public RecyclerItemClickListener(Context context, OnItemClickListener listener) {
        mListener = listener;
        mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
            @Override
            public boolean onSingleTapUp(MotionEvent e) {
                return true;
            }
        });
    }

    @Override
    public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) {
        // タッチした箇所のViewを取得
        View childView = view.findChildViewUnder(e.getX(), e.getY());
        if (childView != null && mListener != null && mGestureDetector.onTouchEvent(e)) {
            // onInterceptTouchEventのタイミングだとアイテムのtouch feedbackがつく前にonItemClickが呼ばれてしまうので、明示的にsetPressed(true)を呼んでいます。
            childView.setPressed(true);
            mListener.onItemClick(childView, view.getChildPosition(childView));
        }
        return false;
    }

    @Override
    public void onTouchEvent(RecyclerView view, MotionEvent motionEvent) {
        // Do nothing
    }

    public interface OnItemClickListener {
        public void onItemClick(View view, int position);
    }

}
```

使う時はlistenerをセットして`OnItemClick`で処理を書くだけ。

```java
mRecyclerView.addOnItemTouchListener(
        new RecyclerItemClickListener(getActivity(), new RecyclerItemClickListener.OnItemClickListener() {
            @Override
            public void onItemClick(View view, int position) {
                // ここで処理
            }
        })
);
```

#そもそもどういう実装するのがよいのか不明
一応これで動いているんですが、なんか全然ベストプラクティスって感じがしません。RecyclerViewをちょっといじった感想としては、Recycleまわりの面倒を見る部分だけに集中していて、他の実装は各開発者に任せるって感じのかなりふわっとしたViewという印象です。
Adapterに実装するもよし、Holderに実装するもよし、今回のようにListenerを作って実装するもよし、好きな方法で作れる分迷うところです。

Googleのサンプルがあったらそれを真似するのがよさそう。ちなみに[ioschd](https://github.com/google/iosched)ではListViewを使っていました。他に何か知っていればぜひ教えてください。

# 2015/06/11（木）追記
2015年のioschedのコードを見ると、`Adapter#getView()` の中でOnClickListenerをセットしてるので、Adapterでやるのがいいかもしれません。
https://github.com/google/iosched/blob/master/android/src/main/java/com/google/samples/apps/iosched/ui/MyScheduleAdapter.java