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

时间:2012-04-01 15:37:24

标签: android event-handling ontouchevent 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,因为它没有给出点击的位置。那么如何检测点击事件并将其与移动区分开来呢?

10 个答案:

答案 0 :(得分:92)

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

你可以把更多的智能放入其中并包括移动的距离,但是当用户在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 :(得分:49)

考虑到这一点,我得到了最好的结果:

  1. 主要是距离ACTION_DOWNACTION_UP事件之间移动。我想在density-indepenent pixels而不是像素中指定最大允许距离,以更好地支持不同的屏幕。例如, 15 DP
  2. 其次,事件之间的持续时间一秒似乎是最好的。 (有些人“点击”相当“彻底”,即缓慢;我仍然想要认识到这一点。)
  3. 示例:

    /**
     * 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's solution中的想法相同,但存在以下差异:

    更新(2015年):还可以查看Gabriel's fine-tuned version of this

答案 2 :(得分:26)

考虑Jonik's lead我构建了一个稍微精细的调整版本,如果你移动手指然后在放手之前返回现场,则不会注册为点击:

所以这是我的解决方案:

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

答案 3 :(得分:14)

使用探测器,它有效,拖动时不会升起

字段:

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

享受:)

答案 4 :(得分:6)

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

  1. ACTION_DOWN和ACTION_UP之间的时差。
  2. 用户触摸和释放手指时x,y之间的差异。
  3. 实际上我结合了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;
            }
        });
    

答案 5 :(得分:4)

下面的代码将解决您的问题


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

答案 6 :(得分:3)

如果没有发生ACTION_MOVE,ACTION_DOWN很难发生。屏幕上的最轻微抽搐在与第一次触摸发生位置不同的位置将触发MOVE事件。此外,我相信手指压力的变化也会触发MOVE事件。我会在Action_Move方法中使用if语句来尝试确定从原始DOWN运动开始移动的距离。如果移动发生在某个设定半径之外,则会发生MOVE操作。它可能不是最好的,资源有效的方式来做你尝试但它应该工作。

答案 7 :(得分:1)

如果您只想点击,请使用:

// remove the ball from its current parent
fewerColorsPowerupIcon.removeFromParent()

// add it the scene
self.addChild(fewerColorsPowerupIcon)

 // place the ball of the screen so that we can bring it on later
fewerColorsPowerupIcon.position = CGPointMake((width * -0.1) , (height * -0.1))

// set the size of the icon
fewerColorsPowerupIcon.xScale = scaleFactor
fewerColorsPowerupIcon.yScale = scaleFactor

答案 8 :(得分:1)

添加上述答案,如果你想同时实现onClick和Drag操作,那么下面我的代码可以。从@Stimsoni获得一些帮助:

try{
    ...
    datenbankverbindung = DriverManager.getConnection(link,connectionProperties);
}catch(SQLException e){
    ...//do whatever you want
}

答案 9 :(得分:1)

使用Gil 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;
    }
});