如何在自定义视图中固定捏缩放焦点? [英] How to fix pinch zoom focal point in a custom view?
问题描述
对于我的问题,我已经准备了一个非常简单的测试应用在Github.
For my question I have prepared a very simple test app at Github.
为简单起见,我删除了浮动,滚动约束和边缘效果(在我的实际应用中效果很好)
For simplicity I have removed flinging, scroll constraints and edge effects (which actually work well in my real app):
and pinch zooming with 2 fingers (the focus is broken though!):
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
:
Finally, here the code drawing the scalled and offsetted game board 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();
}
如果尝试运行我的应用程序,您会注意到在用两根手指缩放缩放手势缩放游戏板的同时,缩放焦点会跳动.
If you try running my app, you will notice that while scaling the game board with two finger pinch zoom gesture works, the zoom focal point jumps around.
我的理解是,在缩放Drawable
时,我需要通过调整mBoardScrollX
和mBoardScrollY
值来平移它-以使焦点在游戏板坐标中位于同一点.所以我的计算是-
My understanding is that while scaling the Drawable
I need to pan it by adjusting the mBoardScrollX
and mBoardScrollY
values - so that the focus point stays at the same point in the game board coordinates. So my calculation is -
该点的旧位置是:
-mBoardScrollX + focusX * mBoardScale
-mBoardScrollY + focusY * mBoardScale
和 new 位置将位于:
-mBoardScrollX + focusX * mBoardScale * factor
-mBoardScrollY + focusY * mBoardScale * factor
通过求解这两个线性方程,我得到:
By solving these 2 linear equations I get:
mBoardScrollX = mBoardScrollX + focusX * (1 - factor) * mBoardScale;
mBoardScrollY = mBoardScrollY + focusY * (1 - factor) * mBoardScale;
但是那行不通!
为消除任何错误,我什至尝试将焦点硬编码到我的自定义视图的中间-并在缩放时仍然使游戏板中心摇摆:
To eliminate any errors I have even tried hardcoding the focal point to the middle of my custom view - and still the game board center sways around while scaling:
float focusX = getWidth() / 2f;
float focusY = getHeight() / 2f;
我想我缺少一些小东西,请帮助我.
I think I am missing something minor, please help me.
我更愿意在不使用Matrix
的情况下找到解决方案,因为我认为上述计算中确实缺少一些细微的东西.是的,我已经研究了很多可比较的代码,包括Chris Banes的 PhotoView 和<由Google提供的href ="https://android.googlesource.com/platform/development.git/+/refs/heads/master/samples/training/InteractiveChart/" rel ="nofollow noreferrer"> InteractiveChart 示例
I would prefer to find a solution without using Matrix
, because I believe that something really minor is missing in the above calculations. And yes, I have already studied a lot of comparable code, including the PhotoView by Chris Banes and the InteractiveChart example by Google.
两次TAP更新:
pskink的基于Matrix
的解决方案效果很好,但是我仍然遇到一个焦点错误的问题-
The Matrix
-based solution by pskink works very well, however I still have one issue with a wrong focal point -
I have tried to add code to the custom view to increase zoom by 100% on a double tap gesture:
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
调用.
And while the zoom changes correctly, the focal point is wrong again - even though the focal point coordinates are passed to each setScale
call.
例如,当我在屏幕中间点按两次时,结果将左右平移太远:
For example when I double tap in the middle of the screen, the result will be panned too far right and down:
推荐答案
使用Matrix
是真的更好的主意-代码更加简单,您不必证明自己的数学技能; -),了解如何使用Matrix#postTranslate
和Matrix#postScale
方法:
Using Matrix
is a really better idea - the code is much more simple and you dont have to prove your math skills ;-), see how Matrix#postTranslate
and Matrix#postScale
methods are used:
class MyView extends View {
private static final String TAG = "MyView";
private final ScaleGestureDetector mScaleDetector;
private final GestureDetector mGestureDetector;
private final Drawable mBoard;
private final float mBoardWidth;
private final float mBoardHeight;
private Matrix mMatrix;
public MyView(Context context) {
this(context, null, 0);
}
public MyView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MyView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mBoard = ResourcesCompat.getDrawable(context.getResources(), R.drawable.chrome, null);
mBoardWidth = mBoard.getIntrinsicWidth();
mBoardHeight = mBoard.getIntrinsicHeight();
mBoard.setBounds(0, 0, (int) mBoardWidth, (int) mBoardHeight);
mMatrix = new Matrix();
mScaleDetector = new ScaleGestureDetector(context, scaleListener);
mGestureDetector = new GestureDetector(context, listener);
}
ScaleGestureDetector.OnScaleGestureListener scaleListener = new ScaleGestureDetector.SimpleOnScaleGestureListener() {
@Override
public boolean onScale(ScaleGestureDetector scaleDetector) {
float factor = scaleDetector.getScaleFactor();
mMatrix.postScale(factor, factor, getWidth() / 2f, getHeight() / 2f);
ViewCompat.postInvalidateOnAnimation(MyView.this);
return true;
}
};
GestureDetector.OnGestureListener listener = new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float dX, float dY) {
mMatrix.postTranslate(-dX, -dY);
ViewCompat.postInvalidateOnAnimation(MyView.this);
return true;
}
};
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
float scale = Math.max(w / mBoardWidth, h / mBoardHeight);
mMatrix.setScale(scale, scale);
mMatrix.postTranslate((w - scale * mBoardWidth) / 2f, (h - scale * mBoardHeight) / 2f);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.save();
canvas.concat(mMatrix);
mBoard.draw(canvas);
canvas.restore();
}
@Override
@SuppressLint("ClickableViewAccessibility")
public boolean onTouchEvent(MotionEvent e) {
mGestureDetector.onTouchEvent(e);
mScaleDetector.onTouchEvent(e);
return true;
}
}
这篇关于如何在自定义视图中固定捏缩放焦点?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!