In meiner Anwendung muss ich sowohl Verschiebungs- als auch Klickereignisse verarbeiten.
Ein Klick ist eine Folge von einer ACTION_DOWN-Aktion, mehreren ACTION_MOVE-Aktionen und einer ACTION_UP-Aktion. Wenn Sie theoretisch ein ACTION_DOWN-Ereignis und dann ein ACTION_UP-Ereignis erhalten, bedeutet dies, dass der Benutzer gerade auf Ihre Ansicht geklickt hat.
In der Praxis funktioniert diese Sequenz jedoch auf einigen Geräten nicht. Auf meinem Samsung Galaxy Gio erhalte ich solche Sequenzen, wenn ich einfach auf meine Ansicht klicke: ACTION_DOWN, mehrmals ACTION_MOVE, dann ACTION_UP. Dh ich erhalte einige unerwartete OnTouchEvent-Zündungen mit dem Aktionscode ACTION_MOVE. Ich bekomme nie (oder fast nie) die Sequenz ACTION_DOWN -> ACTION_UP.
Ich kann OnClickListener auch nicht verwenden, da es nicht die Position des Klicks angibt. Wie kann ich also ein Klickereignis erkennen und vom Verschieben unterscheiden?
quelle
Antworten:
Hier ist eine weitere Lösung, die sehr einfach ist und bei der Sie sich keine Sorgen machen müssen, dass der Finger bewegt wird. Wenn Sie einen Klick einfach auf die zurückgelegte Entfernung stützen, wie können Sie dann einen Klick und einen langen Klick unterscheiden?
Sie könnten mehr Intelligenz in diese Sache stecken und die zurückgelegte Entfernung einbeziehen, aber ich bin noch nicht auf eine Instanz gestoßen, bei der die Entfernung, die ein Benutzer in 200 Millisekunden zurücklegen kann, eine Bewegung im Gegensatz zu einem Klick darstellen sollte.
setOnTouchListener(new OnTouchListener() { private static final int MAX_CLICK_DURATION = 200; private long startClickTime; @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: { startClickTime = Calendar.getInstance().getTimeInMillis(); break; } case MotionEvent.ACTION_UP: { long clickDuration = Calendar.getInstance().getTimeInMillis() - startClickTime; if(clickDuration < MAX_CLICK_DURATION) { //click event has occurred } } } return true; } });
quelle
ViewConfiguration.getLongPressTimeout()
als maximale Dauer für einen Klick verwenden?Ich habe die besten Ergebnisse erzielt, indem ich Folgendes berücksichtigt habe:
ACTION_DOWN
undACTION_UP
Ereignissen. Ich wollte den maximal zulässigen Abstand in dichteunabhängigen Pixeln anstelle von Pixeln angeben , um verschiedene Bildschirme besser unterstützen zu können. Zum Beispiel 15 DP .Beispiel:
/** * Max allowed duration for a "click", in milliseconds. */ private static final int MAX_CLICK_DURATION = 1000; /** * Max allowed distance to move during a "click", in DP. */ private static final int MAX_CLICK_DISTANCE = 15; private long pressStartTime; private float pressedX; private float pressedY; @Override public boolean onTouchEvent(MotionEvent e) { switch (e.getAction()) { case MotionEvent.ACTION_DOWN: { pressStartTime = System.currentTimeMillis(); pressedX = e.getX(); pressedY = e.getY(); break; } case MotionEvent.ACTION_UP: { long pressDuration = System.currentTimeMillis() - pressStartTime; if (pressDuration < MAX_CLICK_DURATION && distance(pressedX, pressedY, e.getX(), e.getY()) < MAX_CLICK_DISTANCE) { // Click event has occurred } } } } private static float distance(float x1, float y1, float x2, float y2) { float dx = x1 - x2; float dy = y1 - y2; float distanceInPx = (float) Math.sqrt(dx * dx + dy * dy); return pxToDp(distanceInPx); } private static float pxToDp(float px) { return px / getResources().getDisplayMetrics().density; }
Die Idee hier ist dieselbe wie in Gems Lösung , mit folgenden Unterschieden:
Update (2015): Schauen Sie sich auch Gabriels fein abgestimmte Version davon an .
quelle
onTouchEvent()
fehlt ein Rückgabewert. Es wäre hilfreich , wenn Sie uns zeigen , was dort zu schreiben (return true;
?return false;
?return super.onTouchEvent(e);
?)Unter Joniks Führung habe ich eine etwas feinere Version erstellt, die sich nicht als Klick registriert, wenn Sie Ihren Finger bewegen und dann zur Stelle zurückkehren, bevor Sie loslassen:
Also hier ist meine Lösung:
/** * Max allowed duration for a "click", in milliseconds. */ private static final int MAX_CLICK_DURATION = 1000; /** * Max allowed distance to move during a "click", in DP. */ private static final int MAX_CLICK_DISTANCE = 15; private long pressStartTime; private float pressedX; private float pressedY; private boolean stayedWithinClickDistance; @Override public boolean onTouchEvent(MotionEvent e) { switch (e.getAction()) { case MotionEvent.ACTION_DOWN: { pressStartTime = System.currentTimeMillis(); pressedX = e.getX(); pressedY = e.getY(); stayedWithinClickDistance = true; break; } case MotionEvent.ACTION_MOVE: { if (stayedWithinClickDistance && distance(pressedX, pressedY, e.getX(), e.getY()) > MAX_CLICK_DISTANCE) { stayedWithinClickDistance = false; } break; } case MotionEvent.ACTION_UP: { long pressDuration = System.currentTimeMillis() - pressStartTime; if (pressDuration < MAX_CLICK_DURATION && stayedWithinClickDistance) { // Click event has occurred } } } } private static float distance(float x1, float y1, float x2, float y2) { float dx = x1 - x2; float dy = y1 - y2; float distanceInPx = (float) Math.sqrt(dx * dx + dy * dy); return pxToDp(distanceInPx); } private static float pxToDp(float px) { return px / getResources().getDisplayMetrics().density; }
quelle
Verwenden Sie den Detektor. Er funktioniert und wird beim Ziehen nicht angehoben
Feld:
private GestureDetector mTapDetector;
Initialisieren:
mTapDetector = new GestureDetector(context,new GestureTap());
Innere Klasse:
class GestureTap extends GestureDetector.SimpleOnGestureListener { @Override public boolean onDoubleTap(MotionEvent e) { return true; } @Override public boolean onSingleTapConfirmed(MotionEvent e) { // TODO: handle tap here return true; } }
auf Berührung:
@Override public boolean onTouch(View v, MotionEvent event) { mTapDetector.onTouchEvent(event); return true; }
Genießen :)
quelle
Um eine optimale Optimierung des Klickereignisses zu erzielen, müssen wir zwei Dinge berücksichtigen:
Eigentlich kombiniere ich die Logik von Stimsoni und Neethirajan
Also hier ist meine Lösung:
view.setOnTouchListener(new OnTouchListener() { private final int MAX_CLICK_DURATION = 400; private final int MAX_CLICK_DISTANCE = 5; private long startClickTime; private float x1; private float y1; private float x2; private float y2; private float dx; private float dy; @Override public boolean onTouch(View view, MotionEvent event) { // TODO Auto-generated method stub switch (event.getAction()) { case MotionEvent.ACTION_DOWN: { startClickTime = Calendar.getInstance().getTimeInMillis(); x1 = event.getX(); y1 = event.getY(); break; } case MotionEvent.ACTION_UP: { long clickDuration = Calendar.getInstance().getTimeInMillis() - startClickTime; x2 = event.getX(); y2 = event.getY(); dx = x2-x1; dy = y2-y1; if(clickDuration < MAX_CLICK_DURATION && dx < MAX_CLICK_DISTANCE && dy < MAX_CLICK_DISTANCE) Log.v("","On Item Clicked:: "); } } return false; } });
quelle
MAX_CLICK_DISTANCE
und die Entfernung etwas anders berechnet habe.setOnTouchListener()
nimmt anonyme Klassenreferenz. Wo die Variablen innerhalb der Klasse global sind. Ich denke, das wird funktionieren, wenn nicht jemandsetOnTouchListener()
mit einer anderen Instanz des Hörers erneut anruft.Mit Gil SH Antwort habe ich es verbessert, indem ich
onSingleTapUp()
eher implementiert alsonSingleTapConfirmed()
. Es ist viel schneller und klickt beim Ziehen / Verschieben nicht auf die Ansicht.GestureTap:
public class GestureTap extends GestureDetector.SimpleOnGestureListener { @Override public boolean onSingleTapUp(MotionEvent e) { button.performClick(); return true; } }
Verwenden Sie es wie:
final GestureDetector gestureDetector = new GestureDetector(getApplicationContext(), new GestureTap()); button.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { gestureDetector.onTouchEvent(event); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: return true; case MotionEvent.ACTION_UP: return true; case MotionEvent.ACTION_MOVE: return true; } return false; } });
quelle
Der folgende Code löst Ihr Problem
quelle
Es ist sehr schwierig, dass ein ACTION_DOWN auftritt, ohne dass ein ACTION_MOVE auftritt. Das geringste Zucken Ihres Fingers auf dem Bildschirm an einer anderen Stelle als an der Stelle, an der die erste Berührung stattgefunden hat, löst das MOVE-Ereignis aus. Ich glaube auch, dass eine Änderung des Fingerdrucks auch das MOVE-Ereignis auslösen wird. Ich würde eine if-Anweisung in der Action_Move-Methode verwenden, um zu versuchen, die Entfernung zu bestimmen, in der die Bewegung von der ursprünglichen DOWN-Bewegung entfernt war. Wenn die Bewegung außerhalb eines festgelegten Radius erfolgt, wird Ihre MOVE-Aktion ausgeführt. Es ist wahrscheinlich nicht die beste und ressourceneffiziente Methode, um das zu tun, was Sie versuchen, aber es sollte funktionieren.
quelle
Wenn Sie nur auf einen Klick reagieren möchten, verwenden Sie:
if (event.getAction() == MotionEvent.ACTION_UP) { }
quelle
Wenn Sie zusätzlich zu den obigen Antworten sowohl onClick- als auch Drag-Aktionen implementieren möchten, können Sie meinen folgenden Code verwenden. Nehmen Sie die Hilfe von @Stimsoni:
// assumed all the variables are declared globally; public boolean onTouch(View view, MotionEvent event) { int MAX_CLICK_DURATION = 400; int MAX_CLICK_DISTANCE = 5; switch (event.getAction()) { case MotionEvent.ACTION_DOWN: { long clickDuration1 = Calendar.getInstance().getTimeInMillis() - startClickTime; startClickTime = Calendar.getInstance().getTimeInMillis(); x1 = event.getX(); y1 = event.getY(); break; } case MotionEvent.ACTION_UP: { long clickDuration = Calendar.getInstance().getTimeInMillis() - startClickTime; x2 = event.getX(); y2 = event.getY(); dx = x2-x1; dy = y2-y1; if(clickDuration < MAX_CLICK_DURATION && dx < MAX_CLICK_DISTANCE && dy < MAX_CLICK_DISTANCE) { Toast.makeText(getApplicationContext(), "item clicked", Toast.LENGTH_SHORT).show(); Log.d("clicked", "On Item Clicked:: "); // imageClickAction((ImageView) view,rl); } } case MotionEvent.ACTION_MOVE: long clickDuration = Calendar.getInstance().getTimeInMillis() - startClickTime; x2 = event.getX(); y2 = event.getY(); dx = x2-x1; dy = y2-y1; if(clickDuration < MAX_CLICK_DURATION && dx < MAX_CLICK_DISTANCE && dy < MAX_CLICK_DISTANCE) { //Toast.makeText(getApplicationContext(), "item clicked", Toast.LENGTH_SHORT).show(); // Log.d("clicked", "On Item Clicked:: "); // imageClickAction((ImageView) view,rl); } else { ClipData clipData = ClipData.newPlainText("", ""); View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view); //Toast.makeText(getApplicationContext(), "item dragged", Toast.LENGTH_SHORT).show(); view.startDrag(clipData, shadowBuilder, view, 0); } break; } return false; }
quelle