【问题标题】:Android: Simultaneous button pressesAndroid:同时按下按钮
【发布时间】:2010-12-25 17:02:41
【问题描述】:

我已经在其他平台(尤其是 iPhone/iPad)上看到了这一点,但无法在 Android 上找到它/弄清楚它,而其他平台似乎并没有比提出这个问题更进一步.

我的游戏包含多人游戏组件,其中两名玩家必须同时按下同一屏幕上的按钮。除了同时按下按钮之外的所有操作都已完成。

到目前为止,Android 似乎调用了按钮的 onTouchEvent,并且由于它已被处理,因此它不会调用其他按钮。你认为让它同时调用两个按钮(如果触摸在按钮区域内)会起作用吗?有没有其他方法可以实现?

感谢您的宝贵时间。


回答者:
我最终在 Activity 的 onTouchEvent 函数中实现了两个不同的触摸点。

基本实现:

public class MainClass extends Activity { 
// ...
btn1 = (CustomButton)findViewById(R.id.button_one);
btn2 = (CustomButton)findViewById(R.id.button_one);
btn1.ignoreMotionEvent(true);
btn2.ignoreMotionEvent(true);
// ...

@Override
public boolean onTouchEvent(MotionEvent event)
{
    if(r1 == null)
    {
        //r1 and r2 are Rect that define the absolute region of the button.
        //They are both defined here to ensure that the everything will be layed out (didn't always work so just for extra "sure-ness" did it here)
    }
    CustomButton btn = null;
    MotionEvent mevent = null;
    switch(event.getAction())
    {
        case MotionEvent.ACTION_DOWN:
        case MotionEvent.ACTION_UP:
            int x = (int)event.getX();
            int y = (int)event.getY();
            if(r1.contains(x, y))
            {
                btn = btn1;
            }
            else if(r2.contains(x, y))
            {
                btn = btn2;
            }
            if(btn != null)
            {
                mevent = event;
            }
            break;
    }
    if(btn != null)
    {
        btn.ignoreMotionEvent(false);
        btn.onTouchEvent(mevent);
        btn.ignoreMotionEvent(true);
    }
    return super.onTouchEvent(event);
}
}

CustomButton 只是 Button 的扩展版本,其中存在方法 ignoreMotionEvent,这只是设置一个布尔值,确定 CustomButton 的 onTouchEvent 函数是否应立即返回 false 或调用 super.onTouchEvent。

不幸的是,如果您尝试了此代码,它将无法正常工作。那为什么要提呢?它是代码的基本轮廓。我遇到的问题是在 Android 1.6 及更早版本上不存在使这项工作的功能。完成这项工作需要 Eclair 和更高版本。

现在只是为了帮助任何其他发现此问题的人,因为他们遇到了同样的问题,这里几乎是我正在运行的完全相同的副本:

public class MainClass extends Activity { 
// ...
btn1 = (CustomButton)findViewById(R.id.button_one);
btn2 = (CustomButton)findViewById(R.id.button_one);
btn1.ignoreMotionEvent(true);
btn2.ignoreMotionEvent(true);
// ...

@Override
public boolean onTouchEvent(MotionEvent event)
{
    if(r1 == null)
    {
        //r1 and r2 are Rect that define the absolute region of the button.
        //They are both defined here to ensure that the everything will be layed out (didn't always work so just for extra "sure-ness" did it here)
    }
    if(Utils.isEclairOrLater())
    {
        //Eclair and later
        boolean froyo = Utils.isFroyoOrLater();
        int action = froyo ? event.getActionMasked() : event.getAction();
        CustomButton btn = null;
        MotionEvent mevent = null;
        switch(action & MotionEvent.ACTION_MASK)
        {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_UP:
                int x = (int)event.getX();
                int y = (int)event.getY();
                if(r1.contains(x, y))
                {
                    btn = btn1;
                }
                else if(r2.contains(x, y))
                {
                    btn = btn2;
                }
                if(btn != null)
                {
                    mevent = event;
                }
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
            case MotionEvent.ACTION_POINTER_UP:
                int index = froyo ? event.getActionIndex() : ((action & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT);
                x = (int)event.getX(index);
                y = (int)event.getY(index);
                if(r1.contains(x, y))
                {
                    btn = btn1;
                }
                else if(r2.contains(x, y))
                {
                    btn = btn2;
                }
                if(btn != null)
                {
                    mevent = MotionEvent.obtain(event.getDownTime(), event.getEventTime(), (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_DOWN ? MotionEvent.ACTION_DOWN : MotionEvent.ACTION_UP, event.getX(index), event.getY(index), 0);
                }
                break;
        }
        if(btn != null)
        {
            btn.ignoreMotionEvent(false);
            btn.onTouchEvent(mevent);
            btn.ignoreMotionEvent(true);
        }
    }
    else
    {
        //Earlier then Eclair
        CustomButton btn = null;
        MotionEvent mevent = null;
        switch(event.getAction())
        {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_UP:
                int x = (int)event.getX();
                int y = (int)event.getY();
                if(r1.contains(x, y))
                {
                    btn = btn1;
                }
                else if(r2.contains(x, y))
                {
                    btn = btn2;
                }
                if(btn != null)
                {
                    mevent = event;
                }
                break;
        }
        if(btn != null)
        {
            btn.ignoreMotionEvent(false);
            btn.onTouchEvent(mevent);
            btn.ignoreMotionEvent(true);
        }
    }
    return super.onTouchEvent(event);
}
}

Utils 是一个简单的实用程序类,就本示例而言,它获取 Build.VERSION.SDK_INT 值并将其与 Eclair (5) 和 Froyo (8) 进行比较以获得适当的功能。

它还要求您使用 2.2 或更高版本进行编译。如果您不知道,Android 是向后兼容的,只要您检查由于版本差异而丢失的功能。这意味着尽管您使用 2.2 进行编译,但它会一直工作到 1.0。

这就是我使用的,希望它可以帮助其他人。

【问题讨论】:

    标签: android button touch simultaneous


    【解决方案1】:

    如果您有两个不同的按钮,您可以将 onTouchListeners 附加到每个按钮,然后让 onTouch 调用向您的主活动发送一条消息,指示该按钮已被按下。大致:

    public class MainClass extends Activity implements Handler.Callback {
    handler = new Handler(this);
    // ...
    ((Button) findViewById(R.id.button_one)).setOnTouchListener(new OnTouchListener() {
      @Override
      public boolean onTouch(View v, MotionEvent event) {
        handler.sendEmptyMessage(BUTTON_ONE);
        return true;
      }
    });
    ((Button) findViewById(R.id.button_two)).setOnTouchListener(new OnTouchListener() {
      @Override
      public boolean onTouch(View v, MotionEvent event) {
        handler.sendEmptyMessage(BUTTON_TWO);
        return true;
      }
    });
    
    public boolean handleMessage(Message msg) {
      if (msg.what == BUTTON_ONE) {
        mButtonOnePressed = System.nanoTime();
        checkIfBothPressed();
      } else if (msg.what == BUTTON_TWO) {
        mButtonTwoPressed = System.nanoTime();
        checkIfBothPressed();
      }
    }
    
    public void checkIfBothPressed() {
      if (Math.abs(mButtonOnePressed - mButtonTwoPressed) < 1000000) {
        // They pressed the buttons within one second
      }
    }
    }
    

    (以上代码未经测试或完整。)

    如果您让它们在单个视图上接触两个不同的点,您可以使用 event.getX(index) 和 event.getY(i) 遍历事件。请参阅 event.getPointerCount()。 (对不起,这个没有示例代码,主要是因为我认为两个不同的按钮方法更好。:))

    【讨论】:

    • 抱歉耽搁了,谢谢您的回复。不幸的是,即使这样,按钮仍然单独接收焦点。用户 1 点击那里的按钮,很好。用户 2 点击那里按钮,很好。用户 1 和 2 同时点击那里的按钮,这是对谁被调用的猜测。使用您提到的时间方法,除非两个按钮同时发生焦点,否则它不起作用。 Android 一次只允许一个焦点。我尝试在线性视图中添加一个触摸监听器以捕捉不同的触摸点,但它似乎没有被调用(可能忘记设置某些东西)。
    • 您认为在按钮触摸处理程序视图本身中进行联系人检查会更好吗?
    • 如果您想在单个视图中执行此操作(我想我理解您的原因),您可能需要在其构造函数中使用这些方法: setFocusable(true);请求焦点(); setFocusableInTouchMode(true);
    • 感谢您的帮助,我最终选择了您提到的第二种方法并在我的原始帖子中进行了解释。
    【解决方案2】:

    试试我的 MultitouchActivity http://www.passsy.de/multitouch-for-all-views/

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-02
      • 2011-10-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多