只在四个方向触摸运动?

时间:2013-05-25 07:42:19

标签: android

我正在进行一场简单的三场比赛,类似于宝石迷阵,我只是想通过触摸精灵对象来移动精灵对象,然后将其向左,向右,向上和向下四个方向移动一步。我通过将Down上的X和Y值与Move上的X和Y值进行比较来实现此目的。它起作用但它还远非完美!如果运动不直接,那么很容易得到错误的值。我的问题是:有没有办法改善这一点并使其更好?

我也看了一下手势,但这对我使用的平面视图来说似乎非常复杂。

@Override
public boolean onTouch(View v, MotionEvent event) {

    switch (event.getAction()) {

    case MotionEvent.ACTION_DOWN:
        Log.i("test","Down");

        touchActionDownX = (int)event.getX();
        touchActionDownY = (int)event.getY();
        touchActionMoveStatus = true;

        gameLoop.touchX = (int)event.getX();
        gameLoop.touchY = (int)event.getY();
        gameLoop.touchActionDown = true;
        break;

    case MotionEvent.ACTION_POINTER_UP:

        touchActionMoveStatus = true;

        break;

    case MotionEvent.ACTION_MOVE:
        //Log.i("test","Move");
        gameLoop.touchActionMove = true;

        if(touchActionMoveStatus) {

        touchActionMoveX = (int)event.getX();
        touchActionMoveY = (int)event.getY();

        if(touchActionMoveX < touchActionDownX)
            Log.i("test","Move Left");
        else if(touchActionMoveX > touchActionDownX)
            Log.i("test","Move Right");
        else if(touchActionMoveY < touchActionDownY)
            Log.i("test","Move Up");
        else if(touchActionMoveY > touchActionDownY)
            Log.i("test","Move Down");

        touchActionMoveStatus = false; // Will be set to true when pointer is up
        }

        break;
    }

    // return false;
    return true; // This gets the coordinates all the time
}

3 个答案:

答案 0 :(得分:3)

尝试这样的事情:

@Override
public boolean onTouch(View v, MotionEvent event) {

    //You may have to play with the value and make it density dependant.
    int threshold = 10;

    switch (event.getAction()) {

    case MotionEvent.ACTION_DOWN:
        Log.i("test","Down");

        touchActionDownX = (int)event.getX();
        touchActionDownY = (int)event.getY();
        touchActionMoveStatus = true;

        gameLoop.touchX = (int)event.getX();
        gameLoop.touchY = (int)event.getY();
        gameLoop.touchActionDown = true;
        break;

    case MotionEvent.ACTION_POINTER_UP:

        touchActionMoveStatus = false;

        break;

    case MotionEvent.ACTION_MOVE:
        //Log.i("test","Move");
        gameLoop.touchActionMove = true;

        if(touchActionMoveStatus) {

        touchActionMoveX = (int)event.getX();
        touchActionMoveY = (int)event.getY();

        if(touchActionMoveX < (touchActionDownX - threshold) && (touchActionMoveY > (touchActionDownY - threshold)) && (touchActionMoveY  (touchActionDownY + threshold))){
            Log.i("test","Move Left");//If the move left was greater than the threshold and not greater than the threshold up or down
            touchActionMoveStatus = false;
        }
        else if(touchActionMoveX > (touchActionDownX + threshold) && (touchActionMoveY > (touchActionDownY - threshold)) && (touchActionMoveY < (touchActionDownY + threshold))){
            Log.i("test","Move Right");//If the move right was greater than the threshold and not greater than the threshold up or 
            touchActionMoveStatus = false;
       }
        else if(touchActionMoveY < (touchActionDownY - threshold) && (touchActionMoveX > (touchActionDownX - threshold)) && (touchActionMoveX < (touchActionDownX + threshold))){
            Log.i("test","Move Up");//If the move up was greater than the threshold and not greater than the threshold left or right
            touchActionMoveStatus = false;
        }
        else if(touchActionMoveY > (touchActionDownY + threshold) && (touchActionMoveX > (touchActionDownX - threshold)) && (touchActionMoveX < (touchActionDownX + threshold))){
            Log.i("test","Move Down");//If the move down was greater than the threshold and not greater than the threshold left or right
            touchActionMoveStatus = false;
        }
        }

        break;
    }

    // return false;
    return true; // This gets the coordinates all the time
}

或使用比例:

@Override
public boolean onTouch(View v, MotionEvent event) {

    //You may have to play with the value. 
    //A value of two means you require the user to move twice as 
    //far in the direction they intend to move than any perpendicular direction.
    float threshold = 2.0;

    switch (event.getAction()) {

    case MotionEvent.ACTION_DOWN:
        Log.i("test","Down");

        touchActionDownX = (int)event.getX();
        touchActionDownY = (int)event.getY();
        touchActionMoveStatus = true;

        gameLoop.touchX = (int)event.getX();
        gameLoop.touchY = (int)event.getY();
        gameLoop.touchActionDown = true;
        break;

    case MotionEvent.ACTION_POINTER_UP:

        touchActionMoveStatus = true;

        break;

    case MotionEvent.ACTION_MOVE:
        //Log.i("test","Move");
        gameLoop.touchActionMove = true;

        if(touchActionMoveStatus) {

        touchActionMoveX = (int)event.getX();
        touchActionMoveY = (int)event.getY();

        // I haven't tested this so you may have a few typos to correct.
        float ratioLeftRight = Math.abs(touchActionMoveX - touchActionDownX)/Math.abs(touchActionMoveY - touchActionDownY)
        float ratioUpDown = Math.abs(touchActionMoveY - touchActionDownY)/Math.abs(touchActionMoveX - touchActionDownX)

        if(touchActionMoveX < touchActionDownX && ratioLeftRight > threshold){
            Log.i("test","Move Left");
            touchActionMoveStatus = false;
        }
        else if(touchActionMoveX > touchActionDownX && ratioLeftRight > threshold){
            Log.i("test","Move Right");
            touchActionMoveStatus = false;
        }
        else if(touchActionMoveY < touchActionDownY && ratioUpDown > threshold){
            Log.i("test","Move Up");
            touchActionMoveStatus = false;
        }
        else if(touchActionMoveY > touchActionDownY && ratioUpDown > threshold){
            Log.i("test","Move Down");
            touchActionMoveStatus = false;
        }
        }

        break;
    }

    // return false;
    return true; // This gets the coordinates all the time
}

答案 1 :(得分:3)

我会选择 LARGEST 移动的尺寸并完全忽略另一个尺寸,例如,如果移动是x = 10且y = 8则只使用x尺寸(即左/右)反之亦然。

同样如 Larry McKenzie 所述,使用阈值忽略较小的移动是一个好主意,以防止注册用户不想要的意外移动。将阈值调整为感觉自然的东西。

以下是使用您的示例的一些代码(仅限ACTION_MOVE案例):

case MotionEvent.ACTION_MOVE:
    //Log.i("test","Move");
    gameLoop.touchActionMove = true;

    if(touchActionMoveStatus) {

        touchActionMoveX = (int)event.getX();
        touchActionMoveY = (int)event.getY();

        // setup a threshold (below which no movement would occur)
        int threshold = 5;        /* tweak this as needed */ 

        // first calculate the "delta" movement amounts
        int xMove = touchActionMoveX - touchActionDownX;
        int yMove = touchActionMoveY - touchActionDownY;

        // now find the largest of the two (note that if they
        // are equal, x is assumed largest)
        if ( Math.abs( xMove ) >= Math.abs( yMove ) )  {  /* X-Axis */
           if ( xMove >= threshold )
              Log.i("test","Move Right");
           else if ( xMove <= -threshold )
              Log.i("test","Move Left");
        }
        else  {                                          /* Y-Axis */
           if ( yMove >= threshold )
              Log.i("test","Move Down");
           else if ( yMove <= -threshold )
              Log.i("test","Move Up");
        }

        touchActionMoveStatus = false; // Will be set to true when pointer is up
    }
}
break;

注意:正如在其他一些答案中所提到的,因为可能会发生具有非常小值的多个事件,所以最好累积(即总结)移动直到达到阈值 - 您可以使用成员来实现此目的在ACTION_DOWN中重置。一旦达到阈值(在任一维度),那么您可以执行检查哪个方向。

替代方法

另一种方法是检测 FIRST ACTION_MOVE事件中的最大移动,然后将所有进一步的移动锁定到该维度。为此,您需要添加各种状态成员 - 这些成员需要在每个州更新。 这是一个粗略的例子(只有状态跟踪):

  // members
  private boolean axisLock = false;    /* Track When Lock is Required */
  private boolean axisX = true;        /* Axis to Lock (true) for X, (false) for Y */


  @Override
  public boolean onTouch(View v, MotionEvent event) {

      switch (event.getAction()) {

      case MotionEvent.ACTION_DOWN:

          // set this state so that ACTION_MOVE knows a lock is required
          axisLock = true;

          break;

      case MotionEvent.ACTION_UP:

          // clear the state in case no move was made
          axisLock = false;

      case MotionEvent.ACTION_MOVE:

          // now lock the axis if this is the first move event
          if ( axisLock )  {

             // this will set whether the locked axis is X (true) or Y (false)
             axisX = event.getX() >= event.getY();

             // reset the state (to keep the axis locked)
             axisLock = false;
          }

          // at this point you only need to consider the movement for the locked axis
          if ( axisX )  {
             int movement = (int)event.getX();    /* Get Movement for Locked Axis */
             // check for your movement conditions here
          }
          else  {
             int movement = (int)event.getY();    /* Get Movement for Locked Axis */
             // check for your movement conditions here
          }

          break;
      }

      return true;
  }

您可以为此代码添加许多优化,现在它只是说明了基本概念。

答案 2 :(得分:2)

拉里有正确的想法,我只想提出一个小解决方案,

//put this in the wraping class  
private static int THRESHOLD = 10;
private static int initX;
private static int initY;

@Override
public boolean onTouch(View v, MotionEvent event) {

switch (event.getAction()) {

case MotionEvent.ACTION_DOWN:
    initX = (int)event.getX();
    initY = (int)event.getY();
    break;

case MotionEvent.ACTION_POINTER_UP:
   //you can add in some kind of "move back" animation for the item
    break;

case MotionEvent.ACTION_MOVE:
if(((int)event.getY - initY) > THRESHOLD){
   //move down
   break;
}

if(((int)event.getY - initY) > -THRESHOLD){
   //move up
   break;
}

if(((int)event.getX - initX) > THRESHOLD){
   //move right
   break;
}

if(((int)event.getX - initX) < -THRESHOLD){
   //move left
   break;
}
break;    
}
}

我没有测试这段代码,只是免费写它,但我希望你能得到我的想法:)