Android多点触控奇怪的行为

时间:2013-07-18 18:10:26

标签: android events input touch motion

经过大量的修补,我想我终于为我的安卓游戏想出了一个很好的多点触控处理系统。它使用了Robert Greene的输入管道,经过修改后可与多点触控一起使用。现在,代码有一个简单的系统,记录哪个指针ID正在执行哪个动作(现在只是拍摄和移动)。每个指针的状态都保存在一个Pointer类中,这只是一个简单的封装,它是否是向下的,以及它的坐标。 ID充当指针数组索引。

这似乎应该在实践中运作良好,但在游戏中它表现得非常不稳定。在LogCat中记录指针动作时,Android通常会发送一个" UP"当指针保持向下时,或者在一些" MOVE"之前的动作。指针的动作。因为我的代码认为指针已启动,所以游戏无法响应它。

按钮按下时也会发生这种情况。当指针落在该区域(现在只是左下区域)时,Android将发送多个" UP"和" DOWN"即使指针在整个时间内保持不变,也会执行操作。我以前只有一个触摸移动系统,但没有发生这些问题。

这只是我对事件做出反应的问题吗?我应该单独处理POINTER_DOWN和DOWN吗?或者我应该在" UP"之后检测哪些指针正在移动?尽管Android说的是什么,看看哪些确实失败了?

这是我线程中的当前代码,它接收来自Android的输入事件。因为它是一个管道系统,所以我将事件封装在InputObject中,这与Robert Greene有点相似。也许一双新的眼睛可以帮助我说出错误的是什么?谢谢你的帮助!

private int inx, iny;
private int shootID;
public boolean shooting = false;
private int moveID;
public boolean moveDown = false;

private static final int MAX_POINTERS = 10;
private Pointer[] pointers = new Pointer[MAX_POINTERS];

public void inputTouch(InputObject in) {

    switch(in.action) {
    case MotionEvent.ACTION_DOWN:
    case MotionEvent.ACTION_POINTER_DOWN:
        pointers[in.pID].press(in.pX[in.actionIndex], in.pY[in.actionIndex]);
        //Log.i("D", "DOWN");
        break;
    case MotionEvent.ACTION_MOVE:
        for(int p = 0; p < in.pointCount; p++) {
            int id = in.pointerIDs[p];
            pointers[id].setCoord(in.pX[id], in.pY[id]);
        }
        //Log.i("D", "MOVE");
        break;
    case MotionEvent.ACTION_UP:
    case MotionEvent.ACTION_POINTER_UP:
        pointers[in.pID].release();

        if(shootID == in.pID) {
            shooting = false;
        }

        if(moveID == in.pID) {
            moveDown = false;
        }

        //Log.i("D", "UP");
        break;
    case MotionEvent.ACTION_CANCEL:
    default:
        break;
    }

    for(int ap = 0; ap < MAX_POINTERS; ap++) {
        if(pointers[ap].down) {
            if(pointers[ap].x < world.cam.pixelWidth / 4 &&
                    pointers[ap].y > world.cam.pixelHeight - (world.cam.pixelHeight / 4)) {

                shootID = ap;
                shooting = true;
            } else {
                inx = pointers[ap].x;
                iny = pointers[ap].y;
                moveID = ap;
                moveDown = true;
            }
        }
    }

    StringBuilder sb = new StringBuilder();
    for(int j = 0; j < 3; j++) {
        sb.append("ID " + (j+1) + ": " + pointers[j].down + "[" + pointers[j].x + ", " + pointers[j].y + "]" + " | ");
    }
    //Log.i("D", sb.toString());
}

1 个答案:

答案 0 :(得分:0)

希望你现在得到答案,但这是我的解决方案:

首先,在InputHolder类中添加了几个东西:

- mPointerIndex的内部公共字段= event.getPointerIndex和mPointerID = event.getPointerID(mPointerIndex)(这些在useEvent / useHistory中分配)

- 如果您只需要跟踪2个接触点,则需要添加mPointerIndex2,x2和y2。根据需要添加更多以跟踪更多积分。

- 传递MotionEvent.ACTION_POINTER_2_UP / DOWN的情况的-add定义。事实证明,POINTER_1与POINTER_UP / DOWN相同!这真让我失望,因为我正在通过开关盒掉到默认状态。我只抓住了这个,因为我将默认值更改为-1并看到它已记录。 根据您在processInput(obj)中处理操作的方式,将这些操作映射到不同的int。在我的情况下,我使用obj.y来查看它们是否在左/右触摸,我只需要两个点,所以我将它们映射到ACTION_TOUCH_POINTER_UP / DOWN,而不是每次触摸它自己的动作int标识符。

- 现在,如果要跟踪多个触摸点,则必须在event.getPointerCount()中的所有条目的for循环中执行上述和下面的操作。在我的情况下,我只对另一个接触点的x / y感兴趣,所以我只能在填充第一个点之后进行检查而另一个指针索引很容易推断:

public void useEvent(MotionEvent event) {
    eventType = EVENT_TYPE_TOUCH;
    int a = event.getAction();
    switch (a) {
        case MotionEvent.ACTION_DOWN:
        action = ACTION_TOUCH_DOWN;
        break;
        case MotionEvent.ACTION_POINTER_DOWN:
        action = ACTION_TOUCH_POINTER_DOWN;
        break;
        case MotionEvent.ACTION_POINTER_2_DOWN:
        action = ACTION_TOUCH_POINTER_DOWN;
        break;
        case MotionEvent.ACTION_MOVE:
        action = ACTION_TOUCH_MOVE;
        break;
        case MotionEvent.ACTION_UP:
        action = ACTION_TOUCH_UP;
        break;
        case MotionEvent.ACTION_POINTER_UP:
        action = ACTION_TOUCH_POINTER_UP;
        break;
        case MotionEvent.ACTION_POINTER_2_UP:
        action = ACTION_TOUCH_POINTER_UP;
        break;
        default:
        action = -1;
    }
    time = event.getEventTime();

    pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT;
    pointerID = event.getPointerId(pointerIndex);

    x = (int) event.getX(pointerIndex);
    y = (int) event.getY(pointerIndex);

    if (event.getPointerCount() > 1)
    {
        pointerIndex2 = pointerIndex== 0 ? 1 : 0;
        x2 = (int)event.getX(pointerIndex2);
        y2 = (int)event.getY(pointerIndex2);
    }
}

如果你正在追踪更多的点,或者需要更多关于每个touchevent的信息,你必须在动作切换案例中扩展for循环。

无论如何,现在你需要在你的线程中有两个变量,这样你就可以跟踪你的两个触摸:调用'em touchOneID和touchTwoID(或索引)。在一个ACTION_POINTER_2_UP上,obj.mPointerID引用了正在向上的obj!这意味着另一个触摸将改变它的ID!跟踪此更改,您已经排序。内部obj的mPointerID / Index将始终是正确的,您只需在surfaceview的线程中正确跟踪它们,这样您就可以在获得POINTER_DOWN时采取相应的行动。在我的情况下,我做了一个简单的x位置检查,以确定在我的ACTION_MOVE事件中做什么,告诉我足够正确地确定我应该使用哪个x / y或x2 / y2以及如何使用它。这意味着运行的代码更少,并且限制了我必须保留在内存中的东西,但这一切都取决于你需要的信息和数量。

最后: 说实话,如果你正确处理了定义并分配了每个MotionEvent并将它们保存在InputObject中,你可能会没事的。好吧,我想你可以忽略并丢失整个switch case,只需说obj.mAction = event.getAction()并在你的processInput(obj)中处理它们! 意思是,他在InputHolder中重新映射的所有静态int似乎都是不必要的,除非你真的只需要一个或两个触摸定义(这解释了他的神秘独立评论“这个应用程序只对下行感兴趣”)。摆脱那些静态定义的int也意味着你可以只测试MotionEvent.ACTION_CODE,而不是对InputHolder.TOUCH_ACTION_CODE进行查找。