如何区分onTouchEvent()中的move和click?

Ana*_*sia 70 android event-handling touch-event android-event

在我的应用程序中,我需要处理移动和单击事件.

单击是一个ACTION_DOWN操作的序列,几个ACTION_MOVE操作和一个ACTION_UP操作.理论上,如果您获得ACTION_DOWN事件然后获得ACTION_UP事件 - 这意味着用户刚刚单击了您的视图.

但实际上,这个序列在某些设备上不起作用.在我的三星Galaxy Gio上,只需单击我的视图:ACTION_DOWN,几次ACTION_MOVE,然后ACTION_UP即可获得此类序列.即我使用ACTION_MOVE动作代码获得了一些无法预料的OnTouchEvent激活.我从未(或几乎从不)获得序列ACTION_DOWN - > ACTION_UP.

我也不能使用OnClickListener,因为它没有给出点击的位置.那么如何检测点击事件并将其与移动区分开来呢?

Sti*_*oni 103

这是另一种非常简单的解决方案,不需要担心手指被移动.如果您只是根据移动的距离进行点击,那么如何区分点击和长按.

您可以将更多的智能放入其中并包括移动的距离,但是当用户可以在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;
    }
});
Run Code Online (Sandbox Code Playgroud)

  • 实际上,最好使用event.getEventTime() - event.getDownTime()来计算点击持续时间 (8认同)
  • @RestInPeace,因为您不需要有一个单独的字段并使用日历.您只需要从另一个数字中减去一个数字. (4认同)
  • 对于长按,您绝对可以使用该方法,但这只是标准点击。 (2认同)

Jon*_*nik 49

考虑到以下因素,我得到了最好的结果:

  1. 主要是距离ACTION_DOWNACTION_UP事件之间的距离.我想指定密度不依赖像素而不是像素的最大允许距离,以更好地支持不同的屏幕.例如,15 DP.
  2. 其次,事件之间的持续时间.一秒似乎是最好的.(有些人"点击"相当"彻底",即缓慢;我仍然想要认识到这一点.)

例:

/**
 * 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;
}
Run Code Online (Sandbox Code Playgroud)

这里的想法与Gem的解决方案相同,但存在以下差异:

更新(2015年):还可以查看Gabriel的微调版本.

  • 哦,您的`onTouchEvent()`缺少返回值。如果您向我们展示在此处写什么,将会很有帮助(`return true;`?`return false;`?`return super.onTouchEvent(e);`?) (2认同)

Gab*_*iel 27

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;
}
Run Code Online (Sandbox Code Playgroud)


Gil*_* SH 17

使用探测器,它可以工作,并且在拖动的情况下不会升起

领域:

private GestureDetector mTapDetector;
Run Code Online (Sandbox Code Playgroud)

初始化:

mTapDetector = new GestureDetector(context,new GestureTap());
Run Code Online (Sandbox Code Playgroud)

内班:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

onTouch:

@Override
public boolean onTouch(View v, MotionEvent event) {
    mTapDetector.onTouchEvent(event);
    return true;
}
Run Code Online (Sandbox Code Playgroud)

请享用 :)


Gem*_*Gem 6

要获得最佳的点击事件识别我们必须考虑两件事:

  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;
        }
    });
Run Code Online (Sandbox Code Playgroud)


Hus*_*eky 6

使用 Gil SH 答案,我通过实施onSingleTapUp()而不是onSingleTapConfirmed(). 它要快得多,如果拖动/移动也不会单击视图。

手势点击:

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

像这样使用它:

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;
    }
});
Run Code Online (Sandbox Code Playgroud)