草庐IT

Android 两指旋转

coder 2023-06-06 原文

我正在尝试在 android 中实现两个手指旋转,但是它并没有像预期的那样工作。目标是像谷歌地球一样实现旋转(两指围绕焦点旋转图像)。目前我的旋转监听器看起来像这样:

 private class RotationGestureListener {
    private static final int INVALID_POINTER_ID = -1;
    private float fX, fY, sX, sY, focalX, focalY;
    private int ptrID1, ptrID2;

    public RotationGestureListener(){
        ptrID1 = INVALID_POINTER_ID;
        ptrID2 = INVALID_POINTER_ID;
    }

    public boolean onTouchEvent(MotionEvent event){
        switch (event.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                sX = event.getX();
                sY = event.getY();
                ptrID1 = event.getPointerId(0);
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                fX = event.getX();
                fY = event.getY();
                focalX = getMidpoint(fX, sX);
                focalY = getMidpoint(fY, sY);
                ptrID2 = event.getPointerId(event.getActionIndex());
                break;
            case MotionEvent.ACTION_MOVE:

                if(ptrID1 != INVALID_POINTER_ID && ptrID2 != INVALID_POINTER_ID){
                    float nfX, nfY, nsX, nsY;
                    nfX = event.getX(event.findPointerIndex(ptrID1));
                    nfY = event.getY(event.findPointerIndex(ptrID1));
                    nsX = event.getX(event.findPointerIndex(ptrID2));
                    nsY = event.getY(event.findPointerIndex(ptrID2));
                    float angle = angleBtwLines(fX, fY, nfX, nfY, sX, sY, nsX, nsY);
                    rotateImage(angle, focalX, focalY);
                    fX = nfX;
                    fY = nfY;
                    sX = nfX;
                    sY = nfY;
                }
                break;
            case MotionEvent.ACTION_UP:
                ptrID1 = INVALID_POINTER_ID;
                break;
            case MotionEvent.ACTION_POINTER_UP:
                ptrID2 = INVALID_POINTER_ID;
                break;
        }
        return false;
    }

    private float getMidpoint(float a, float b){
        return (a + b) / 2;
    }
    private float angleBtwLines (float fx1, float fy1, float fx2, float fy2, float sx1, float sy1, float sx2, float sy2){
        float angle1 = (float) Math.atan2(fy1 - fy2, fx1 - fx2);
        float angle2 = (float) Math.atan2(sy1 - sy2, sx1 - sx2);
        return (float) Math.toDegrees((angle1-angle2));
    }
}

但是,每当我旋转时,旋转角度会大得多,有时它会旋转到错误的一侧。有关如何解决此问题的任何想法?

顺便说一句,我在摩托罗拉 Atrix 上测试它,所以它没有触摸屏错误。

谢谢

最佳答案

类的改进:

  • 自旋转开始后返回的总角度
  • 删除不必要的功能
  • 简化
  • 仅在第二个指针向下后获取第一个指针的位置
public class RotationGestureDetector {
    private static final int INVALID_POINTER_ID = -1;
    private float fX, fY, sX, sY;
    private int ptrID1, ptrID2;
    private float mAngle;

    private OnRotationGestureListener mListener;

    public float getAngle() {
        return mAngle;
    }

    public RotationGestureDetector(OnRotationGestureListener listener){
        mListener = listener;
        ptrID1 = INVALID_POINTER_ID;
        ptrID2 = INVALID_POINTER_ID;
    }

    public boolean onTouchEvent(MotionEvent event){
        switch (event.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                ptrID1 = event.getPointerId(event.getActionIndex());
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                ptrID2 = event.getPointerId(event.getActionIndex());
                sX = event.getX(event.findPointerIndex(ptrID1));
                sY = event.getY(event.findPointerIndex(ptrID1));
                fX = event.getX(event.findPointerIndex(ptrID2));
                fY = event.getY(event.findPointerIndex(ptrID2));
                break;
            case MotionEvent.ACTION_MOVE:
                if(ptrID1 != INVALID_POINTER_ID && ptrID2 != INVALID_POINTER_ID){
                    float nfX, nfY, nsX, nsY;
                    nsX = event.getX(event.findPointerIndex(ptrID1));
                    nsY = event.getY(event.findPointerIndex(ptrID1));
                    nfX = event.getX(event.findPointerIndex(ptrID2));
                    nfY = event.getY(event.findPointerIndex(ptrID2));

                    mAngle = angleBetweenLines(fX, fY, sX, sY, nfX, nfY, nsX, nsY);

                    if (mListener != null) {
                        mListener.OnRotation(this);
                    }
                }
                break;
            case MotionEvent.ACTION_UP:
                ptrID1 = INVALID_POINTER_ID;
                break;
            case MotionEvent.ACTION_POINTER_UP:
                ptrID2 = INVALID_POINTER_ID;
                break;
            case MotionEvent.ACTION_CANCEL:
                ptrID1 = INVALID_POINTER_ID;
                ptrID2 = INVALID_POINTER_ID;
                break;
        }
        return true;
    }

    private float angleBetweenLines (float fX, float fY, float sX, float sY, float nfX, float nfY, float nsX, float nsY)
    {
        float angle1 = (float) Math.atan2( (fY - sY), (fX - sX) );
        float angle2 = (float) Math.atan2( (nfY - nsY), (nfX - nsX) );

        float angle = ((float)Math.toDegrees(angle1 - angle2)) % 360;
        if (angle < -180.f) angle += 360.0f;
        if (angle > 180.f) angle -= 360.0f;
        return angle;
    }

    public static interface OnRotationGestureListener {
        public void OnRotation(RotationGestureDetector rotationDetector);
    }
}

使用方法:

  1. 把上面的类放在一个单独的文件中RotationGestureDetector.java
  2. 在您的 Activity 类中创建一个 RotationGestureDetector 类型的私有(private)字段 mRotationDetector,并在初始化期间创建检测器的新实例(onCreate 方法例如)并给出一个实现onRotation方法的类作为参数(这里是activity = this)。
  3. 在方法onTouchEvent中,将接收到的触摸事件通过'mRotationDetector.onTouchEvent(event);'发送到手势检测器
  4. 在您的 Activity 中实现 RotationGestureDetector.OnRotationGestureListener 并在 Activity 中添加方法“public void OnRotation(RotationGestureDetector rotationDetector)”。在此方法中,通过 rotationDetector.getAngle()
  5. 获取角度

示例:

public class MyActivity extends Activity implements RotationGestureDetector.OnRotationGestureListener {
    private RotationGestureDetector mRotationDetector;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mRotationDetector = new RotationGestureDetector(this);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event){
        mRotationDetector.onTouchEvent(event);
        return super.onTouchEvent(event);
    }

    @Override
    public void OnRotation(RotationGestureDetector rotationDetector) {
        float angle = rotationDetector.getAngle();
        Log.d("RotationGestureDetector", "Rotation: " + Float.toString(angle));
    }

}

注意:

您还可以在 View 中使用 RotationGestureDetector 类来代替 Activity

关于Android 两指旋转,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10682019/

有关Android 两指旋转的更多相关文章

  1. 旋转矩阵的几何意义 - 2

    点向量坐标矩阵的几何意义介绍旋转矩阵的几何含义之前,先介绍一下点向量坐标矩阵的几何含义点:在一维空间下就是一个标量,如同一条直线上,以任意某一个位置为0点,以一定的尺度间隔为1,2,3...,相反方向为-1,-2,-3...;如此就形成了一维坐标系,这时候任何一个点都可以用一个数值表示,如点p1=5,即即从原点出发沿着x轴正方向移动5个尺度;点p2=-3,负方向移动3个尺度;     在一维坐标系上过原点做垂直于一维坐标系的直线,则形成了二维坐标系,此时描述一个点需要两个数值来表示点p3=(3,2),即从原点出发沿着x轴正方向移动3个尺度,在此基础上沿着y轴正方向移动两个尺度的位置就是点p3。

  2. Unity 3D 制作开关门动画,旋转门制作,推拉门制作,门把手动画制作 - 2

    Unity自动旋转动画1.开门需要门把手先动,门再动2.关门需要门先动,门把手再动3.中途播放过程中不可以再次进行操作觉得太复杂?查看我的文章开关门简易进阶版效果:如果这个门可以直接打开的话,就不需要放置"门把手"如果门把手还有钥匙需要旋转,那就可以把钥匙放在门把手的"门把手",理论上是可以无限套娃的可调整参数有:角度,反向,轴向,速度运行时点击Test进行测试自己写的代码比较垃圾,命名与结构比较拉,高手轻点喷,新手有类似的需求可以拿去做参考上代码usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;u

  3. 安卓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,打开命令窗口,并将路

  4. 欧拉角、旋转矩阵及四元数 - 2

    欧拉角、旋转矩阵及四元数1.简介2.欧拉角2.1欧拉角定义2.2右手系和左手系2.3转换流程3.旋转矩阵4.四元数4.1四元数与欧拉角和旋转矩阵之间等效变换4.2测试Matlab代码5.总结1.简介常用姿态参数表达方式包括方向余弦矩阵、欧拉轴/角参数、欧拉角、四元数以及罗德里格参数等。高分辨率光学遥感卫星主要采用欧拉角与四元数对姿态参数进行描述。这里着重讲解欧拉角、旋转矩阵和四元数。2.欧拉角2.1欧拉角定义欧拉角是表征刚体旋转的一种方法之一,由莱昂哈德·欧拉引入的三个角度,用于描述刚体相对于固定坐标系的方向。在摄影测量、空间科学或其它技术领域,一般用一组(三个)欧拉角描述两个空间坐标之间的旋

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

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

  6. 华为OD机试 -旋转骰子(Python) | 机试题算法思路 【2023】 - 2

    最近更新的博客华为OD机试-卡片组成的最大数字(Python)|机试题算法思路华为OD机试-网上商城优惠活动(一)(Python)|机试题算法思路华为OD机试-统计匹配的二元组个数(Python)|机试题算法思路华为OD机试-找到它(Python)|机试题算法思路华为OD机试-九宫格按键输入(Python)|机试算法备考思路华为OD机试-身高排序(Python)|备考思路使用说明参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,通过率才会高。华为OD清单查看地址:blog.csdn.net/hihell/catego

  7. 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

  8. 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外,用户可

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

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

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

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

随机推荐