Ich benutze gerade
onTouchEvent(MotionEvent event){
}
Um zu erkennen, wann der Benutzer mein glSurfaceView drückt, gibt es eine Möglichkeit zu erkennen, wann ein langer Klick erfolgt. Ich vermute, wenn ich in den Entwicklungsdokumenten nicht viel finden kann, wird es eine Art Umgehungsmethode sein. So etwas wie ACTION_DOWN registrieren und sehen, wie lange es dauert, bis ACTION_UP.
Wie erkennt man lange Drücke auf Android mit opengl-es?
GestureDetector
Konstruktoren sind veraltet - Sie können den Rest der Konstruktoren verwenden.GestureDetector ist die beste Lösung.
Hier ist eine interessante Alternative. Planen Sie in onTouchEvent bei jedem ACTION_DOWN einen Runnable, der in 1 Sekunde ausgeführt werden soll. Brechen Sie bei jedem ACTION_UP oder ACTION_MOVE die geplante Ausführung ab. Wenn der Abbruch weniger als 1 Sekunde nach dem Ereignis ACTION_DOWN erfolgt, wird Runnable nicht ausgeführt.
final Handler handler = new Handler(); Runnable mLongPressed = new Runnable() { public void run() { Log.i("", "Long press!"); } }; @Override public boolean onTouchEvent(MotionEvent event, MapView mapView){ if(event.getAction() == MotionEvent.ACTION_DOWN) handler.postDelayed(mLongPressed, ViewConfiguration.getLongPressTimeout()); if((event.getAction() == MotionEvent.ACTION_MOVE)||(event.getAction() == MotionEvent.ACTION_UP)) handler.removeCallbacks(mLongPressed); return super.onTouchEvent(event, mapView); }
quelle
android.view.ViewConfiguration.getLongPressTimeout()
.Ich habe einen Code, der ein Klicken, ein langes Klicken und eine Bewegung erkennt. Es ist ziemlich eine Kombination aus der oben gegebenen Antwort und den Änderungen, die ich durch das Einblicken in jede Dokumentationsseite vorgenommen habe.
//Declare this flag globally boolean goneFlag = false; //Put this into the class final Handler handler = new Handler(); Runnable mLongPressed = new Runnable() { public void run() { goneFlag = true; //Code for long click } }; //onTouch code @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: handler.postDelayed(mLongPressed, 1000); //This is where my code for movement is initialized to get original location. break; case MotionEvent.ACTION_UP: handler.removeCallbacks(mLongPressed); if(Math.abs(event.getRawX() - initialTouchX) <= 2 && !goneFlag) { //Code for single click return false; } break; case MotionEvent.ACTION_MOVE: handler.removeCallbacks(mLongPressed); //Code for movement here. This may include using a window manager to update the view break; } return true; }
Ich bestätige, dass es funktioniert, da ich es in meiner eigenen Anwendung verwendet habe.
quelle
initialTouchX
irgendwo automatisch definiert? Ich bin verwirrt.Wenn Sie meinen, Benutzer drückt, meinen Sie einen Klick? Ein Klick ist, wenn der Benutzer nach unten drückt und dann sofort den Finger anhebt. Daher umfasst es zwei onTouch-Ereignisse. Sie sollten die Verwendung von onTouchEvent für Dinge speichern, die beim ersten Berühren oder nach der Veröffentlichung auftreten.
Daher sollten Sie onClickListener verwenden, wenn es sich um einen Klick handelt.
Ihre Antwort ist analog: Verwenden Sie onLongClickListener.
quelle
Ich habe ein Snippet erstellt, das von der eigentlichen View-Quelle inspiriert ist und lange Klicks / Druckvorgänge mit einer benutzerdefinierten Verzögerung zuverlässig erkennt. Aber es ist in Kotlin:
val LONG_PRESS_DELAY = 500 val handler = Handler() var boundaries: Rect? = null var onTap = Runnable { handler.postDelayed(onLongPress, LONG_PRESS_DELAY - ViewConfiguration.getTapTimeout().toLong()) } var onLongPress = Runnable { // Long Press } override fun onTouch(view: View, event: MotionEvent): Boolean { when (event.action) { MotionEvent.ACTION_DOWN -> { boundaries = Rect(view.left, view.top, view.right, view.bottom) handler.postDelayed(onTap, ViewConfiguration.getTapTimeout().toLong()) } MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { handler.removeCallbacks(onLongPress) handler.removeCallbacks(onTap) } MotionEvent.ACTION_MOVE -> { if (!boundaries!!.contains(view.left + event.x.toInt(), view.top + event.y.toInt())) { handler.removeCallbacks(onLongPress) handler.removeCallbacks(onTap) } } } return true }
quelle
Die Lösung von MSquare funktioniert nur, wenn Sie ein bestimmtes Pixel halten. Dies ist jedoch eine unangemessene Erwartung für einen Endbenutzer, es sei denn, er verwendet eine Maus (was er nicht tut, er verwendet Finger).
Also habe ich einen kleinen Schwellenwert für den Abstand zwischen der DOWN- und der UP-Aktion hinzugefügt, falls sich dazwischen eine MOVE-Aktion befindet.
final Handler longPressHandler = new Handler(); Runnable longPressedRunnable = new Runnable() { public void run() { Log.e(TAG, "Long press detected in long press Handler!"); isLongPressHandlerActivated = true; } }; private boolean isLongPressHandlerActivated = false; private boolean isActionMoveEventStored = false; private float lastActionMoveEventBeforeUpX; private float lastActionMoveEventBeforeUpY; @Override public boolean dispatchTouchEvent(MotionEvent event) { if(event.getAction() == MotionEvent.ACTION_DOWN) { longPressHandler.postDelayed(longPressedRunnable, 1000); } if(event.getAction() == MotionEvent.ACTION_MOVE || event.getAction() == MotionEvent.ACTION_HOVER_MOVE) { if(!isActionMoveEventStored) { isActionMoveEventStored = true; lastActionMoveEventBeforeUpX = event.getX(); lastActionMoveEventBeforeUpY = event.getY(); } else { float currentX = event.getX(); float currentY = event.getY(); float firstX = lastActionMoveEventBeforeUpX; float firstY = lastActionMoveEventBeforeUpY; double distance = Math.sqrt( (currentY - firstY) * (currentY - firstY) + ((currentX - firstX) * (currentX - firstX))); if(distance > 20) { longPressHandler.removeCallbacks(longPressedRunnable); } } } if(event.getAction() == MotionEvent.ACTION_UP) { isActionMoveEventStored = false; longPressHandler.removeCallbacks(longPressedRunnable); if(isLongPressHandlerActivated) { Log.d(TAG, "Long Press detected; halting propagation of motion event"); isLongPressHandlerActivated = false; return false; } } return super.dispatchTouchEvent(event); }
quelle
Die Idee ist es, eine
Runnable
in Zukunft langen Klick für die Ausführung zu Diese Ausführung kann jedoch aufgrund eines Klicks oder einer Verschiebung abgebrochen werden.Sie müssen auch wissen, wann ein langer Klick verbraucht wurde und wann er abgebrochen wird, weil sich der Finger zu stark bewegt hat. Wir verwenden
initialTouchX
&initialTouchY
um zu überprüfen, ob der Benutzer einen quadratischen Bereich von 10 Pixel, 5 auf jeder Seite, verlässt.Hier ist mein vollständiger Code zum Delegieren von Click & LongClick von
Cell
inListView
nachActivity
mitOnTouchListener
:ClickDelegate delegate; boolean goneFlag = false; float initialTouchX; float initialTouchY; final Handler handler = new Handler(); Runnable mLongPressed = new Runnable() { public void run() { Log.i("TOUCH_EVENT", "Long press!"); if (delegate != null) { goneFlag = delegate.onItemLongClick(index); } else { goneFlag = true; } } }; @OnTouch({R.id.layout}) public boolean onTouch (View view, MotionEvent motionEvent) { switch (motionEvent.getAction()) { case MotionEvent.ACTION_DOWN: handler.postDelayed(mLongPressed, ViewConfiguration.getLongPressTimeout()); initialTouchX = motionEvent.getRawX(); initialTouchY = motionEvent.getRawY(); return true; case MotionEvent.ACTION_MOVE: case MotionEvent.ACTION_CANCEL: if (Math.abs(motionEvent.getRawX() - initialTouchX) > 5 || Math.abs(motionEvent.getRawY() - initialTouchY) > 5) { handler.removeCallbacks(mLongPressed); return true; } return false; case MotionEvent.ACTION_UP: handler.removeCallbacks(mLongPressed); if (goneFlag || Math.abs(motionEvent.getRawX() - initialTouchX) > 5 || Math.abs(motionEvent.getRawY() - initialTouchY) > 5) { goneFlag = false; return true; } break; } Log.i("TOUCH_EVENT", "Short press!"); if (delegate != null) { if (delegate.onItemClick(index)) { return false; } } return false; }
ClickDelegate
ist eininterface
zum Senden von Klickereignissen an die Handlerklasse wie einActivity
public interface ClickDelegate { boolean onItemClick(int position); boolean onItemLongClick(int position); }
Und alles, was Sie brauchen, ist, es in Ihrem
Activity
oder einem Elternteil zu implementieren,View
wenn Sie das Verhalten delegieren müssen:public class MyActivity extends Activity implements ClickDelegate { //code... //in some place of you code like onCreate, //you need to set the delegate like this: SomeArrayAdapter.delegate = this; //or: SomeViewHolder.delegate = this; //or: SomeCustomView.delegate = this; @Override public boolean onItemClick(int position) { Object obj = list.get(position); if (obj) { return true; //if you handle click } else { return false; //if not, it could be another event } } @Override public boolean onItemLongClick(int position) { Object obj = list.get(position); if (obj) { return true; //if you handle long click } else { return false; //if not, it's a click } } }
quelle
setOnTouchListener(new View.OnTouchListener() { public boolean onTouch(View v, MotionEvent event) { int action = MotionEventCompat.getActionMasked(event); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: longClick = false; x1 = event.getX(); break; case MotionEvent.ACTION_MOVE: if (event.getEventTime() - event.getDownTime() > 500 && Math.abs(event.getX() - x1) < MIN_DISTANCE) { longClick = true; } break; case MotionEvent.ACTION_UP: if (longClick) { Toast.makeText(activity, "Long preess", Toast.LENGTH_SHORT).show(); } } return true; } });
quelle
Hier ist ein Ansatz, der auf der guten Idee von MSquare zum Erkennen eines langen Tastendrucks basiert und eine zusätzliche Funktion aufweist: Es wird nicht nur ein Vorgang als Reaktion auf einen langen Druck ausgeführt, sondern der Vorgang wird wiederholt, bis eine MotionEvent.ACTION_UP-Nachricht angezeigt wird empfangen. In diesem Fall sind die Aktionen für langes und kurzes Drücken gleich, können jedoch unterschiedlich sein.
Beachten Sie, dass, wie andere berichtet haben, das Entfernen des Rückrufs als Antwort auf eine MotionEvent.ACTION_MOVE-Nachricht verhinderte, dass der Rückruf jemals ausgeführt wurde, da ich meinen Finger nicht ruhig genug halten konnte. Ich habe dieses Problem umgangen, indem ich diese Nachricht ignoriert habe.
private void setIncrementButton() { final Button btn = (Button) findViewById(R.id.btn); final Runnable repeater = new Runnable() { @Override public void run() { increment(); final int milliseconds = 100; btn.postDelayed(this, milliseconds); } }; btn.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent e) { if (e.getAction() == MotionEvent.ACTION_DOWN) { increment(); v.postDelayed(repeater, ViewConfiguration.getLongPressTimeout()); } else if (e.getAction() == MotionEvent.ACTION_UP) { v.removeCallbacks(repeater); } return true; } }); } private void increment() { Log.v("Long Press Example", "TODO: implement increment operation"); }
quelle
Option: Benutzerdefinierter Detektor
class
abstract public class Long_hold extends View.OnTouchListener { public@Override boolean onTouch(View view, MotionEvent touch) { switch(touch.getAction()) { case ACTION_DOWN: down(touch); return true; case ACTION_MOVE: move(touch); } return true; } private long time_0; private float x_0, y_0; private void down(MotionEvent touch) { time_0= touch.getEventTime(); x_0= touch.getX(); y_0= touch.getY(); } private void move(MotionEvent touch) { if(held_too_short(touch) {return;} if(moved_too_much(touch)) {return;} long_press(touch); } abstract protected void long_hold(MotionEvent touch); }
verwenden
private double moved_too_much(MotionEvent touch) { return Math.hypot( x_0 -touch.getX(), y_0 -touch.getY()) >TOLERANCE; } private double held_too_short(MotionEvent touch) { return touch.getEventTime()-time_0 <DOWN_PERIOD; }
wo
TOLERANCE
ist die maximal tolerierte BewegungDOWN_PERIOD
ist die Zeit, die man drücken mussimport
static android.view.MotionEvent.ACTION_MOVE; static android.view.MotionEvent.ACTION_DOWN;
in Code
setOnTouchListener(new Long_hold() { protected@Override boolean long_hold(MotionEvent touch) { /*your code on long hold*/ } });
quelle
Ich habe eine Lösung gefunden und es ist nicht erforderlich, lauffähige oder andere Dinge zu definieren, und es funktioniert gut.
var lastTouchTime: Long = 0 // ( ViewConfiguration.#.DEFAULT_LONG_PRESS_TIMEOUT =500) val longPressTime = 500 var lastTouchX = 0f var lastTouchY = 0f view.setOnTouchListener { v, event -> when (event.action) { MotionEvent.ACTION_DOWN -> { lastTouchTime = SystemClock.elapsedRealtime() lastTouchX = event.x lastTouchY = event.y return@setOnTouchListener true } MotionEvent.ACTION_UP -> { if (SystemClock.elapsedRealtime() - lastTouchTime > longPressTime && Math.abs(event.x - lastTouchX) < 3 && Math.abs(event.y - lastTouchY) < 3) { Log.d(TAG, "Long press") } return@setOnTouchListener true } else -> { return@setOnTouchListener false } } }
quelle