Jak odróżnić move I click w onTouchEvent ()?
W mojej aplikacji muszę obsługiwać zarówno zdarzenia move, jak i click.
Kliknięcie jest sekwencją jednej akcji ACTION_DOWN, kilku akcji ACTION_MOVE i jednej akcji ACTION_UP. Teoretycznie, jeśli otrzymasz Zdarzenie ACTION_DOWN, a następnie zdarzenie ACTION_UP - oznacza to, że użytkownik właśnie kliknął Twój widok.
Ale w praktyce ta sekwencja nie działa na niektórych urządzeniach. Na moim Samsungu Galaxy Gio dostaję takie sekwencje po prostu klikając Mój widok: ACTION_DOWN, kilka razy ACTION_MOVE, a następnie ACTION_UP. Czyli dostaję kilka nieoczekiwanych uruchomień OnTouchEvent z kodem akcji ACTION_MOVE. Nigdy (lub prawie nigdy) nie dostaję sekwencji ACTION_DOWN - > ACTION_UP.
Nie mogę również użyć OnClickListener, ponieważ nie podaje pozycji kliknięcia. W jaki sposób mogę wykryć Zdarzenie kliknięcia i odróżnić je od ruchu?
10 answers
Oto inne rozwiązanie, które jest bardzo proste i nie wymaga martwienia się o ruch palca. Jeśli bazujesz kliknięcie jako po prostu odległość przesunięta, to jak możesz odróżnić kliknięcie i długie kliknięcie.
Możesz dodać więcej sprytu do tego i uwzględnić przesuniętą odległość, ale jeszcze nie natknąłem się na przypadek, w którym odległość, którą użytkownik może przenieść w 200 milisekund, powinna stanowić ruch, a nie kliknięcie.
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;
}
});
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-22 17:09:16
Najlepsze wyniki uzyskałem biorąc pod uwagę:
- przede wszystkim, odległość przesunięta między
ACTION_DOWN
iACTION_UP
zdarzenia. Chciałem określić maksymalną dozwoloną odległość w density-indepenent pixels zamiast pikseli, aby lepiej obsługiwać różne ekrany. Na przykład 15 DP. - wtórnie, Czas trwania pomiędzy wydarzeniami. jedna sekunda wydawało się dobre maksimum. (Niektórzy "klikają" dość "dokładnie", tzn. powoli; ja nadal chcę rozpoznaj to.)
Przykład:
/**
* 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;
}
Idea tutaj jest taka sama jak w rozwiązanie Gem , z tymi różnicami:
- to oblicza rzeczywistą odległość euklidesową między tymi dwoma punktami.
- używa dp zamiast px.
Aktualizacja (2015): sprawdź również dopracowaną wersję tego Gabriela.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:38
Biorąc prowadzenie Jonika zbudowałem nieco bardziej dopracowaną wersję, która nie rejestruje się jako kliknięcie, jeśli poruszysz palcem i wrócisz do miejsca przed puszczeniem:
Oto moje rozwiązanie:
/**
* 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;
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:22
Użyj detektora, działa i nie podniesie się w przypadku przeciągania
Pole:
private GestureDetector mTapDetector;
Initialize:
mTapDetector = new GestureDetector(context,new GestureTap());
Klasa wewnętrzna:
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;
}
}
OnTouch:
@Override
public boolean onTouch(View v, MotionEvent event) {
mTapDetector.onTouchEvent(event);
return true;
}
Enjoy:)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-01 14:05:15
Aby uzyskać jak najbardziej zoptymalizowane rozpoznawanie zdarzenia kliknięcia musimy wziąć pod uwagę 2 rzeczy:
- różnica czasu między ACTION_DOWN i ACTION_UP.
- różnica między x,y, gdy użytkownik dotyka i kiedy zwalnia palec.
Właściwie to łączę logikę podaną przez Stimsoniego i Neethirajana
Oto moje rozwiązanie:
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;
}
});
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-28 19:12:42
Używając Gil SH answer, ulepszyłem ją implementując onSingleTapUp()
zamiast onSingleTapConfirmed()
. Jest znacznie szybszy i nie kliknie widoku, jeśli zostanie przeciągnięty/przeniesiony.
GestureTap:
public class GestureTap extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onSingleTapUp(MotionEvent e) {
button.performClick();
return true;
}
}
Użyj go jak:
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;
}
});
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-08 23:53:34
Poniższy kod rozwiąże twój problem
@Override public boolean onTouchEvent(MotionEvent event) { switch(event.getAction()) { case(MotionEvent.ACTION_DOWN): x1 = event.getX(); y1 = event.getY(); break; case(MotionEvent.ACTION_UP): { x2 = event.getX(); y2 = event.getY(); dx = x2-x1; dy = y2-y1; if(Math.abs(dx) > Math.abs(dy)) { if(dx>0) move(1); //right else if (dx == 0) move(5); //click else move(2); //left } else { if(dy>0) move(3); // down else if (dy == 0) move(5); //click else move(4); //up } } } return true; }
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-20 14:39:16
Jest bardzo trudno, aby ACTION_DOWN wystąpiło bez action_move. Najmniejsze poruszenie palca na ekranie w innym miejscu niż miejsce pierwszego dotknięcia spowoduje wywołanie zdarzenia MOVE. Ponadto, wierzę, że zmiana nacisku palców spowoduje również zdarzenie MOVE. Użyłbym instrukcji if W metodzie Action_Move, aby spróbować określić odległość od pierwotnego ruchu w dół. jeśli ruch nastąpił poza określonym promieniem, Twój ruch akcja nastąpi. Prawdopodobnie nie jest to najlepszy, zasobooszczędny sposób, aby zrobić to, co starasz się, ale powinien działać.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-01 15:46:06
Jeśli chcesz reagować tylko na kliknięcie, użyj:
if (event.getAction() == MotionEvent.ACTION_UP) {
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-11 10:27:01
Dodając do powyższych odpowiedzi, jeśli chcesz zaimplementować zarówno akcje onClick, jak i Drag, mój kod poniżej możesz. @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;
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-02 06:11:32