問題
Google Playアプリケーションの正確な動作をシミュレートしようとしています。
現在、「トップゲーム」などのカテゴリをスクロールしているときにリストをタッチすると、そのセルとRecyclerView
がタッチイベントを処理し、波紋が表示されているため、両方がタッチイベントを処理していることを確認できます指を押したままにすると、スクロールが止まります。または、リストがまだ移動しているときにセルをクリックすると、そのコンテンツはすぐに開かれます。
デフォルトの動作では、移動中に指を下に置くとスクロールが即座に停止し、そのタッチイベントを消費します。つまり、セルはそのタッチについて通知されません。アプリケーションのメインコンテンツがRecyclerView
にある場合、これは非常に苛立たしく、ユーザーが連続してスクロールしてからセルをクリックすると、最初のクリックがスクロールを停止して消費されたため、2回クリックする必要があります。リストがほとんど動いていない場合でも。
これまでに試したこと
最初は、RecyclerView
内にタッチイベントを消費しないように指示する何らかのフラグがあるはずだと思いました。私はAndroidソースコードを調べ、Androidのドキュメントを介してルーティングしましたが、役に立ちませんでした。
次に、onInterceptTouchEvent
を使用してタッチイベントをキャプチャし、タッチされていることを自分のビューに手動で伝えようとしました。
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
View myCell = recyclerView.findChildViewUnder(event.getX(), event.getY());
myCell.dispatchTouchEvent(event);
return super.onInterceptTouchEvent(event);
}
ここで、dispatchTouchEvent
は、アダプターで設定した対応するタグがビューにあったとしても、何もしなかったようです。
私はコードマシンガンを使ってこの問題をあらゆる角度から撮影してみました。 requestDisallowInterceptTouchEvent
で遊んで、手動でonTouch
を呼び出し、RecyclerView
以降のリスナーのすべての方法をオーバーライドします。
何も動作していないようです。これにはエレガントな解決策があるはずだと私は確信しています、そして私は狂気への道に沿ったどこかで重要な詳細をすくい取りましたか?特に、Google独自のアプリはこのように動作するためです。
助けてください! :)ありがとう。
ソリューション
Jagoan Neonのおかげで解決策が見つかりました。使いやすさのために、私は彼の答えをカスタムRecyclerView
で囲みました。
public class MultiClickRecyclerView extends RecyclerView {
public MultiClickRecyclerView(Context context) {
super(context);
}
public MultiClickRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MultiClickRecyclerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN && this.getScrollState() == RecyclerView.SCROLL_STATE_SETTLING) {
this.stopScroll();
}
return super.onInterceptTouchEvent(event);
}
}
まず、OnItemTouchListenerを、おそらく次のようなグローバル変数として定義する必要があります。
RecyclerView.OnItemTouchListener mOnItemTouchListener = new RecyclerView.OnItemTouchListener() {
@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
if (e.getAction() == MotionEvent.ACTION_DOWN && rv.getScrollState() == RecyclerView.SCROLL_STATE_SETTLING) {
Log.d(TAG, "onInterceptTouchEvent: click performed");
rv.findChildViewUnder(e.getX(), e.getY()).performClick();
return true;
}
return false;
}
@Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {}
@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {}
};
なぜACTION_DOWNとSCROLL_STATE_SETTLINGなのですか?
ACTION_DOWNモーションイベントは、RecyclerViewでタッチを実行したときにトリガーされ、その正確な時点で、RecyclerViewはスクロールを停止しようとするため、スクロール状態をSCROLL_STATE_SETTLINGに変更します。そして、それはまさにあなたがクリックを実行したいときです。
そして、MotionEventを消費したことをRecyclerViewに伝えるために、trueを返すことを忘れないでください。そうしないと、performClick()が複数回呼び出される可能性があります。
それをonResumeでRecyclerViewに追加し、onPauseで削除すれば、すべて完了です;)
更新
FindChildViewUnderを実行しているときにnullチェッカーを追加します。特定のセルに触れていないときはnullを返します。
View childView = rv.findChildViewUnder(e.getX(), e.getY());
if (childView != null) childView.performClick();
アップデート2
RecyclerViewのスクロールを停止するだけで十分であることがわかります。代わりにこれを行ってください:
@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
if (e.getAction() == MotionEvent.ACTION_DOWN && rv.getScrollState() == RecyclerView.SCROLL_STATE_SETTLING)
rv.stopScroll();
return false;
}