66
60

More than 5 years have passed since last update.

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

Last updated at Posted at 2015-01-06

OnItemClickListenerをセットできない

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

// 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を実装してるコードが多いみたいでした。

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

独自のListenerを作る

twoway-viewというライブラリでは、独自にItemClickSupportというクラスを作って色々やってるみたいですが、ここまで色々いらないので単純にListViewのsetOnItemClickListenerみたいなのがほしいなぁと思っていたらありました。

sticky-headers-recyclerviewRecyclerItemClickListenerです。

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

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で処理を書くだけ。

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

そもそもどういう実装するのがよいのか不明

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

Googleのサンプルがあったらそれを真似するのがよさそう。ちなみにioschdでは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

66
60
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
66
60