安卓。画布缩放和平移

2024-03-13

我创建了自定义视图,您可以在其中触摸和缩放它。 大部分作品都是在这个的帮助下创作的post http://android-developers.blogspot.com/2010/06/making-sense-of-multitouch.html

接下来我观察到,如果我想放大图像,它总是放大到左上角。 这是我的onDraw() method:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    canvas.save();
    canvas.translate(mPosX, mPosY);
    canvas.scale(mScaleFactor, mScaleFactor);

    // actual drawing

    canvas.getClipBounds(mRect);
    canvas.restore();
}

所以这是正常行为canvas.scale(px, py);我希望从我的角度来看缩放到中心点(稍后我将采取focalX, focalY坐标,但现在只是到中心)。我决定更换

canvas.scale(mScaleFactor, mScaleFactor); to

canvas.scale(mScaleFactor, mScaleFactor, getWidth()/2-mPosX, getHeight()/2-mPosY);

现在它在图像中间完美缩放。但它以某种方式影响我的 mPosX、mPosY 坐标。我附上一张图片以便更好地理解。

左上角相当于mPoX=0,mPosY=0,就可以了。

接下来,如果我缩小,接下来会发生:

现在它决定左上角相当于mPosX=0,mPosY=0(左上角)。但按照我的逻辑,它应该类似于(mPosX=100,mPosY=130)或类似的东西。

所以我的问题是我正在从实际的内容视图中滑出。我应该怎么做才能防止这种行为?


我对接触点计算和参考有同样的问题这个答案 https://stackoverflow.com/questions/12479859/view-with-horizontal-and-vertical-pan-drag-and-pinch-zoom/38205219#38205219,我使用指标以自己的方式解决了这个问题。上面的答案提供了更好的捏缩放和拖动功能,但没有提供正确的触摸点。这是完整的代码,忽略我自己的变量。

package axis.nbapp;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Region;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;

import java.io.IOException;

public class NewView extends ViewGroup {
    float dx;
    float dy;
    float scaleFac;

    Region r;
    private Path mPath;
    private Paint mPaint;
    private Path mTransformedPath;
    Stencil stencil;
    private Paint cColor;
    boolean scaling;
    Canvas bmcanvas;
    SVG svg = null;
    int viewWidth;
    int viewHeight;
    float svgWidth;
    float svgHeight;

    // States.
    private static final byte NONE = 0;
    private static final byte DRAG = 1;
    private static final byte ZOOM = 2;

    private byte mode = NONE;

    // Matrices used to move and zoom image.
    private Matrix matrix = new Matrix();
    private Matrix matrixInverse = new Matrix();
    private Matrix savedMatrix = new Matrix();


    // Parameters for zooming.
    private PointF start = new PointF();
    private PointF mid = new PointF();
    private float oldDist = 1f;
    private float[] lastEvent = null;
    private long lastDownTime = 0l;

    private float[] mDispatchTouchEventWorkingArray = new float[2];
    private float[] mOnTouchEventWorkingArray = new float[2];


    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        mDispatchTouchEventWorkingArray[0] = ev.getX();
        mDispatchTouchEventWorkingArray[1] = ev.getY();
        mDispatchTouchEventWorkingArray = screenPointsToScaledPoints(mDispatchTouchEventWorkingArray);
        ev.setLocation(mDispatchTouchEventWorkingArray[0], mDispatchTouchEventWorkingArray[1]);
        return super.dispatchTouchEvent(ev);
    }

    public NewView(Context context) {
        super(context);
        init(context);
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);


    }

    public NewView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public NewView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }


    private void init(Context context) {

    }


    /**
     * Determine the space between the first two fingers
     */
    private float spacing(MotionEvent event) {
        float x = event.getX(0) - event.getX(1);
        float y = event.getY(0) - event.getY(1);
        return (float) Math.sqrt(x * x + y * y);
    }

    /**
     * Calculate the mid point of the first two fingers
     */
    private void midPoint(PointF point, MotionEvent event) {
        float x = event.getX(0) + event.getX(1);
        float y = event.getY(0) + event.getY(1);
        point.set(x / 2, y / 2);
    }

    private float[] scaledPointsToScreenPoints(float[] a) {
        matrix.mapPoints(a);
        return a;
    }

    private float[] screenPointsToScaledPoints(float[] a) {
        matrixInverse.mapPoints(a);
        return a;
    }


    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);
            if (child.getVisibility() != GONE) {
                child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight());
            }
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);
            if (child.getVisibility() != GONE) {
                measureChild(child, widthMeasureSpec, heightMeasureSpec);
            }
        }
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
        float[] values = new float[9];
        matrix.getValues(values);
        canvas.save();
        canvas.translate(values[Matrix.MTRANS_X], values[Matrix.MTRANS_Y]);
        canvas.scale(values[Matrix.MSCALE_X], values[Matrix.MSCALE_Y]);



        super.dispatchDraw(canvas);
        canvas.restore();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        // handle touch events here
        mOnTouchEventWorkingArray[0] = event.getX();
        mOnTouchEventWorkingArray[1] = event.getY();

        mOnTouchEventWorkingArray = scaledPointsToScreenPoints(mOnTouchEventWorkingArray);

        event.setLocation(mOnTouchEventWorkingArray[0], mOnTouchEventWorkingArray[1]);

        switch (event.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN:
                savedMatrix.set(matrix);
                mode = DRAG;
                lastEvent = null;
                long downTime = event.getDownTime();
                if (downTime - lastDownTime < 300l) {
                    float density = getResources().getDisplayMetrics().density;
                    if (Math.max(Math.abs(start.x - event.getX()), Math.abs(start.y - event.getY())) < 40.f * density) {
                        savedMatrix.set(matrix);
                        mid.set(event.getX(), event.getY());
                        mode = ZOOM;
                        lastEvent = new float[4];
                        lastEvent[0] = lastEvent[1] = event.getX();
                        lastEvent[2] = lastEvent[3] = event.getY();
                    }
                    lastDownTime = 0l;
                } else {
                    lastDownTime = downTime;
                }
                start.set(event.getX(), event.getY());
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                oldDist = spacing(event);
                if (oldDist > 10f) {
                    savedMatrix.set(matrix);
                    midPoint(mid, event);
                    mode = ZOOM;
                }
                lastEvent = new float[4];
                lastEvent[0] = event.getX(0);
                lastEvent[1] = event.getX(1);
                lastEvent[2] = event.getY(0);
                lastEvent[3] = event.getY(1);
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_POINTER_UP:
                mode = NONE;
                lastEvent = null;
                break;
            case MotionEvent.ACTION_MOVE:
                final float density = getResources().getDisplayMetrics().density;
                if (mode == DRAG) {
                    matrix.set(savedMatrix);
                    dx = event.getX() - start.x;
                    dy = event.getY() - start.y;
                    matrix.postTranslate(dx, dy);
                    matrix.invert(matrixInverse);
                    if (Math.max(Math.abs(start.x - event.getX()), Math.abs(start.y - event.getY())) > 20.f * density) {
                        lastDownTime = 0l;
                    }
                } else if (mode == ZOOM) {
                    if (event.getPointerCount() > 1) {
                        float newDist = spacing(event);
                        if (newDist > 10f * density) {
                            matrix.set(savedMatrix);
                            float scale = (newDist / oldDist);
                            scaleFac = newDist;
                            matrix.postScale(scale, scale, mid.x, mid.y);
                            matrix.invert(matrixInverse);
                        }
                    } else {
                        matrix.set(savedMatrix);
                        float scale = event.getY() / start.y;
                        scaleFac = scale;
                        matrix.postScale(scale, scale, mid.x, mid.y);
                        matrix.invert(matrixInverse);
                    }
                }

                break;
        }


        float[] values = new float[9];
        matrix.getValues(values);


        //int x = ((int)(event.getX() - values[2]*values[Matrix.MSCALE_X]))/(int)values[0];
        //int y = ((int)(event.getY() - values[5]*values[Matrix.MSCALE_Y]))/(int)values[4];

        int x = ((int)(event.getX() / values[Matrix.MSCALE_X] - (values[Matrix.MTRANS_X]/values[Matrix.MSCALE_X])));
        int y = ((int)(event.getY() / values[Matrix.MSCALE_Y] - (values[Matrix.MTRANS_Y]/values[Matrix.MSCALE_Y])));


        invalidate();
        return true;
    }


}

Check onTouchEvent您可以通过以下计算找到变换后的 x、y 触摸点。

 int x = ((int)(event.getX() / values[Matrix.MSCALE_X] - (values[Matrix.MTRANS_X]/values[Matrix.MSCALE_X])));
 int y = ((int)(event.getY() / values[Matrix.MSCALE_Y] - (values[Matrix.MTRANS_Y]/values[Matrix.MSCALE_Y])));
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

安卓。画布缩放和平移 的相关文章

随机推荐

  • 如何找到解决方案中的所有扩展方法?

    如何找到解决方案中的所有扩展方法 如果我这样做 我会在所有文件中搜索字符串 this 您的搜索字符串可能会根据您的格式选项而有所不同 EDIT 经过一点实验 以下内容似乎对我来说使用 在文件中查找 Ctrl Shift F 可以高精度工作
  • 启动/停止服务器时 MySQL Workbench 冻结

    I recently started using MySQL Server and Workbench both version 8 0 and noticed a strange issue When I load Workbench g
  • 如何以编程方式更改 Mac OS X 键盘布局?

    我的 Qt 应用程序支持在 Linux 和 Windows 上更改输入语言 我还想添加对更改 Mac OSX 中的输入语言的支持 不幸的是我没有任何关于 Mac SDK 的信息 我在 OS X 上的第一个也是最后一个工作是编译 Qt 并编译
  • 找不到 spring hibernate.cfg.xml

    Configuration configuration new Configuration configure hibernate cfg xml 我的配置文件在 src 我仍然收到这个错误 有人能发现我的错误吗 您正在使用具有标准目录布局
  • 来自文档根目录的 Route-Me 离线地图

    在我的应用程序中 有一个从 sqlite 文件呈现的离线地图 RMDBMapSource mapSrc RMDBMapSource alloc initWithPath map sqlite RMMapContents contents n
  • 如何在输入密码字段中插入复选框

    我希望我的网页在密码字段内显示一个复选框 用户单击复选框并查看文本形式的密码 取消选中后 再次输入其密码 This is what I want This is from the Ebay website login page 这就是我得到
  • java垃圾收集日志中的“GC--”是什么意思?

    我们打开了详细 GC 日志记录来跟踪已知的内存泄漏 并在日志中获取以下条目 3607872 687 GC 471630K gt 390767K 462208K 0 0325540 secs 3607873 213 GC 458095K gt
  • Python 3 如何检查一个值是否已经在列表中的列表中

    我的 Python 3 中有一个列表列表 mylist a x x b x x c x x x只是一些数据 我有我的代码可以做到这一点 for sublist in mylist if sublist 0 a sublist 1 subli
  • 如何与 AlarmManager 结合启动通知?

    我正在尝试弄清楚应该如何启动通知 创建通知不是我所要求的 而是一种在后台启动它的方法 这样它就不引人注目 并且用户可以做他们正在做的任何事情 它是日历 准确地说是提醒 同样重要的是要注意我正在使用AlarmManager 我应该使用什么方法
  • ng-repeat动画完成回调

    所以我有一个简单的 ng repeat 和在 javascript 中定义的输入动画 沙盒 http codepen io anri82 pen KwgGeY http codepen io anri82 pen KwgGeY Code d
  • 从 CompletableFuture.allof() 获取单独的结果

    我有一个类 它使用 CompletableFutures 向两个依赖服务发出并发请求 我的代码如下所示 Builder Slf4j public class TestClass NonNull private final ExecutorS
  • R 中分层样本的单向方差分析

    我有一个包含三组 a b c 的分层样本 这些样本是从较大的总体 N 中抽取的 所有组都有 30 个观察值 但它们在 N 中的比例不相等 因此它们的采样权重不同 我用surveyR 中的包来计算汇总统计数据和线性回归模型 并且想知道如何计算
  • 如何使用jquery在div内容更改时发出警报

    我想在 div 内容发生变化时发出警报消息 jquery api 是否为 div 元素提供了任何侦听器 绑定dom修改事件 document ready function test div bind DOMNodeInserted DOMS
  • Pandas 数据帧性能

    Pandas 确实很棒 但我真的很惊讶从 Pandas DataFrame 检索值的效率是多么低下 在下面的玩具示例中 即使是 DataFrame iloc 方法也比字典慢 100 倍以上 问题 这里的教训是否只是字典是查找值的更好方法 是
  • Angular 2打字稿调用javascript函数

    是否有正确的方法从 Angular 2 TypeScript 中的组件调用 JavaScript 函数 这是我的组件 import ElementRef AfterViewInit from angular core export clas
  • 无法运行 arquillian 测试

    我正在尝试使用 Arquillian 进行一些单元测试 但是我找不到它们在使用 Maven 部署时失败的原因 这是班级测试 package com ndeveloper spec test import javax inject Injec
  • 使用 SSL 访问 RDS - 不支持的记录版本 Unknown-0.0

    我正在使用亚马逊RDS MySQL并与SSL证书 默认证书位于http s3 amazonaws com rds downloads mysql ssl ca cert pem http s3 amazonaws com rds downl
  • PHPUnit 中的assertEquals 和assertSame 之间的区别?

    PHPUnit 包含一个assertEquals https phpunit de manual current en appendixes assertions html appendixes assertions assertEqual
  • 在 llvm 上运行 x86 程序

    是否可以使用llvm来运行x86程序 IE 我想使用 llvm 作为 x86 模拟器来运行 x86 程序 然后对 x86 程序进行检测 Thanks 我想你正在寻找LibCPU http LibCPU Org It has x86 前端 h
  • 安卓。画布缩放和平移

    我创建了自定义视图 您可以在其中触摸和缩放它 大部分作品都是在这个的帮助下创作的post http android developers blogspot com 2010 06 making sense of multitouch htm