简体   繁体   English

Android围绕动态枢轴旋转视图

[英]Android rotate view around dynamic pivot

I'm developing a ViewGroup layout which is able to detect touch events and scale/rotate/pan it's child view based on touch events. 我正在开发一个ViewGroup布局,该布局能够检测触摸事件并根据触摸事件缩放/旋转/平移其子视图。 My problem is that I would like the child to rotate/zoom around the point between the user's fingers two fingers, performing the gesture, not around its center. 我的问题是我希望孩子围绕用户手指两根手指之间的点旋转/缩放, 而不是围绕其中心进行手势。

I've got the gesture recognizers in place and I am already able to scale/pan/rotate around the center of the child. 我已经安装了手势识别器,并且已经能够围绕孩子的中心进行缩放/平移/旋转。 I think the main issue is transforming the pivot point (which in my case would be the midpoint between the user's first and second pointers) to the child's current rotation. 我认为主要问题是将枢轴点(在我的情况下是用户的第一和第二指针之间的中点)转换为孩子当前的旋转角度。 The first time I set the pivot, it works nicely, but as soon as I rotate the view, lift fingers and then try to rotate around another pivot, the view jumps around. 第一次设置枢轴时,效果很好,但是一旦旋转视图,抬起手指,然后尝试绕另一个枢轴旋转,视图就会跳转。 Here's how it looks (only with rotation): 这是它的外观(仅旋转):

在此处输入图片说明

So when the first rotation happens it works as expected and it rotates around the point between the two fingers. 因此,当第一次旋转发生时,它会按预期工作,并绕着两个手指之间的点旋转。 However, once rotated, the next rotation jumps to a new location. 但是,一旦旋转,下一个旋转将跳到新位置。 Here's the relevant code: 以下是相关代码:

@Override
public boolean onRotationBegin(RotationGestureDetector detector) {
    float[] coordinates = screenPointsToScaledPoints(new float[]{detector.getFocusX(), detector.getFocusY()});
    pivotX = coordinates[0];
    pivotY = coordinates[1];
    child().setPivotX(pivotX);
    child().setPivotY(pivotY);
    return true;
}

@Override
public boolean onRotate(RotationGestureDetector detector) {
    rotation += detector.getRotationDelta();
    return true;
}

private float[] screenPointsToScaledPoints(float[] a){
    mRotateMatrixInverse.mapPoints(a);
    return a;
}

private void applyScaleAndTranslation() {
    View child = child();
    child.setRotation(rotation);
    mRotateMatrix.setRotate(rotation, pivotX, pivotY);
    mRotateMatrix.invert(mRotateMatrixInverse);
}

Any ideas what I could be missing? 有什么想法我可能会错过吗?

Okay so, thanks to @pskink I've managed to get to the answer. 好的,感谢@pskink,我设法找到了答案。 The key was here - Android ImageView Scaling and translating issue 关键在这里-Android ImageView缩放和翻译问题

My main mistake was that I was using separate matrices for scaling, moving and rotating whereas in the suggested answer there is only one which contains all operations, which is what I ended up doing. 我的主要错误是我在使用单独的矩阵进行缩放,移动和旋转,而在建议的答案中只有一个包含所有运算的矩阵,这就是我最终要做的。 Here's the interesting part of the code: 这是代码中有趣的部分:

private Matrix mMatrix = new Matrix();
private Matrix mMatrixInverse = new Matrix();

@Override
protected void dispatchDraw(Canvas canvas) {
    canvas.save();
    canvas.concat(mMatrix);
    super.dispatchDraw(canvas);
    canvas.restore();
}

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    doubleFingers = ev.getPointerCount() == 2;
    if (!doubleFingers) {
        // Map touch coordinates to our matrix to draw correctly in the canvas.
        float[] mapped = screenPointsToScaledPoints(new float[]{ev.getX(0), ev.getY(0)});
        ev.setLocation(mapped[0], mapped[1]);
    }
    return super.dispatchTouchEvent(ev);
}

private float[] screenPointsToScaledPoints(float[] a){
    mMatrix.invert(mMatrixInverse);
    mMatrixInverse.mapPoints(a);
    return a;
}

@Override
public boolean onScale(ScaleGestureDetector detector) {
    float scaleFactor = detector.getScaleFactor();
    if (scale >= MIN_CANVAS_ZOOM) {
        scale *= scaleFactor;
        scale = Math.max(MIN_CANVAS_ZOOM, Math.min(scale, MAX_CANVAS_ZOOM));
        mMatrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusY());
    }
    return true;
}

@Override
public boolean onRotate(RotationGestureDetector detector) {
    rotation += detector.getRotationDelta();
    mMatrix.postRotate(detector.getRotationDelta(), detector.getFocusX(), detector.getFocusY());
    return true;
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    scaleDetector.onTouchEvent(event);
    rotationDetector.onTouchEvent(event);
    // Lots of business logic
    dX = coordinates[0] - mLastTouchX;
    dY = coordinates[1] - mLastTouchY;
    mLastTouchX = coordinates[0];
    mLastTouchY = coordinates[1];
    mMatrix.postTranslate(dX, dY);
    invalidate();
    return false;
}

In my case I have to manually detect two-finger pan because the one-finger event is used to paint on an OpenGL surface, hence the translation from real-world to scaled matrix coordinates. 在我的情况下,我必须手动检测两指平移,因为一个手指事件用于在OpenGL表面上绘制,因此需要从真实世界转换为缩放的矩阵坐标。 Here's the result: 结果如下:

在此处输入图片说明

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM