2016-06-22 4 views
3

Я новичок в Android, и я пытаюсь получить поддержку multi touch. Я начал с простого приложения, которое позволяет пользователю создавать прямоугольники на холсте, перетаскивая и отпуская одним пальцем, который у меня работает. Чтобы расширить это, теперь я хочу, чтобы пользователь мог поворачивать прямоугольник, который они рисуют, используя второй палец, в котором возникают мои проблемы. Как бы то ни было, добавление второго пальца приведет к вращению нескольких прямоугольников вместо текущего, но они вернутся к своей ориентации по умолчанию, как только второй палец будет отпущен.Поворот холста Multitouch в Android

Я работаю над этим какое-то время, и я думаю, что моя основная проблема в том, что я злоупотребляю несколькими MotionEvents, которые поставляются с двумя (или несколькими пальцами). Заявления о регистрации я ушел, чтобы отобразить координаты на экране, чтобы каждое событие оставалось привязанным к первому пальцу, касающемуся экрана, вместо перехода ко второму. Я пробовал несколько конфигураций доступа и изменения идентификатора указателя событий, но до сих пор не повезло. Если бы кто-нибудь мог дать некоторые рекомендации в правильном направлении, я был бы чрезвычайно благодарен.

Мой код выглядит следующим образом:

public class BoxDrawingView extends View { 

    private static final String TAG = "BoxDrawingView"; 
    private static final int INVALID_POINTER_ID = -1; 

    private int mActivePointerId = INVALID_POINTER_ID; 
    private Box mCurrentBox; 
    private List<Box> mBoxen = new ArrayList<>(); 
    private Float mLastTouchX; 
    private Float mLastTouchY; 

    ... 

    @Override 
    public boolean onTouchEvent(MotionEvent event) { 
     switch(MotionEventCompat.getActionMasked(event)) { 
      case MotionEvent.ACTION_DOWN: 
       mActivePointerId = MotionEventCompat.getPointerId(event, 0); 
       current = new PointF(MotionEventCompat.getX(event, mActivePointerId), 
        MotionEventCompat.getY(event, mActivePointerId)); 
       action = "ACTION_DOWN"; 

       // Reset drawing state 
       mCurrentBox = new Box(current); 
       mBoxen.add(mCurrentBox); 

       mLastTouchX = MotionEventCompat.getX(event, MotionEventCompat.getPointerId(event, 0)); 
       mLastTouchY = MotionEventCompat.getY(event, MotionEventCompat.getPointerId(event, 0)); 
       break; 

      case MotionEvent.ACTION_POINTER_DOWN: 
       action = "ACTION_POINTER_DOWN"; 
       mActivePointerId = MotionEventCompat.getPointerId(event, 0); 

       mLastTouchX = MotionEventCompat.getX(event, MotionEventCompat.getPointerId(event, 0)); 
       mLastTouchY = MotionEventCompat.getY(event, MotionEventCompat.getPointerId(event, 0)); 
       break; 

      case MotionEvent.ACTION_MOVE: 
       action = "ACTION_MOVE"; 
       current = new PointF(MotionEventCompat.getX(event, mActivePointerId), 
        MotionEventCompat.getY(event, mActivePointerId)); 

       if (mCurrentBox != null) { 
        mCurrentBox.setCurrent(current); 
        invalidate(); 
       } 
       if(MotionEventCompat.getPointerCount(event) > 1) { 

        int pointerIndex = MotionEventCompat.findPointerIndex(event, mActivePointerId); 
        float currX = MotionEventCompat.getX(event, pointerIndex); 
        float currY = MotionEventCompat.getY(event, pointerIndex); 


        if(mLastTouchX < currX) { 
         // simplified: only use x coordinates for rotation for now. 
         // +X for clockwise, -X for counter clockwise 
         Log.d(TAG, "Clockwise"); 
         mRotationAngle = 30; 
        } 
        else if (mLastTouchX > getX()) { 
         Log.d(TAG, "Counter clockwise"); 
         mRotationAngle = -30; 
        } 
       } 
       break; 

      case MotionEvent.ACTION_UP: 
       action = "ACTION_UP"; 
       mCurrentBox = null; 
       mLastTouchX = null; 
       mLastTouchY = null; 
       mActivePointerId = INVALID_POINTER_ID; 
       break; 

      case MotionEvent.ACTION_POINTER_UP: 
       action = "ACTION_POINTER_UP"; 
       int pointerIndex = event.getActionIndex(); 
       int pointerId = event.getPointerId(pointerIndex); 
       if(pointerId == mActivePointerId){ 
        mActivePointerId = INVALID_POINTER_ID; 
       } 
       break; 

      case MotionEvent.ACTION_CANCEL: 
       action = "ACTION_CANCEL"; 
       mCurrentBox = null; 
       mActivePointerId = INVALID_POINTER_ID; 
       break; 
     } 

     return true; 
    } 


    @Override 
    protected void onDraw(Canvas canvas){ 
     // Fill the background 
     canvas.drawPaint(mBackgroundPaint); 

     for(Box box : mBoxen) { 
      // Box is a custom object. Origin is the origin point, 
      // Current is the point of the opposite diagonal corner 

      float left = Math.min(box.getOrigin().x, box.getCurrent().x); 
      float right = Math.max(box.getOrigin().x, box.getCurrent().x); 
      float top = Math.min(box.getOrigin().y, box.getCurrent().y); 
      float bottom = Math.max(box.getOrigin().y, box.getCurrent().y); 

      if(mRotationAngle != 0) { 
       canvas.save(); 
       canvas.rotate(mRotationAngle); 
       canvas.drawRect(left, top, right, bottom, mBoxPaint); 
       canvas.rotate(-mRotationAngle); 
       canvas.restore(); 
       mRotationAngle = 0; 
      } else { 
       canvas.drawRect(left, top, right, bottom, mBoxPaint); 
      } 
     } 
    } 
} 

ответ

0

Есть несколько способов, чтобы нарисовать вещи, а не только в Android, но в Java, а также. Дело в том, что вы пытаетесь рисовать прямоугольники, поворачивая Canvas. Это способ, но в моем личном опыте я считаю, что это только хороший выбор, если вы хотите повернуть всю картину. Если нет, это может стать немного сложным, потому что вам нужно поместить ось вращения, которая кажется, что вы ее не используете, поэтому Android будет использовать то, что вы хотите повернуть из левого верхнего угла или в центр представления (я не знаю, не помню).

Если Вы выбираете этот выбор, вы можете попробовать сделать это следующим образом:

Matrix matrix = new Matrix(); 
matrix.setRotate(angle, rectangleCenterX, rectangleCenterY); 
canvas.setMatrix(matrix); 

Но я рекомендую вам попробовать другой подход. Сделайте поворот прямо на прямоугольник, который вы двигаете, путем вычисления осей многоугольника. Это вы можете сделать это с помощью операции Java Math:

public void formShape(int cx[], int cy[], double scale) { 
    double xGap = (width/2) * Math.cos(angle) * scale; 
    double yGap = (width/2) * Math.sin(angle) * scale; 
    cx[0] = (int) (x * scale + xGap); 
    cy[0] = (int) (y * scale + yGap); 
    cx[1] = (int) (x * scale - xGap); 
    cy[1] = (int) (y * scale - yGap); 
    cx[2] = (int) (x * scale - xGap - length * Math.cos(radians) * scale); 
    cy[2] = (int) (y * scale - yGap - length * Math.sin(radians) * scale); 
    cx[3] = (int) (x * scale + xGap - length * Math.cos(radians) * scale); 
    cy[3] = (int) (y * scale + yGap - length * Math.sin(radians) * scale); 
} 

So (х, у) является центром прямоугольника и с высотой сказать вам, как он велик. В методе formShape(int[], int[], double) cx и cy будут использоваться для рисования вашей фигуры, а масштаб - это значение, которое нужно использовать, если вы хотите увеличить или уменьшить масштаб позже, если не просто использовать scale = 1;

Теперь для рисования ваших прямоугольников, это то, как вы это делаете:

Paint paint = new Paint(); 
paint.setColor(Color.GRAY); 
paint.setStyle(Style.FILL); 

int[] cx = new int[4]; 
int[] cy = new int[4]; 
Box box = yourBoxHere; 
box.formShape(cx, cy, 1); 
Path path = new Path(); 
path.reset(); // only needed when reusing this path for a new build 
path.moveTo(cx[0], cy[0]); // used for first point 
path.lineTo(cx[1], cy[1]); 
path.lineTo(cx[2], cy[2]); 
path.lineTo(cx[3], cy[3]); 
path.lineTo(cx[0], cy[0]); // repeat the first point 

canvas.drawPath(wallpath, paint); 

Для мультитач вращения слушателя вы должны переопределить 2 метода в вашей деятельности или Вид:

@Override 
public boolean onTouch(View v, MotionEvent event) { 
    if(event.getId() == MotionEvent.ACTION_UP) 
     this.points = null; 
    } 
} 

@Override 
public boolean dispatchTouchEvent(MotionEvent event) { 
    if(event.getPointerCount() >= 2) { 

     float newPoints[][] = new float[][] { 
      {event.getX(0), event.getY(0)}, 
      {event.getX(1), event.getY(1)} 
     }; 

     double angle = angleBetweenTwoPoints(newPoints[0][0], newPoints[0][1], newPoints[1][0], newPoints[1][1]); 

     if(points != null) { 
      double difference = angle - initialAngle; 
      if(Math.abs(difference) > rotationSensibility) { 
       listener.onGestureListener(GestureListener.ROTATION, Math.toDegrees(difference)); 
       this.initialAngle = angle; 
      } 
     } else { 
      this.initialAngle = angle; 
     } 

     this.points = newPoints; 
    } 
} 

public static double angleBetweenTwoPoints(double xHead, double yHead, double xTail, double yTail) { 

    if(xHead == xTail) { 
     if(yHead > yTail) 
      return Math.PI/2; 
     else 
      return (Math.PI*3)/2; 
    } else if(yHead == yTail) { 
     if(xHead > xTail) 
      return 0; 
     else 
      return Math.PI; 
    } else if(xHead > xTail) { 
     if(yHead > yTail) 
     return Math.atan((yHead-yTail)/(xHead-xTail)); 
     else 
      return Math.PI*2 - Math.atan((yTail-yHead)/(xHead-xTail)); 
    } else { 
     if(yHead > yTail) 
      return Math.PI - Math.atan((yHead-yTail)/(xTail-xHead)); 
     else 
      return Math.PI + Math.atan((yTail-yHead)/(xTail-xHead)); 
    } 
} 

Извините, но это ответ становится длинным, если у вас есть дополнительные вопросы о любой из этих операций, и вы хотите изменить подход к решению, попросите еще раз и скажите мне в комментариях.

Я надеюсь, что это было полезно.

Смежные вопросы