2016-06-22 5 views
3

Android에 익숙하지 않아 멀티 터치 입력이 필요합니다. 필자는 사용자가 한 손가락으로 드래그 앤 릴리즈하여 사각형을 Canvas에 만들 수있는 간단한 응용 프로그램을 시작했습니다. 이를 확장하기 위해, 이제 사용자가 내 문제가 시작되는 두 번째 손가락을 사용하여 그리는 사각형을 회전 시키길 원합니다. 두 번째 손가락을 추가하면 현재 사각형 대신 여러 사각형이 회전하지만 두 번째 손가락을 놓으면 바로 기본 방향으로 되돌아갑니다.Android에서 멀티 터치 캔버스 회전

저는 잠시 동안 작업 해 왔으며, 두 가지 (또는 더 많은 손가락)와 함께 제공되는 여러 개의 MotionEvent를 잘못 처리하고 있다는 것이 핵심 문제라고 생각합니다. 로깅 문 각 이벤트에 대한 화면의 좌표를 표시하기 위해 남긴 로깅 문은 두 번째로 전환하는 대신 첫 번째 손가락이 화면을 터치하는 것과 관련되어 있습니다. 이벤트 포인터 ID를 액세스하고 변경하는 여러 구성을 시도했지만 여전히 운이 없습니다. 누구든지 올바른 방향으로 지침을 제공 할 수 있다면 매우 감사하게 생각합니다. 다음과 같이

내 코드는 다음과 같습니다

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는 회전하려는 축을 왼쪽 상단이나 뷰 중심에서 회전합니다. 기억하지 마라).

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

을하지만 난 당신이 다른 접근을 시도하는 것이 좋습니다 : 당신은 그 선택에 대한 사용 중단하는 경우

, 당신은 이런 식으로 할 시도 할 수 있습니다. 다각형의 축을 계산하여 이동중인 직사각형에 회전을 직접 수행하십시오. 이것은 자바 수학 연산을 사용하여 수행 할 수 있습니다

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); 
} 

그래서 (X, Y)가 당신의 사각형과 함께 센터, 높이가 얼마나 큰 당신을 말해. formShape(int[], int[], double) 메서드에서 cx 및 cy는 모양을 그리는 데 사용되며 scale은 scale = 1을 사용하는 것이 아니라 나중에 확대 또는 축소하려는 경우에 사용할 값입니다.

@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)); 
    } 
} 

죄송하지만,이 : 멀티 터치 회전 청취자를 들어

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); 

당신이 당신의 활동 또는보기에이 방법을 오버라이드 (override) :

지금 당신의 사각형을 그리기 위해, 이것은 당신이 그것을하는 방법이다 대답이 길어지고 있습니다. 이러한 작업에 대해 더 궁금한 점이 있고 솔루션의 접근 방식을 변경하려면 다시 질문하고 의견을 말해주십시오.

도움이 되었기를 바랍니다.