[android] onTouchEvent ()에서 이동과 클릭을 구별하는 방법은 무엇입니까?

내 응용 프로그램에서 이동 및 클릭 이벤트를 모두 처리해야합니다.

클릭은 하나의 ACTION_DOWN 작업, 여러 ACTION_MOVE 작업 및 하나의 ACTION_UP 작업의 시퀀스입니다. 이론적으로 ACTION_DOWN 이벤트가 발생한 다음 ACTION_UP 이벤트가 발생하면 사용자가 방금보기를 클릭했음을 의미합니다.

그러나 실제로이 시퀀스는 일부 장치에서 작동하지 않습니다. 내 Samsung Galaxy Gio에서보기 : ACTION_DOWN, ACTION_MOVE, ACTION_UP을 여러 번 클릭하면 이러한 시퀀스가 ​​나타납니다. 즉, ACTION_MOVE 액션 코드로 예상치 못한 OnTouchEvent가 발생합니다. ACTION_DOWN-> ACTION_UP 시퀀스를 전혀 (또는 거의) 얻지 않습니다.

또한 클릭 위치를 제공하지 않기 때문에 OnClickListener를 사용할 수 없습니다. 그렇다면 클릭 이벤트를 감지하고 이동과 어떻게 다릅니 까?



답변

여기에 매우 간단하고 손가락이 움직이는 것에 대해 걱정할 필요가없는 또 다른 솔루션이 있습니다. 단순히 이동 한 거리만큼 클릭을 기반으로하는 경우 클릭과 긴 클릭을 어떻게 구별 할 수 있습니까?

여기에 더 많은 스마트를 넣고 이동 한 거리를 포함 할 수 있지만, 사용자가 200 밀리 초 안에 이동할 수있는 거리가 클릭이 아닌 이동을 구성해야하는 경우를 아직 만나지 못했습니다.

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;
    }
});


답변

다음을 고려하여 최상의 결과를 얻었습니다.

  1. 주로 와 이벤트 간에 이동 한 거리 입니다. 다른 화면을 더 잘 지원하기 위해 최대 허용 거리를 픽셀이 아닌 밀도 독립형 픽셀 로 지정하고 싶었습니다 . 예 : 15 DP .ACTION_DOWNACTION_UP
  2. 둘째, 이벤트 사이 의 기간 입니다. 1 초 는 최대로 보였습니다. (어떤 사람들은 아주 “엄청나게”, 즉 천천히 “클릭”합니다. 저는 여전히 그것을 인식하고 싶습니다.)

예:

/**
 * 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;
}

여기의 아이디어는 Gem의 솔루션 과 동일하지만 다음과 같은 차이점이 있습니다.

  • 이것은 두 점 사이 의 실제 유클리드 거리를 계산합니다 .
  • 이것은 px 대신 dp를 사용합니다.

업데이트 (2015) : Gabriel의 미세 조정 된이 .


답변

촬영 Jonik의 우위를 내가 손가락을 이동 한 후 놓아 전에 자리로 돌아갈 경우 클릭으로 등록하지 않는 약간 더 미세 조정 버전을 내장 :

그래서 여기 내 해결책이 있습니다.

/**
 * 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;
}


답변

감지기를 사용하면 작동하며 드래그시 상승하지 않습니다.

들:

private GestureDetector mTapDetector;

초기화 :

mTapDetector = new GestureDetector(context,new GestureTap());

이너 클래스 :

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;
}

즐겨 🙂


답변

클릭 이벤트 인식을 최적화하려면 다음 두 가지를 고려해야합니다.

  1. ACTION_DOWN과 ACTION_UP 사이의 시차입니다.
  2. 사용자가 터치 할 때와 손가락을 뗄 때의 x, y의 차이.

사실 저는 Stimsoni와 Neethirajan의 논리를 결합합니다.

그래서 여기 내 해결책이 있습니다.

        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;
        }
    });


답변

길 SH 응답을 사용하여, 나는 구현하여 개선 onSingleTapUp()보다는 onSingleTapConfirmed(). 훨씬 더 빠르며 드래그 / 이동하면보기를 클릭하지 않습니다.

GestureTap :

public class GestureTap extends GestureDetector.SimpleOnGestureListener {
    @Override
    public boolean onSingleTapUp(MotionEvent e) {
        button.performClick();
        return true;
    }
}

다음과 같이 사용하십시오.

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;
    }
});


답변

아래 코드는 문제를 해결할 것입니다

    @우세하다
        public boolean onTouchEvent (MotionEvent event) {
            switch (event.getAction ()) {
                case (MotionEvent.ACTION_DOWN) :
                    x1 = event.getX ();
                    y1 = event.getY ();
                    단절;
                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); //권리
                    그렇지 않으면 (dx == 0) move (5); //딸깍 하는 소리
                    그렇지 않으면 move (2); //왼쪽
                }
                그밖에
                {
                    if (dy> 0) move (3); // 아래로
                    그렇지 않으면 (dy == 0) move (5); //딸깍 하는 소리
                    그렇지 않으면 move (4); //쪽으로
                }
                }
            }
            true를 반환하십시오.
        }