jjzjj

android - canvas.scale(mScaleFactor,mScaleFactor,detector.getFocusX(),detector.getFocusY()) 拖动限制搞砸了吗?

coder 2023-12-18 原文

我试图通过在 google 和此处搜索 SO 来开发自定义 ImageView

一切正常,除非我尝试使用 mdetector.getFocusX()mdetector.getFocusY() 缩放 Canvas 。
缩放正确发生,但拖动计算搞砸了。

当我在下面的代码中使用 canvas.scale(mScaleFactor,mScaleFactor,gx,gy) 时,整个地方都发生了拖动,并且可以看到后面的黑屏。
我想将拖动限制在屏幕边界内。

在我的代码中,当 canvas.scale(mScaleFactor,mScaleFactor) 时,绑定(bind)计算适用于放大,即:当它从 (0,0)<> 不使用枢轴点。

代码如下:

import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.widget.ImageView;

/* @description : Custom View Zoom
 *
 */

public class ZoomView extends ImageView {

    // Maximum and Minimum Zoom
    private static float MIN_ZOOM = 1.0f;
    private static float MAX_ZOOM = 3.0f;

    //Different Operation to be used 
    private final int NONE_OPERATION=0;
    private final int DRAG_OPERATION=1;
    private final int ZOOM_OPERATION=2;
    private float mWidth= 1047;
    private float mHeight=800; 
    private boolean dragged=true;

    // Mode to select the operation
    private int mode;

    //Track X and Y coordinate of the finger when it first touches the screen
    private float mInitialX = 0f;
    private float mInitialY = 0f;

    //Track the amount to translate(Drag) the canvas along the X and the Y coordinate
    private float mTranslateX = 0f;
    private float mTranslateY = 0f;

    //Track the last translated X and the Y coordinate while panning so that canvas does not get the jerk (Issue was happening when we change the position again and again ) 
    private float mPreviousTranslateX = 0f;
    private float mPreviousTranslateY = 0f;

    //ScalingFactor i.e. Amount of Zoom
    private float mScaleFactor = 1.0f;
    float gx=0,gy=0;





    private ScaleGestureDetector mDetector;


    // Called if used from code 
    public ZoomView(Context context) {
            super(context);
        // Intialize ScaleGestureDetector
        mDetector = new ScaleGestureDetector(context, new ZoomListener());

    }

    //Called if used from XML
    public ZoomView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mDetector = new ScaleGestureDetector(context, new ZoomListener());

    }

    //Everything that is going to reflect on the screen will happen in on draw
    @Override
    protected void onDraw(Canvas canvas) {


    //Save the canvas to set the scaling factor returned from detector
        canvas.save();
        canvas.scale(mScaleFactor, mScaleFactor,gx,gy);
        Log.d("Print", "mScaleFactor::" + mTranslateX);
        Log.d("Print", " mTranslateX::" + (mScaleFactor - 1) * mWidth);
    //Check the bound that we never pan past the top of left edge of the
    if((mTranslateX) < 0) {
        mTranslateX=0;

    }
    ////Check the right bound.
    // eg : Height of display is 1280. When it is zoom by 2 it is 1280 . when it is zoom by 3 it is  2560
    // Compare translateX times -1 to (scaleFactor - 1) * displayWidth. 
    //If translateX is greater than that value, then it has gone over the bound. So we set the value of translateX to (1 - scaleFactor) times the display width. 
    // Notice that the terms are interchanged... it's the same as doing -1 * (scaleFactor - 1) * displayWidth
    else if((mTranslateX) > (mScaleFactor - 1) * mWidth){
        mTranslateX=(mScaleFactor - 1 )* mWidth;
        Log.d("Print", " InDraw mTranslateX::" + mTranslateX);
    }

    if((mTranslateY)< 0) 
        mTranslateY=0;
/*  else if((mTranslateY) > (mScaleFactor - 1) * mHeight)
        mTranslateY= (mScaleFactor-1)* mHeight;*/



    //divide by the scale factor here,
    //otherwise it will end up with excessive panning based on our zoom level since the translation amount also gets scaled according to how much we've zoomed into the canvas.
      canvas.translate(mTranslateX / mScaleFactor, mTranslateY / mScaleFactor);    

    // Draw anything more if needed here ....

      // Restore the canvas to balance the save Canvas which removes all the last modification before save.


      super.onDraw(canvas);

        canvas.restore();
    }


    //handle the touch event of the view with the detector to get the scalingFactor and also keep the track of 
    // the touch events like drag and zoom event using booleans 
    @Override
    public boolean onTouchEvent(MotionEvent event) {

        // Handles all type of motion-events possible
        switch(event.getAction() & MotionEvent.ACTION_MASK) {

        case MotionEvent.ACTION_DOWN:
            // Event occurs when the first finger is pressed on the Screen

            // setting the mode to Drag Operation
            mode = DRAG_OPERATION;

            // Store the initial X and Y of the first finger when touches on the Screen. Take the difference with the previous translation so as to avoid the jerk in canvas.
            //Initial difference will be X and Y since previousTranslation will be ZERO.
            mInitialX = event.getX() - mPreviousTranslateX;
            mInitialY = event.getY() - mPreviousTranslateY;

            break;
        case MotionEvent.ACTION_MOVE:
            // Event occurs when the finger move across the screen and also when the finger is kept pressed on the screen

            // Update the translate value constantly as the event is occured at every move
            mTranslateX = event.getX() - mInitialX;             // Translate value is calculated by diff from current and initial
            mTranslateY = event.getY() - mInitialY;





            Log.d("Print", " TranslateX::" + mTranslateX + " Translate Y::" + mTranslateY);

            // If finger is kept pressed it will still consider the move so to avoid that use this value
            //Initial X and Initial Y can not be used directly because they were adjusted  using the previous translation values. So need to add those
            // values to InitialX and InitialY so that the actual coordinates of the finger are retrieved.
            // Using distance Forumla
            double distance = Math.sqrt(Math.pow(event.getX() - (mInitialX + mPreviousTranslateX), 2) + Math.pow(event.getY() - (mInitialY + mPreviousTranslateY), 2));
                if(distance > 0) {
            dragged = true;

            } 



            break;
        case MotionEvent.ACTION_POINTER_DOWN:
            //Event occurs when the second finger is pressed down


            // If second finger is pressed on the screen with the first set the Mode to Zoom operation
            mode=ZOOM_OPERATION;

            break;

        case MotionEvent.ACTION_UP: 
            //Event occurs when all the finger are taken of the screen

            //If all the fingers are taken up there will be no operation 
            mode = NONE_OPERATION;
            dragged= false;
            // All the operations are done.Store the previousTranslate value here. ( Might not need at the time of second finger down ??)
            mPreviousTranslateX = mTranslateX;
            mPreviousTranslateY = mTranslateY;

            break;

        case MotionEvent.ACTION_POINTER_UP:
            // Event occurs when the second finger is taken of the screen while first finger is pressed 


            // Second finger is taken up stop zooming and again Drag Operation 
            mode=DRAG_OPERATION;
            break;
        }

        // give the event to the mDetector to get the scaling Factor
        mDetector.onTouchEvent(event);

        //We need to invalidate the canvas to redraw itself for the changes.Here we need to invalidate only when zoom is done and drag operation has happened 
        //or else for the Zoom which was happening in the onScale function 
        if((mode==DRAG_OPERATION && mScaleFactor!=1f && dragged ) || mode==ZOOM_OPERATION)
        {

            invalidate();
        }



        // we are handling the touch event 
        return true;
    }



/* @name : ZoomListener
 * @description : Class which defines the listener for ScaleGestureDetector while extending abstract 
 * 
 */
    private class ZoomListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {

/*
 * ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 *
 * @description: Method gives the scaleFactor from the detector
 *
 * ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 */
        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            // getting the scaleFactor from the detector
            mScaleFactor *= detector.getScaleFactor();              // gives the scaling factor from the previous scaling to the current
            Log.d("Print", "detector scaling Factor" + mScaleFactor);


            gx = detector.getFocusX();
            gy = detector.getFocusY();

            // Limit the scale factor in the MIN and MAX bound
            mScaleFactor= Math.max(Math.min(mScaleFactor, MAX_ZOOM),MIN_ZOOM);
            Log.d("Print", "Bounded scaling Factor" + mScaleFactor);

            /*//Force canvas to redraw itself only if the one event is to happen (say Zooming only ) else do not invalidate here for multi operations
               As what we de for scrolling or panning will not reflect here. So we will add this in onDraw method 
            invalidate();*/

            // we have handle the onScale 
            return true;
        }

        @Override
            public void onScaleEnd(ScaleGestureDetector detector) {

                super.onScaleEnd(detector);
            }
    }
}

最佳答案

我用这个:

public void checkZoom(){
    float[] values = new float[9];
    matrix.getValues(values);
    float scaleX = values[Matrix.MSCALE_X];
    if(scaleX > MAX_ZOOM) {
        setZoom(MAX_ZOOM);
    } 
    else if(scaleX < MIN_ZOOM) {
        setZoom(MIN_ZOOM);
    }
    limitCorners();
}

private void limitCorners() {
    viewWidth = this.getWidth();
    viewHeight = this.getHeight();
    float []m = new float[9];
    matrix.getValues(m);
    float transX = m[Matrix.MTRANS_X];
    float transY = m[Matrix.MTRANS_Y];
    float fixTransX = getFixTrans(transX, viewWidth, getImageWidth());
    float fixTransY = getFixTrans(transY, viewHeight, getImageHeight());
    if (fixTransX != 0 || fixTransY != 0) {
        matrix.postTranslate(fixTransX, fixTransY);
    }
    matrix.getValues(m);
    if (getImageWidth() < viewWidth) {
        m[Matrix.MTRANS_X] = (viewWidth - getImageWidth()) / 2;
    }
    if (getImageHeight() < viewHeight) {
        m[Matrix.MTRANS_Y] = (viewHeight - getImageHeight()) / 2;
    }
    matrix.setValues(m);
}

private float getFixTrans(float trans, float viewSize, float contentSize) {
    float minTrans, maxTrans;
    if (contentSize <= viewSize) {
        minTrans = 0;
        maxTrans = viewSize - contentSize;
    } else {
        minTrans = viewSize - contentSize;
        maxTrans = 0;
    }
    if (trans < minTrans)
        return -trans + minTrans;
    if (trans > maxTrans)
        return -trans + maxTrans;
    return 0;
}

/** 获取图片的宽度(bitmapWidthZoom)/

float getImageWidth(){
    float []m = new float[9];
    matrix.getValues(m);
    return m[Matrix.MSCALE_X]*bitmap.getWidth();
}

/** 获取图片高度(位图高度缩放)/

float getImageHeight(){
    float []m = new float[9];
    matrix.getValues(m);
    return m[Matrix.MSCALE_Y]*bitmap.getHeight();
}

关于android - canvas.scale(mScaleFactor,mScaleFactor,detector.getFocusX(),detector.getFocusY()) 拖动限制搞砸了吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10069817/

有关android - canvas.scale(mScaleFactor,mScaleFactor,detector.getFocusX(),detector.getFocusY()) 拖动限制搞砸了吗?的更多相关文章

  1. 安卓apk修改(Android反编译apk) - 2

    最近因为项目需要,需要将Android手机系统自带的某个系统软件反编译并更改里面某个资源,并重新打包,签名生成新的自定义的apk,下面我来介绍一下我的实现过程。APK修改,分为以下几步:反编译解包,修改,重打包,修改签名等步骤。安卓apk修改准备工作1.系统配置好JavaJDK环境变量2.需要root权限的手机(针对系统自带apk,其他软件免root)3.Auto-Sign签名工具4.apktool工具安卓apk修改开始反编译本文拿Android系统里面的Settings.apk做demo,具体如何将apk获取出来在此就不过多介绍了,直接进入主题:按键win+R输入cmd,打开命令窗口,并将路

  2. ruby-on-rails - Facebook Canvas 应用程序不保存 session - 2

    我制作了一个测试facebook应用程序只是为了玩玩,我正在使用session来存储身份验证。我正在使用omniauth。当我从http://fbbtest.heroku.com/登录时然后刷新页面,session仍然保存,它说我已经登录。当我从Canvas上尝试时http://apps.facebook.com/herokutestapp/它让我登录,重定向回来并说我已登录但是当我手动刷新它然后说我没有登录。我必须对rails3中的session做一些特别的事情以便它也可以在FacebookCanvas?这是我目前在我的Controller和View中拥有的内容defindexend

  3. Android Studio开发之使用内容组件Content获取通讯信息讲解及实战(附源码 包括添加手机联系人和发短信) - 2

    运行有问题或需要源码请点赞关注收藏后评论区留言一、利用ContentResolver读写联系人在实际开发中,普通App很少会开放数据接口给其他应用访问。内容组件能够派上用场的情况往往是App想要访问系统应用的通讯数据,比如查看联系人,短信,通话记录等等,以及对这些通讯数据及逆行增删改查。首先要给AndroidMaifest.xml中添加响应的权限配置 下面是往手机通讯录添加联系人信息的例子效果如下分成三个步骤先查出联系人的基本信息,然后查询联系人号码,再查询联系人邮箱代码 ContactAddActivity类packagecom.example.chapter07;importandroid

  4. Android 10.0 设置默认launcher后安装另外launcher后默认Launcher失效的功能修复 - 2

    1.前言 在10.0的系统rom定制化开发中,在系统中有多个launcher的时候,会在开机进入launcher的时候弹窗launcher列表,让用户选择进入哪个launcher,这样显得特别的不方便所以产品开发中,要求用RoleManager的相关api来设置默认Launcher,但是在设置完默认Launcher以后,在安装一款Launcher的时候,默认Launcher就会失效,在系统设置的默认应用中Launcher选项就为空,点击home键的时候会弹出默认Launcher列表,让选择进入哪个默认Launcher.所以需要从安装Launcher的流程来分析相关的设置。来解决问题设置默认La

  5. AiBote 2022 新研发的自动化框架,支持 Android 和 Windows 系统。速度非常快 - 2

    Ai-Bot基于流行的Node.js和JavaScript语言的一款新自动化框架,支持Windows和Android自动化。1、Windowsxpath元素定位算法支持支持Windows应用、.NET、WPF、Qt、Java和Electron客户端程序和ie、edgechrome浏览器2、Android支持原生APP和H5界面,元素定位速度是appium十倍,无线远程自动化操作多台安卓设备3、基于opencv图色算法,支持找图和多点找色,1080*2340全分辨率找图50MS以内4、内置免费OCR人工智能技术,无限制获取图片文字和找字功能。5、框架协议开源,除官方node.jsSDK外,用户可

  6. Android Gradle 7.1+新版本依赖变化 - 2

    前一段时间由于工作需要把可爱的小雪狐舍弃了,找到了小蜜蜂。但是新版本的小蜜蜂出现了很多和旧版本不一样的位置。1.功能位置迁移,原来在工程build.gradle的buildscript和allprojects移动至setting.gradle并改名为pluginManagement和dependencyResolutionManagement。里面的东西依旧可以按照原来的copy过来。pluginManagement{repositories{gradlePluginPortal()google()mavenCentral()}}dependencyResolutionManagement{r

  7. 前端基于DOM或者Canvas实现页面水印 - 2

    🐱个人主页:不叫猫先生🙋‍♂️作者简介:前端领域新星创作者、阿里云专家博主,专注于前端各领域技术,共同学习共同进步,一起加油呀!💫系列专栏:vue3从入门到精通、TypeScript从入门到实践📢资料领取:前端进阶资料以及文中源码可以找我免费领取🔥前端学习交流:博主建立了一个前端交流群,汇集了各路大神,一起交流学习,期待你的加入!(文末有我wx或者私信)目录前言一、vue自定义指令directive讲解二、基于DOM的实现方式1.思路整理2.新建index.vue3.新建`directives`文件4.在`directives`文件下创建`index.ts`文件5.在`main.ts`中全局引

  8. ruby - Ruboto 的最佳教程(适用于 Android 的 ruby​​)? - 2

    关闭。这个问题不符合StackOverflowguidelines.它目前不接受答案。要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于StackOverflow来说是偏离主题的,因为它们往往会吸引自以为是的答案和垃圾邮件。相反,describetheproblem以及迄今为止为解决该问题所做的工作。关闭9年前。Improvethisquestion我几乎用完了Ruby,但现在想试试Ruboto,android上的ruby​​。谷歌未能给我足够的(几乎没有结果)。所以任何人都可以分享一些关于Ruboto的教程。

  9. Android Studio 解决Could not resolve com.android.tools.build:gradle:7.4.2问题 - 2

    Aproblemoccurredconfiguringrootproject'MyApplication2'.>Couldnotresolveallfilesforconfiguration':classpath'.  >Couldnotresolvecom.android.tools.build:gradle:7.4.2.   Requiredby:     project:>com.android.application:com.android.application.gradle.plugin:7.4.2     project:>com.android.library:com.andr

  10. Android对话框的详细介绍(提示对话框,自定义对话框) - 2

    简介:我们都知道在Android开发中,当我们的程序在与用户交互时,用户会得到一定的反馈,其中以对话框的形式的反馈还是比较常见的,接下来我们来介绍几种常见的对话框的基本使用。前置准备:(文章最后附有所有代码)我们首先先写一个简单的页面用于测试这几种Dialog(对话框)代码如下,比较简单,就不做解释了一、提示对话框(即最普通的对话框)首先我们给普通对话框的按钮设置一个点击事件,然后通过AlertDialog.Builder来构造一个对象,为什么不直接Dialog一个对象,是因为Dialog是一个基类,我们尽量要使用它的子类来进行实例化对象,在实例化对象的时候,需要将当前的上下文传过去,因为我这

随机推荐