Android自定义视图缩放时如何防止偏移至原点?
问题:自定义View缩放时偏移至原点,无法保持居中
我参照Android官方教程实现了自定义View的缩放功能,缩放本身正常,但缩小时视图会偏移到(0,0)位置,希望缩放时保持原居中位置。以下是我的自定义View代码:
public class MyCustomView extends View { private ScaleGestureDetector mScaleDetector; private float mScaleFactor = 1.0f; private Paint paint; public MyCustomView(Context context) { super(context); init(context); } public MyCustomView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(context); } public MyCustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } private void init(Context context) { paint = new Paint(); paint.setStyle(Paint.Style.STROKE); mScaleDetector = new ScaleGestureDetector(context, new ScaleListener()); } @Override protected void onDraw(Canvas canvas) { float cx = getWidth()/2f; float cy = getHeight()/2f; canvas.save(); canvas.scale(mScaleFactor, mScaleFactor); canvas.drawRect(cx - 100, cy - 100, cx + 100, cy + 100, paint); canvas.restore(); } private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { @Override public boolean onScale(ScaleGestureDetector detector) { mScaleFactor *= detector.getScaleFactor(); // Don't let the object get too small or too large. mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f)); invalidate(); return true; } } @Override public boolean onTouchEvent(MotionEvent ev) { // Let the ScaleGestureDetector inspect all events. mScaleDetector.onTouchEvent(ev); return true; } }

解决方案
问题根源
当前代码中canvas.scale(mScaleFactor, mScaleFactor)默认以原点(0,0)作为缩放锚点,缩放时会将原本居中的矩形向原点拉扯,导致偏移。要保持居中,需将缩放锚点设置为视图中心,或根据手势动态调整锚点。
方案1:固定围绕视图中心缩放
只需修改onDraw方法,给scale方法添加视图中心作为锚点参数:
@Override protected void onDraw(Canvas canvas) { float cx = getWidth() / 2f; float cy = getHeight() / 2f; canvas.save(); // 将缩放锚点设置为视图中心,替代默认的(0,0) canvas.scale(mScaleFactor, mScaleFactor, cx, cy); canvas.drawRect(cx - 100, cy - 100, cx + 100, cy + 100, paint); canvas.restore(); }
修改后,缩放操作会始终围绕视图中心进行,矩形不会再偏移至原点。
方案2:支持围绕双指手势中心点缩放(更符合用户习惯)
如果需要让用户通过双指在任意位置缩放,视图围绕手势中心点变化,需添加偏移量记录并调整画布变换:
- 添加成员变量记录手势偏移:
private float mPosX = 0f; private float mPosY = 0f;
- 修改
ScaleListener,记录手势中心点并调整偏移量:
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { @Override public boolean onScale(ScaleGestureDetector detector) { float scaleFactor = detector.getScaleFactor(); float focusX = detector.getFocusX(); float focusY = detector.getFocusY(); // 计算偏移量,确保手势中心点缩放后位置不变 mPosX = focusX - (focusX - mPosX) * scaleFactor; mPosY = focusY - (focusY - mPosY) * scaleFactor; mScaleFactor *= scaleFactor; mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f)); invalidate(); return true; } @Override public boolean onScaleBegin(ScaleGestureDetector detector) { // 记录手势开始时的中心点 mPosX = detector.getFocusX(); mPosY = detector.getFocusY(); return true; } }
- 修改
onDraw方法,结合平移和缩放实现手势中心缩放:
@Override protected void onDraw(Canvas canvas) { float cx = getWidth() / 2f; float cy = getHeight() / 2f; canvas.save(); // 先平移到手势偏移位置,再缩放,最后平移回原中心确保初始位置正确 canvas.translate(mPosX, mPosY); canvas.scale(mScaleFactor, mScaleFactor); canvas.translate(-cx, -cy); canvas.drawRect(cx - 100, cy - 100, cx + 100, cy + 100, paint); canvas.restore(); }
这种方式能让缩放操作更贴合用户的交互预期,双指在哪里就围绕哪里缩放。
内容的提问来源于stack exchange,提问作者Abdo21
相关产品推荐
相关产品推荐

