Problème
J'essaie de simuler le comportement exact de l'application Google Play.
Actuellement, lorsque vous faites défiler une catégorie comme "Top Games" et que vous touchez la liste, cette cellule ainsi que le RecyclerView
gère l'événement tactile, vous pouvez dire qu'ils le gèrent tous les deux en raison de l'ondulation apparaissant lorsque vous maintenez votre doigt vers le bas ainsi que le défilement s'arrêtant. Ou si vous cliquez sur une cellule alors que la liste est toujours en mouvement, le contenu est ouvert immédiatement.
Le comportement par défaut est que le défilement s'arrête instantanément si vous placez votre doigt vers le bas pendant qu'il se déplace, et qu'il consomme ensuite cet événement tactile, ce qui signifie que la cellule n'est jamais informée de ce toucher. Cela est extrêmement irritant lorsque le contenu principal de vos applications se trouve dans un RecyclerView
et si l'utilisateur fait continuellement défiler puis clique sur une cellule, il lui faut cliquer deux fois, car le premier clic a été consommé en arrêtant le défilement, même si la liste bougeait à peine.
Ce que j'ai essayé jusqu'à présent
Au début, je pensais qu'il devait y avoir une sorte de drapeau à l'intérieur du RecyclerView
qui lui dit de ne pas consommer l'événement tactile. J'ai parcouru le code source Android et parcouru la documentation Android en vain).
Ensuite, j'ai essayé de capturer l'événement tactile avec onInterceptTouchEvent
et de dire manuellement à ma vue qu'il est touché;
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
View myCell = recyclerView.findChildViewUnder(event.getX(), event.getY());
myCell.dispatchTouchEvent(event);
return super.onInterceptTouchEvent(event);
}
Ici, dispatchTouchEvent
ne semble rien faire même si la vue que je reçois a la balise correspondante que j'ai définie dans l'adaptateur.
J'ai essayé de résoudre ce problème avec ma mitrailleuse à code sous tous les angles; jouer avec requestDisallowInterceptTouchEvent
, appeler manuellement onTouch
et remplacer toutes sortes d'écouteurs pour RecyclerView
et au-delà.
Rien ne semble fonctionner. Je suis sûr qu'il doit y avoir une solution élégante à cela et j'ai survolé un détail important quelque part sur le chemin de la folie? Surtout parce que les propres applications de Google se comportent de cette manière.
Aidez-moi! :) Merci.
La solution
Grâce à Jagoan Neon, une solution a été trouvée. J'ai fini par envelopper sa réponse dans un RecyclerView
personnalisé pour en faciliter l'utilisation.
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);
}
}
Tout d'abord, vous devez définir un OnItemTouchListener, peut-être comme une variable globale comme celle-ci:
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) {}
};
Pourquoi ACTION_DOWN et SCROLL_STATE_SETTLING?
L'événement de mouvement ACTION_DOWN sera déclenché lorsque vous effectuez une touche sur le RecyclerView, et à ce moment précis, RecyclerView change son état de défilement en SCROLL_STATE_SETTLING alors qu'il essaie d'arrêter le défilement. Et c'est exactement là que vous souhaitez effectuer le clic.
Et n'oubliez pas de retourner vrai pour que vous disiez à votre RecyclerView que vous avez consommé le MotionEvent. Sinon, votre performClick () pourrait être appelé plusieurs fois.
Ajoutez-le à votre RecyclerView sur onResume et supprimez-le sur onPause, et vous êtes prêt;)
METTRE À JOUR
Ajoutez un vérificateur nul lorsque vous effectuez findChildViewUnder - il renvoie null lorsque vous ne touchez pas à une cellule spécifique.
View childView = rv.findChildViewUnder(e.getX(), e.getY());
if (childView != null) childView.performClick();
MISE À JOUR 2
Il s'avère que l'arrêt de RecyclerView du défilement suffirait. Faites ceci à la place:
@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
if (e.getAction() == MotionEvent.ACTION_DOWN && rv.getScrollState() == RecyclerView.SCROLL_STATE_SETTLING)
rv.stopScroll();
return false;
}