Android中的多点触摸画布旋转

Android中的多点触摸画布旋转,android,canvas,multi-touch,motionevent,Android,Canvas,Multi Touch,Motionevent,我是Android新手,我正在尝试多点触摸输入的窍门。我从一个简单的应用程序开始,它允许用户通过用一个手指拖动和释放来在画布上创建矩形,我正在使用它。为了扩展这一点,我现在希望用户能够使用第二个手指旋转他们正在绘制的矩形,这就是我的问题开始的地方。目前,添加第二个手指将导致多个矩形旋转,而不仅仅是当前矩形,但一旦释放第二个手指,它们将恢复为默认方向 我已经做了一段时间了,我认为我的核心问题是,我错误地处理了由两个(或更多个)手指引起的多个动作事件。我留下来在屏幕上显示每个事件的坐标的日志语句与触

我是Android新手,我正在尝试多点触摸输入的窍门。我从一个简单的应用程序开始,它允许用户通过用一个手指拖动和释放来在画布上创建矩形,我正在使用它。为了扩展这一点,我现在希望用户能够使用第二个手指旋转他们正在绘制的矩形,这就是我的问题开始的地方。目前,添加第二个手指将导致多个矩形旋转,而不仅仅是当前矩形,但一旦释放第二个手指,它们将恢复为默认方向

我已经做了一段时间了,我认为我的核心问题是,我错误地处理了由两个(或更多个)手指引起的多个动作事件。我留下来在屏幕上显示每个事件的坐标的日志语句与触摸屏幕的第一个手指保持联系,而不是切换到第二个手指。我尝试了多种访问和更改事件指针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);
            }
        }
    }
}
公共类BoxDrawingView扩展了视图{
私有静态最终字符串标记=“BoxDrawingView”;
私有静态final int无效\u指针\u ID=-1;
private int mActivePointerId=无效的\u指针\u ID;
私人信箱mCurrentBox;
private List mBoxen=new ArrayList();
私有浮动mLastTouchX;
私密的浮躁和敏感;
...
@凌驾
公共布尔onTouchEvent(运动事件){
开关(MotionEventCompat.getActionMasked(事件)){
case MotionEvent.ACTION\u DOWN:
MacTivePointId=MotionEventCompat.getPointerId(事件,0);
当前=新点F(MotionEventCompat.getX(事件,MacTivePointId),
getY(事件,mactivePointId));
action=“action\u DOWN”;
//重置绘图状态
mCurrentBox=新框(当前);
mBoxen.add(mCurrentBox);
mLastTouchX=MotionEventCompat.getX(事件,MotionEventCompat.getPointerId(事件,0));
mLastTouchY=MotionEventCompat.getY(事件,MotionEventCompat.getPointerId(事件,0));
打破
case MotionEvent.ACTION\u指针\u向下:
action=“action\u POINTER\u DOWN”;
MacTivePointId=MotionEventCompat.getPointerId(事件,0);
mLastTouchX=MotionEventCompat.getX(事件,MotionEventCompat.getPointerId(事件,0));
mLastTouchY=MotionEventCompat.getY(事件,MotionEventCompat.getPointerId(事件,0));
打破
case MotionEvent.ACTION\u移动:
action=“action\u MOVE”;
当前=新点F(MotionEventCompat.getX(事件,MacTivePointId),
getY(事件,mactivePointId));
if(mCurrentBox!=null){
mCurrentBox.setCurrent(当前);
使无效();
}
如果(MotionEventCompat.getPointerCount(事件)>1){
int pointerIndex=MotionEventCompat.findPointerIndex(事件,mactivePointId);
float currX=MotionEventCompat.getX(事件,指针索引);
float currY=MotionEventCompat.getY(事件,指针索引);
如果(mLastTouchXgetX()){
对数d(标签“逆时针”);
旋转角度=-30;
}
}
打破
case MotionEvent.ACTION\u UP:
action=“action\u UP”;
mCurrentBox=null;
mLastTouchX=null;
mLastTouchY=null;
MacTivePointId=无效的\u指针\u ID;
打破
case MotionEvent.ACTION\u指针\u向上:
action=“action\u POINTER\u UP”;
int pointerIndex=event.getActionIndex();
int pointerId=event.getPointerId(pointerIndex);
if(pointerId==MacTivePointId){
MacTivePointId=无效的\u指针\u ID;
}
打破
case MotionEvent.ACTION\u取消:
action=“action\u CANCEL”;
mCurrentBox=null;
MacTivePointId=无效的\u指针\u ID;
打破
}
返回true;
}
@凌驾
受保护的void onDraw(画布){
//填充背景
帆布.拉丝漆(mBackgroundPaint);
用于(盒子:mBoxen){
//长方体是自定义对象。原点是原点,
//当前是对角点的另一个点
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);
如果(mRotationAngle!=0){
canvas.save();
画布旋转(旋转角度);
画布.drawRect(左、上、右、下、mBoxPaint);
画布。旋转(-m旋转角度);
canvas.restore();
mRotationAngle=0;
}否则{
画布.drawRect(左、上、右、下、mBoxPaint);
}
}
}
}

绘制东西有几种方法,不仅在android中,在Java中也是如此。问题是你是
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);
}
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
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));
    }
}