Wykrywanie długiego naciśnięcia w systemie Android


Obecnie używam
onTouchEvent(MotionEvent event){
}

aby wykryć, kiedy użytkownik kliknie mój glSurfaceView, czy istnieje sposób na wykrycie, kiedy następuje długie kliknięcie. Myślę, że jeśli nie mogę znaleźć zbyt wiele w dokumentacji deweloperskiej, to będzie to jakieś obejście. Coś jak rejestracja ACTION_DOWN i zobaczenie, ile czasu minie, zanim ACTION_UP.
Jak wykryć długie naciśnięcia na Androidzie za pomocą OpenGL-es?
Zaproszony:
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:


GestureDetector

to najlepsze rozwiązanie.
Oto interesująca alternatywa. W

onTouchEvent

na każdym

ACTION_DOWN

zaplanuj uruchomienie obiektu w 1 sekundę. Na każdym

ACTION_UP

lub

ACTION_MOVE

anulować zaplanowane wykonanie. Jeśli odwołanie nastąpi krócej niż 1 sekundę od wydarzenia

ACTION_DOWN

Runnable nie będzie działać.
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);
}
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Spróbuj tego:
final GestureDetector gestureDetector = new GestureDetector(new GestureDetector.SimpleOnGestureListener() {
public void onLongPress(MotionEvent e) {
Log.e("", "Longpress detected");
}
});public boolean onTouchEvent(MotionEvent event) {
return gestureDetector.onTouchEvent(event);
};
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Mam kod, który wykrywa kliknięcie, długie kliknięcie i ruch.
To spora kombinacja powyższej odpowiedzi i zmian, które wprowadziłem, patrząc na każdą stronę dokumentacji.
//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;
}

Potwierdzam, że działa tak, jak użyłem go we własnej aplikacji.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Kiedy masz na myśli kliknięcia użytkowników, czy masz na myśli kliknięcie? Kliknięcie występuje, gdy użytkownik naciska, a następnie natychmiast podnosi palec. Dlatego obejmuje dwa zdarzenia onTouch. Powinieneś nadal używać onTouchEvent do rzeczy, które zdarzają się przy pierwszym dotknięciu lub po wydaniu.
Musisz więc użyć onClickListener, jeśli jest to kliknięcie.
Twoja odpowiedź jest podobna: użyj onLongClickListener.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

ja stworzyłem

skrawek
https://gist.github.com/Benjoy ... 2a26f
- Zainspirowany rzeczywistym źródłem widoku - które niezawodnie wykrywa długie kliknięcia/stuknięcia z niestandardowym opóźnieniem. Ale to jest w 28 roku:
val LONG_PRESS_DELAY = 500val handler = Handler()
var boundaries: Rect? = nullvar 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
}
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Rozwiązanie MSquare działa tylko wtedy, gdy trzymasz określony piksel, ale jest to nieuzasadnione oczekiwanie dla użytkownika końcowego, jeśli nie używa myszy (czego nie robi, używa palców).
Więc dodałem mały próg odległości między akcjami DOWN i UP na wypadek, gdyby między nimi była akcja MOVE.
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);
}
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Chodzi o to, aby utworzyć
Runnable
, aby wykonać długie kliknięcie w przyszłości, ale to wykonanie może zostać anulowane przez kliknięcie lub ruch.
Musisz także wiedzieć, kiedy długie kliknięcie zostało zużyte, a kiedy zostało anulowane, ponieważ palec poruszył się zbyt mocno. Używamy
initialTouchX
& amp;
initialTouchY
, aby przetestować wyjście użytkownika z obszaru kwadratu o wielkości 10 pikseli, po 5 z każdej strony.
Oto mój kompletny kod do delegacji

Click & LongClick

od
Cell
do
ListView
do
Activity
z
OnTouchListener
:
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
to
interfejs
służący do wysyłania zdarzeń kliknięcia do klasy modułu obsługi jako
Activity
public interface ClickDelegate {
boolean onItemClick(int position);
boolean onItemLongClick(int position);
}

Wszystko, co musisz zrobić, to zaimplementować to w swoim
Activity
lub nadrzędnym
View
, jeśli chcesz delegować zachowanie:
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
}
}
}
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

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;
}
});
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Oto podejście oparte na dobrym pomyśle MSquare, polegającym na wykrywaniu długiego naciśnięcia przycisku, który ma dodatkową funkcję: nie tylko operacja jest wykonywana w odpowiedzi na długie naciśnięcie, ale operacja jest powtarzana aż do komunikatu MotionEvent.ACTION_UP jest otrzymywany. W tym przypadku długie naciśnięcie i krótkie naciśnięcie są takie same, ale mogą się różnić.
Zwróć uwagę, że, jak donoszą inni, usunięcie wywołania zwrotnego w odpowiedzi na komunikat MotionEvent.ACTION_MOVE uniemożliwiło wykonanie wywołania zwrotnego, ponieważ nie mogłem wystarczająco trzymać palca. Omówiłem ten problem, ignorując tę ​​wiadomość.
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");
}
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

opcja: wykrywacz niestandardowy
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);
}

posługiwać się
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;
}

Gdzie
  • TOLERANCE
    is the maximum tolerated movement
  • DOWN_PERIOD
    is the time one has to press

import
static android.view.MotionEvent.ACTION_MOVE;
static android.view.MotionEvent.ACTION_DOWN;

W kodzie
setOnTouchListener(new Long_hold()
{
protected@Override boolean
long_hold(MotionEvent touch)
{
/*your code on long hold*/
}
});
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Znalazłem jedno rozwiązanie i nie wymaga definiowania działających ani innych rzeczy i działa dobrze.
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
}
} }

Aby odpowiedzieć na pytania, Zaloguj się lub Zarejestruj się