Android围绕动态枢轴旋转视图

Posted

技术标签:

【中文标题】Android围绕动态枢轴旋转视图【英文标题】:Android rotate view around dynamic pivot 【发布时间】:2017-09-04 13:20:55 【问题描述】:

我正在开发一个 ViewGroup 布局,它能够检测触摸事件并根据触摸事件缩放/旋转/平移它的子视图。我的问题是我希望孩子围绕用户手指之间的点旋转/缩放两个手指,执行手势,围绕其中心。

我已经安装了手势识别器,并且已经能够围绕孩子的中心进行缩放/平移/旋转。我认为主要问题是将枢轴点(在我的情况下是用户的第一个和第二个指针之间的中点)转换为孩子的当前旋转。我第一次设置枢轴时,效果很好,但是一旦我旋转视图,抬起手指然后尝试围绕另一个枢轴旋转,视图就会跳来跳去。这是它的外观(仅限旋转):

因此,当第一次旋转发生时,它会按预期工作并围绕两个手指之间的点旋转。但是,一旦旋转,下一次旋转会跳转到新位置。以下是相关代码:

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

有什么我可能会遗漏的想法吗?

【问题讨论】:

见***.com/a/21657145/2252830 不,不需要任何invert()ed 矩阵 谢谢@pskink!该链接实际上非常有用,并引导我找到解决方案。现在一切正常。作为记录,我需要一个invert,因为我实际上正在跟踪单点触摸事件并且需要它们的坐标,因为我正在制作一个绘图应用程序,您可以在其中移动画布。如果您将该链接作为答案发布,我可以将其标记为已接受并将我所做的作为单独的答案发布:) 然后将当前矩阵transform()ed 的事件提供给您的检测器(或者我错过了您的观点;-(),也可以随意自动回答... 是的,如果不执行缩放、平移或旋转,我最终会使用当前矩阵转换事件。请参阅答案以获取更多信息:) 【参考方案1】:

好的,多亏了@pskink,我终于找到了答案。关键在这里 - android ImageView Scaling and translating issue

我的主要错误是我使用单独的矩阵进行缩放、移动和旋转,而在建议的答案中,只有一个包含所有操作的矩阵,这就是我最终要做的。这是代码中有趣的部分:

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;

在我的情况下,我必须手动检测两指平移,因为单指事件用于在 OpenGL 表面上绘制,因此从真实世界到缩放矩阵坐标的转换。结果如下:

【讨论】:

canvas.setMatrix(mMatrix); 有效吗?没有canvas.concat(mMatrix); 而不是? 好像没问题,看到建议不要直接设置..用concat试试 setMatrix 我相信您需要将矩阵转换为dy 等于“标题标题”视图的高度 - 显示在屏幕顶部的那个 它实际上以完全相同的方式工作,但我会坚持使用concat的建议

以上是关于Android围绕动态枢轴旋转视图的主要内容,如果未能解决你的问题,请参考以下文章

随着视图围绕视图中心旋转,向中心移动

使用 OpenGL 围绕枢轴点旋转

Android:通过拖动图像的特定部分来旋转图像

如何在 Pygame 中围绕偏离中心的枢轴旋转图像

Unity 对象不围绕枢轴点旋转

不使用 CSS3 围绕左下边缘枢轴旋转图像