【问题标题】:Android : Zoom by center pinchAndroid:按中心捏缩放
【发布时间】:2015-06-17 11:56:09
【问题描述】:

所以,我有用于井字游戏的 ScrollView、Horizo​​ntalScrollView 和 BoardView。

当用户缩放时,当我通过 ScalegestureDetector 将缩放重绘到单元格类别中时,缩放捏分配在屏幕的顶部,左侧,而不是捏的中心,我怎样才能将它分配到中心?

这是我在 github 中的项目:https://github.com/boyfox/TestTicTac.git

项目使用 com.android.support:appcompat-v7

有人有解决这个问题的办法吗?

【问题讨论】:

  • this 帖子的重复?
  • stackoverflow.com/questions/13961817/… --- 这里不存在我想要的答案,并且在我的情况下不存在原生比例画布逻辑,请查看我的测试项目。你能从我的测试项目中找出我的错误吗?

标签: android scale android-scrollview horizontalscrollview


【解决方案1】:

我认为您将希望转向更接近Android Dragging and Scaling examples 的实现(以及类似的问题here)。

这是您需要的开始。您可以像以前一样平移视图,现在在捏合手势的中心缩放视图。它是您的基本 BoardView,具有在来自here 的点击上绘制点的逻辑。绘制自定义 X 和 O 图标而不是圆圈应该很容易。

BoardView.java

public class BoardView extends View {

    private static final int JUST_SCALED_DURATION = 100;
    private static final int MAX_TOUCH_DURATION = 1000;
    private static final int MAX_TOUCH_DISTANCE = 10;
    private boolean stayedWithinTouchDistance;
    private float firstTouchX, firstTouchY;
    private long firstTouchTime, lastScaleTime;

    private float posX, posY, lastTouchX, lastTouchY;
    private ScaleGestureDetector scaleDetector;
    private float minScaleFactor = 0.1f;
    private float maxScaleFactor = 5.0f;
    private float scaleFactor = 1.0f;
    private float cellSize = 50.0f;
    private int numCells = 50;

    private ArrayList<Point> points;
    private Paint linePaint, pointPaint;

    public BoardView(Context context) {
        this(context, null, 0);
    }

    public BoardView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public BoardView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        points = new ArrayList<>();
        linePaint = new Paint();
        linePaint.setAntiAlias(true);
        linePaint.setColor(-65536);
        linePaint.setStrokeWidth(1.0f);
        pointPaint = new Paint();
        pointPaint.setAntiAlias(true);
        pointPaint.setColor(-65536);
        pointPaint.setStrokeWidth(1.0f);
        posX = linePaint.getStrokeWidth();
        posY = linePaint.getStrokeWidth();
        scaleDetector = new ScaleGestureDetector(context, new ScaleListener());
    }

    private static float distancePx(float x1, float y1, float x2, float y2) {
        float dx = x1 - x2;
        float dy = y1 - y2;
        return (float) Math.sqrt(dx * dx + dy * dy);
    }

    private float distanceDp(float distancePx) {
        return distancePx / getResources().getDisplayMetrics().density;
    }

    private Point coerceToGrid(Point point) {
        point.x = (int) ((((int) (point.x / cellSize)) * cellSize) + (cellSize / 2));
        point.y = (int) ((((int) (point.y / cellSize)) * cellSize) + (cellSize / 2));
        return point;
    }

    @Override
    public boolean onTouchEvent(@NonNull MotionEvent event) {
        scaleDetector.onTouchEvent(event);
        final int action = MotionEventCompat.getActionMasked(event);
        if (action == MotionEvent.ACTION_DOWN) {
            stayedWithinTouchDistance = true;
            firstTouchX = lastTouchX = event.getRawX();
            firstTouchY = lastTouchY = event.getRawY();
            firstTouchTime = System.currentTimeMillis();
        } else if (action == MotionEvent.ACTION_MOVE) {
            float thisTouchX = event.getRawX();
            float thisTouchY = event.getRawY();

            boolean justScaled = System.currentTimeMillis() - lastScaleTime < JUST_SCALED_DURATION;
            float distancePx = distancePx(firstTouchX, firstTouchY, thisTouchX, thisTouchY);
            stayedWithinTouchDistance = stayedWithinTouchDistance &&
                    distanceDp(distancePx) < MAX_TOUCH_DISTANCE;

            if (!stayedWithinTouchDistance && !scaleDetector.isInProgress() && !justScaled) {
                posX += thisTouchX - lastTouchX;
                posY += thisTouchY - lastTouchY;
                invalidate();
            }

            lastTouchX = thisTouchX;
            lastTouchY = thisTouchY;
        } else if (action == MotionEvent.ACTION_UP) {
            long touchDuration = System.currentTimeMillis() - firstTouchTime;
            if (touchDuration < MAX_TOUCH_DURATION && stayedWithinTouchDistance) {
                int[] location = {0, 0};
                getLocationOnScreen(location);
                float x = ((lastTouchX - posX - location[0]) / scaleFactor);
                float y = ((lastTouchY - posY - location[1]) / scaleFactor);
                points.add(coerceToGrid(new Point((int) x, (int) y)));
                invalidate();
            }
        }
        return true;
    }

    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            lastScaleTime = System.currentTimeMillis();
            float scale = detector.getScaleFactor();
            scaleFactor = Math.max(minScaleFactor, Math.min(scaleFactor * scale, maxScaleFactor));
            if (scaleFactor > minScaleFactor && scaleFactor < maxScaleFactor) {
                float centerX = detector.getFocusX();
                float centerY = detector.getFocusY();
                float diffX = centerX - posX;
                float diffY = centerY - posY;
                diffX = diffX * scale - diffX;
                diffY = diffY * scale - diffY;
                posX -= diffX;
                posY -= diffY;
                invalidate();
                return true;
            }
            return false;
        }
    }

    private float getScaledCellSize() {
        return scaleFactor * cellSize;
    }

    private float getScaledBoardSize() {
        return numCells * getScaledCellSize();
    }

    private void drawBoard(Canvas canvas) {
        for (int i = 0; i <= numCells; i++) {
            float total = getScaledBoardSize();
            float offset = getScaledCellSize() * i;
            canvas.drawLine(offset, 0, offset, total, linePaint);
            canvas.drawLine(0, offset, total, offset, linePaint);
        }
    }

    private void drawPoints(Canvas canvas) {
        for (Point point : points) {
            float x = point.x * scaleFactor;
            float y = point.y * scaleFactor;
            float r = getScaledCellSize() / 4;
            canvas.drawCircle(x, y, r, pointPaint);
        }
    }

    @Override
    public void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        float total = getScaledBoardSize();
        float edge = linePaint.getStrokeWidth();
        posX = Math.max(Math.min(edge, getWidth() - total - edge), Math.min(edge, posX));
        posY = Math.max(Math.min(edge, getHeight() - total - edge), Math.min(edge, posY));

        canvas.save();
        canvas.translate(posX, posY);
        drawBoard(canvas);
        drawPoints(canvas);
        canvas.restore();
    }

}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.example.client.BoardView
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</FrameLayout>

【讨论】:

  • 感谢您对我的问题的最佳回答!!!我会接受你的问题,但是,在缩放之后,我从屏幕上捏起来,中心缩放将移动到另一个地方。你能解释一下为什么吗?
  • 抱歉,一旦达到最小/最大比例因子,它仍在调整 posXposY,因此它会平移以调整未发生的比例。我已通过解决此问题的检查更新了答案。
  • 我明白什么时候出现自动移动到另一个地方。如果您将两根手指放在屏幕上并进行缩放,则第二根手指从屏幕上抬起,平移非常小(移动非常小),但第一根手指需要停留在屏幕上,您会看到问题。
  • 好的,我明白你的意思了。我再次编辑了答案以解决问题。基本上,当您抬起第一根手指然后第二根手指稍微移动时,缩放事件就结束了,导致屏幕稍微平移。我通过跟踪最后一个缩放事件并且在最后一个缩放事件之后不允许平移 100 毫秒来修复它。
猜你喜欢
  • 2019-01-22
  • 2012-03-20
  • 2012-12-04
  • 2013-01-06
  • 1970-01-01
  • 2014-02-16
  • 2016-11-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多