对于我的问题,我在Github上准备了一个非常简单的测试应用程序。
为了简单起见,我删除了滑动、滚动约束和边缘效果(实际上在我的真实应用程序中工作得很好):
在我的测试应用程序中,自定义视图 仅支持滚动:mGestureDetector = new GestureDetector(context,
new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float dX, float dY) {
mBoardScrollX -= dX;
mBoardScrollY -= dY;
ViewCompat.postInvalidateOnAnimation(MyView.this);
return true;
}
});
使用两个手指进行捏合缩放(但焦点会被打破!):
mScaleDetector = new ScaleGestureDetector(context,
new ScaleGestureDetector.SimpleOnScaleGestureListener() {
@Override
public boolean onScale(ScaleGestureDetector scaleDetector) {
float focusX = scaleDetector.getFocusX();
float focusY = scaleDetector.getFocusY();
float factor = scaleDetector.getScaleFactor();
mBoardScrollX = mBoardScrollX + focusX * (1 - factor) * mBoardScale;
mBoardScrollY = mBoardScrollY + focusY * (1 - factor) * mBoardScale;
mBoardScale *= factor;
ViewCompat.postInvalidateOnAnimation(MyView.this);
return true;
}
});
最后,这里是绘制经过缩放和偏移的游戏板
Drawable
的代码:@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
canvas.scale(mBoardScale, mBoardScale);
canvas.translate(mBoardScrollX / mBoardScale, mBoardScrollY / mBoardScale);
mBoard.draw(canvas);
canvas.restore();
}
如果您尝试运行我的应用程序,您会注意到,在使用双指捏合手势缩放游戏板时,缩放焦点会跳动。
我的理解是,在缩放Drawable时,我需要通过调整mBoardScrollX和mBoardScrollY值来平移它,以便焦点在游戏板坐标中保持相同的位置。因此,我的计算是 -
该点的旧位置为:
-mBoardScrollX + focusX * mBoardScale
-mBoardScrollY + focusY * mBoardScale
新位置将会在此处:
-mBoardScrollX + focusX * mBoardScale * factor
-mBoardScrollY + focusY * mBoardScale * factor
通过解这两个线性方程,我得到:
mBoardScrollX = mBoardScrollX + focusX * (1 - factor) * mBoardScale;
mBoardScrollY = mBoardScrollY + focusY * (1 - factor) * mBoardScale;
然而,那并不起作用!
为了消除任何错误,我甚至尝试将焦点硬编码到我的自定义视图的中心 - 但仍然在缩放时游戏板的中心晃动:
float focusX = getWidth() / 2f;
float focusY = getHeight() / 2f;
我认为我错过了一些微小的东西,请帮助我。
我希望找到一个不使用 Matrix
的解决方案,因为我相信上述计算中确实缺少了一些微小的东西。是的,我已经学习了很多类似的代码,包括 Chris Banes 的 PhotoView 和 Google 的 InteractiveChart 示例。
双击更新:
pskink 提供的基于 Matrix
的解决方案非常好,但是我仍然有一个问题,那就是聚焦点错误 -
我尝试向自定义视图添加代码,在双击手势上将缩放增加100%:
public boolean onDoubleTap(final MotionEvent e) {
float[] values = new float[9];
mMatrix.getValues(values);
float scale = values[Matrix.MSCALE_X];
ValueAnimator animator = ValueAnimator.ofFloat(scale, 2f * scale);
animator.setDuration(3000);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animator){
float scale = (float) animator.getAnimatedValue();
mMatrix.setScale(scale, scale, e.getX(), e.getY());
ViewCompat.postInvalidateOnAnimation(MyView.this);
}
});
animator.start();
return true;
}
虽然每次调用“setScale”时都传递了焦点坐标,但缩放变化正确,焦点仍然是错误的。
例如,当我在屏幕中间双击时,结果会向右和向下移动得太远。
Matrix
会更好,可以看看Canvas.concat
方法,它展示了如何轻松地做一些漂亮的事情,而不需要那么多的数学计算和Canvas.scale
/Canvas.translate
/Canvas.rotate
调用。顺便说一句,界面看起来很不错;-) - pskinkgetMainView
方法中返回一些ImageView
以查看它的效果。 - pskinkmMatrix.getValues()
吗?我想知道在onRestoreInstanceState
中是否应该保存所有9个浮点值,或者更少的值是否足够? - Alexander FarberValueAnimator
很好用,只需使用scaleFactor = newScale / oldScale
就可以了,但是好吧,也许我会使用ObjectAnimator
- 这样可以减少样板代码。 - pskink