如何在Android中实现双指双击?

7
我知道如何检测双击和双指触摸事件,但是如何结合它们以响应需要用两个手指双击的情况呢?
Android 默认有长按来充当第二种点击形式,但我特别想要实现双指双击。
2个回答

16

我希望有一个简单且可重用的界面,监听两个手指的双击并行为类似于GestureDetector。这样你就可以像这样使用它(所有剪贴和可运行的代码):

public class Example extends Activity {
    SimpleTwoFingerDoubleTapDetector multiTouchListener = new SimpleTwoFingerDoubleTapDetector() {
        @Override
        public void onTwoFingerDoubleTap() {
            // Do what you want here, I used a Toast for demonstration
            Toast.makeText(Example.this, "Two Finger Double Tap", Toast.LENGTH_SHORT).show();
        }
    };

    // Override onCreate() and anything else you want

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if(multiTouchListener.onTouchEvent(event))
            return true;
        return super.onTouchEvent(event);
    }
}

我创建了一个名为SimpleTwoFingerDoubleTapDetector的东西。(虽然很长,但它很描述性。你可以将其重命名为任何你想要的名称。) 将这个新文件保存在你的项目中或作为库使用:

public abstract class SimpleTwoFingerDoubleTapDetector {
    private static final int TIMEOUT = ViewConfiguration.getDoubleTapTimeout() + 100;
    private long mFirstDownTime = 0;
    private boolean mSeparateTouches = false;
    private byte mTwoFingerTapCount = 0;

    private void reset(long time) {
        mFirstDownTime = time;
        mSeparateTouches = false;
        mTwoFingerTapCount = 0;
    }

    public boolean onTouchEvent(MotionEvent event) {
        switch(event.getActionMasked()) {
        case MotionEvent.ACTION_DOWN:
            if(mFirstDownTime == 0 || event.getEventTime() - mFirstDownTime > TIMEOUT) 
                reset(event.getDownTime());
            break;
        case MotionEvent.ACTION_POINTER_UP:
            if(event.getPointerCount() == 2)  
                mTwoFingerTapCount++;
            else 
                mFirstDownTime = 0;
            break;
        case MotionEvent.ACTION_UP:
            if(!mSeparateTouches)
                mSeparateTouches = true;
            else if(mTwoFingerTapCount == 2 && event.getEventTime() - mFirstDownTime < TIMEOUT) {
                onTwoFingerDoubleTap();
                mFirstDownTime = 0;
                return true;
            }
        }               

        return false;
    }

    public abstract void onTwoFingerDoubleTap();
}

首先,关于Android(单触)GestureDetector的一些注释:

  • Android的onDoubleTap()事件使用ViewConfiguration中的标准超时值。我指的是相同时间。
  • 他们测量从第一个点击的手指按下事件到第二个点击的手指按下事件所经过的时间,然后广播onDoubleTap()onDoubleTapEvent()
    • onDoubleTap()仅在第二个点击的手指按下事件发生时触发。
    • onDoubleTapEvent()为第二次操作(按下、移动和松开)的每个动作触发。

有关SimpleTwoFingerDoubleTapDetector的一些注释:

  • 我的超时时间是从第一个手指按下事件到最后一个手指抬起事件测量的,以防止误报双击通知。我将默认的ViewConfiguration双击超时时间增加了一点额外时间来解决这个问题。
  • Android的GestureDetector测量slop(两个点击之间的距离)。我认为这里不需要这样做,也没有检查每次点击时两个手指之间的距离。
  • 我只广播一个事件onTwoFingerDoubleTap()

最后说明: 您可以轻松将其更改为像OnTouchListener一样的行为:

  1. 更改SimpleTwoFingerDoubleTapDetector的定义:

  2. public abstract class SimpleTwoFingerDoubleTapListener implements OnTouchListener {
    
  3. 添加一个新的类变量:

  4. private View mFirstView;
    
  5. 修改 ACTION_DOWN 代码块:

    case MotionEvent.ACTION_DOWN:
        if(mFirstDownTime == -1 || mFirstView != v || hasTimedOut(event.getEventTime())) {
            mFirstView = v;
            reset(event.getDownTime());
        }
        break;
    
  6. ACTION_UP 事件中传递 mFirstView

  7. onTwoFingerDoubleTap(mFirstView);
    
  8. 最后,更改onTwoFingerDoubleTap()方法以反映被点击的是哪个View:

  9. public abstract void onTwoFingerDoubleTap(View v);
    

这个答案太棒了! - dowi
4
我已经尝试了这段代码(谢谢!),但它没有起作用——我只得到了 ACTION_DOWN 事件。我相信 onTouchEvent() 函数必须返回 True(对于 ACTION_DOWN),否则之后就收不到 ACTION_UP 事件了。参考:https://dev59.com/W2Uo5IYBdhLWcg3wvBmX#16495363 - FuzzyAmi
谢谢,即使它没有起作用。它帮助我找到了正确的答案。 - KeepAtIt

1

这是我创建的双击监听器,用于检测两个手指的双击。

使用的变量:

private GestureDetector gesture;
private View.OnTouchListener gestureListener;
boolean click1 = false;
boolean click2 = false;
long first = 0;
long second = 0;

在活动的onCreate()中注册触摸事件:
gesture = new GestureDetector(getApplicationContext(), new SimpleOnGestureListener(){
    public boolean onDown(MotionEvent event) {
        return true;
    }
});
gestureListener = new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        return gesture.onTouchEvent(event);
    }
};

在活动中的onCreate()之外:

@Override
public boolean onTouchEvent(MotionEvent event) {   
    try {
        int action = event.getAction() & MotionEvent.ACTION_MASK;
        //capture the event when the user lifts their fingers, not on the down press
        //to make sure they're not long pressing
        if (action == MotionEvent.ACTION_POINTER_UP) {
            //timer to get difference between clicks
            Calendar now = Calendar.getInstance();

            //detect number of fingers, change to 1 for a single-finger double-click, 3 for a triple-finger double-click, etc.
            if (event.getPointerCount() == 2) {
                if (!click1) {
                    //if this is the first click, then there hasn't been a second
                    //click yet, also record the time
                    click1 = true;
                    click2 = false;
                    first = now.getTimeInMillis(); 
                } else if (click1) {
                    //if this is the second click, record its time 
                    click2 = true;
                    second = now.getTimeInMillis();

                    //if the difference between the 2 clicks is less than 500 ms (1/2 second)
                    //Math.abs() is used because you need to be able to detect any sequence of clicks, rather than just in pairs of two
                    //(e.g. click1 could be registered as a second click if the difference between click1 and click2 > 500 but
                    //click2 and the next click1 is < 500)
                    if (Math.abs(second-first) < 500) {

                        //do something!!!!!!

                    } else if (Math.abs(second-first) >= 500) {
                        //reset to handle more clicks
                        click1 = false;
                        click2 = false;
                    }
                }
            }
        }
    } catch (Exception e){

    }
    return true;
}

如果 (click1 && click2 && Math.abs(second-first) < 500) {...} 是过度设计。除非 click1 && click2 已经为真,否则无法到达代码的这一部分。要到达此处,您必须通过此处:if (click1) { click2 = true; ... }。可以使用以下方式实现相同的效果:if (Math.abs(second-first) < 500) { //do something!!!!!! } else {click1 = false; click2 = false;} - James Newton
@Nezam 我已经好几年没用这段代码了,但我不确定为什么会出现这种情况——在我们的应用程序中它完全正常。 - WOUNDEDStevenJones

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接