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-recyclerviewのRecyclerItemClickListenerです。
少し問題があったので、これに少し手を加えたコードを採用しました。
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