Considérez le schéma suivant ci-dessous (pour une meilleure compréhension de mon problème) .
Comme vous pouvez le constater, j’envisage une vue en liste entourée d’un remplissage. À présent, si un utilisateur appuie sur un élément listview, l’action que j’ai fournie lui fournit une couleur d’arrière-plan bleu clair. Maintenant, mon application traite elle-même des événements onTouch pour déterminer des actions telles que
Voici mon code.
public boolean onTouch(View v, MotionEvent event) {
if(v == null)
{
mSwipeDetected = Action.None;
return false;
}
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
downX = event.getRawX();
downY = event.getRawY();
mSwipeDetected = Action.Start;
// Find the child view that was touched (perform a hit test)
Rect rect = new Rect();
int childCount = listView.getChildCount();
int[] listViewCoords = new int[2];
listView.getLocationOnScreen(listViewCoords);
int x = (int) event.getRawX() - listViewCoords[0];
int y = (int) event.getRawY() - listViewCoords[1];
View child;
for (int i = 0; i < childCount; i++) {
child = listView.getChildAt(i);
child.getHitRect(rect);
if (rect.contains(x, y)) {
mDownView = child;
break;
}
}
return false; // allow other events like Click to be processed
}
case MotionEvent.ACTION_MOVE: {
upX = event.getRawX();
upY = event.getRawY();
float deltaX=0,deltaY=0;
deltaX = downX - upX;
deltaY = downY - upY;
if(deltaY < VERTICAL_MIN_DISTANCE)
{
setTranslationX(mDownView, -(deltaX));
setAlpha(mDownView, Math.max(0f, Math.min(1f, 1f - 2f * Math.abs(deltaX) / listView.getWidth())));
return false;
}
else
{
forceBringBack(v);
}
return false;
}
case MotionEvent.ACTION_UP:
{
stopX = event.getX();
float stopValueY = event.getRawY() - downY;
float stopValue = stopX - downX;
if(!mDownView.isPressed())
{
forceBringBack(mDownView);
return false;
}
boolean dismiss = false;
boolean dismissRight = false;
if(Math.abs(stopValue)<10)
{
mSwipeDetected = Action.Start;
}
else
{
mSwipeDetected = Action.None;
}
String log = "";
Log.d(log, "Here is Y" + Math.abs(stopValueY));
Log.d(log, "First Comparison of Stop Value > with/4" + (Math.abs(stopValue) > (listView.getWidth() /4)));
Log.d(log, "Second Comparison " + (Math.abs(stopValueY)<VERTICAL_MIN_DISTANCE));
Log.d(log, "Action Detected is " + mSwipeDetected + " with Stop Value " + stopValue);
if((Math.abs(stopValue) > (listView.getWidth() /4))&&(Math.abs(stopValueY)<VERTICAL_MIN_DISTANCE))
{
dismiss = true;
dismissRight = stopValue > 0;
if(stopValue>0)
{
mSwipeDetected = Action.LR;
}
else
mSwipeDetected = Action.RL;
}
Log.d(log, "Action Detected is " + mSwipeDetected + " with Stop Value after dissmiss" + stopValue);
if(dismiss)
{
if(dismissRight)
mSwipeDetected = Action.LR;
else
mSwipeDetected = Action.RL;
animate(mDownView)
.translationX(dismissRight ? listView.getWidth() : - listView.getWidth())
.alpha(0)
.setDuration(mAnimationTime)
.setListener(new AnimatorListenerAdapter() {
public void onAnimationEnd(Animator animation)
{
}
});
}
else
{
animate(mDownView)
.translationX(0)
.alpha(1)
.setDuration(mAnimationTime)
.setListener(null);
}
break;
}
}
return false;
}
Comme vous pouvez le constater, je détermine l'action effectuée dans MotionEvent.ACTION_UP et définit la valeur de Enum Action en conséquence. Cette logique fonctionne comme un charme si l'utilisateur ne franchit pas la limite de la vue liste.
Désormais, si l'utilisateur glisse (ou spécifiquement), déplace son doigt le long de l'élément de liste, passe du bleu à l'orange, MotionEvent.ACTION_UP ne sera pas attribué à listview, ce qui empêchera mon code de prendre une décision et à cause de la traductionX () et setAlpha (), puisqu’aucune action n’est déterminée dans ce cas, cet élément de la liste devient vide.
Le problème ne s'arrête pas là, car je ne gonfle pas la vue à chaque fois, la même ligne translateX () est gonflée à chaque fois, ce qui entraîne la multiplication d'un élément de liste vide/blanche.
Y a-t-il quelque chose à faire pour que même si je ne rencontrais pas MotionEvent.ACTION_UP, je puisse toujours prendre une décision?
Merci.
Vous devriez return true;
dans case MotionEvent.ACTION_DOWN:
, pour que le MotionEvent.ACTION_UP
soit traité.
Comme expliqué sur View.OnTouchListener :
Résultats:
True si l'auditeur a consommé l'événement, false sinon.
MotionEvent.ACTION_UP
Ne sera pas appelé avant que le MotionEvent.ACTION_DOWN
se soit produit, une explication logique à cela est qu'il est impossible pour un ACTION_UP
de se produire si un ACTION_DOWN
ne s'est jamais produit auparavant.
Cette logique permet au développeur de bloquer d'autres événements après ACTION_DOWN
.
Notez également que dans certaines circonstances (rotation d'écran, par exemple), le geste peut être annulé, auquel cas un MotionEvent.ACTION_UP
ne sera PAS envoyé. À la place, un MotionEvent.ACTION_CANCEL
est envoyé. Par conséquent, une instruction de commutateur d'action normale devrait ressembler à ceci:
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
// check if we want to handle touch events, return true
// else don't handle further touch events, return false
break;
// ... handle other cases
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
// finish handling touch events
// note that these methods won't be called if 'false' was returned
// from any previous events related to the gesture
break;
}
Je ne pense pas que l'ajout de return true;
à la casse MotionEvent.ACTION_DOWN:
finirait par résoudre le problème. Cela compliquait simplement la situation où return false
aurait pu faire le travail comme un charme.
Ce qu'il faut remarquer, c'est que MotionEvent.ACTION_DOWN: /*something*/ return true;
bloquera tous les autres rappels d'écouteur disponibles pour la vue, même onClickListenerm, alors que correctement return false
dans MotionEvent.ACTION_UP:
peut aider à propager l'événement MotionEvent à la bonne destination.
Référence à son code source original: https://github.com/romannurik/Android-swipetodismiss
Comme Danpe a expliqué dans sa réponse concise - J'ai dû ajouter le code ACTION_DOWN afin de faire reconnaître ACTION_UP.
case MotionEvent.ACTION_DOWN:
return true;
case MotionEvent.ACTION_UP:
XyPos xyPos = new XyPos();
xyPos.x = last_x;
xyPos.y = last_y;
handleViewElementPositionUpdate(xyPos);
break;
De toute façon, la méthode onTouch (..) entière est revenue, alors je ne suis pas sûre de savoir pourquoi cela ne suffisait pas ...