Android的部分图片编辑应用中需要对图片进行移动、缩放和旋转,这些变化都依赖于触摸手势实现,而本文主要阐述移动、缩放和旋转手势的简单实现。

一、移动

首先需要从触摸事件(MotionEvent)中获取每个手指(Pointer)的坐标,随后计算这些坐标的中心(重心)位置,那么本次触摸事件与前一次触摸事件的中心距离就是手势的移动距离:

// calc center coordinates
float centerX = 0;
float centerY = 0;
for (int index = 0; index < pointerCount; index++) {
centerX += event.getX(index);
centerY += event.getY(index);
}
centerX /= pointerCount;
centerY /= pointerCount; // calc translation
float translateX = centerX - mPrevCenterX;
float translateY = centerY - mPrevCenterY; mPrevCenterX = centerX;
mPrevCenterY = centerY;

二、缩放

同样手势缩放也需要计算触摸事件的中心,其次计算每一个手指坐标与中心的距离,然后计算这些距离的平均值,那么本次触摸事件平均中心距离与上一次触摸事件的比值就是本次手势的缩放比例:

// omit calc center coordinates

// calc mean distance
double sumDistance = 0;
for (int index = 0; index < pointerCount; index++) {
float vx = event.getX(index) - centerX;
float vy = event.getY(index) - centerY; // calc distance to center coordinates
sumDistance += Math.sqrt(vx * vx + vy * vy);
}
float meanDistance = (float) (sumDistance / pointerCount); // calc scale
float scale = mPrevMeanDistance < 1e-5 ? 1 : meanDistance / mPrevMeanDistance; mPrevMeanDistance = meanDistance;

三、旋转

手势旋转同样需要计算触摸事件的中心,然后计算中心到每一个手指坐标的向量,计算每一个手指向量相对于上一次触摸事件中对应的手指向量的旋转角度,最后计算这些旋转角度的平均值,这个平均值就是本次手势的旋转角度:

// omit calc center coordinates

// calc rotate degrees
double sumRotateDegrees = 0;
for (int index = 0; index < pointerCount; index++) {
float vx = event.getX(index) - centerX;
float vy = event.getY(index) - centerY // calc angle between vectors
int pointerId = event.getPointerId(index);
sumRotateDegrees = Math.toDegrees(angleBetweenVectors(mPrevVx[pointerId], mPrevVy[pointerId], vx, vy)); mPrevVx[pointerId] = vx;
mPrevVy[pointerId] = vy;
}
float rotateDegrees = (float) (sumRotateDegrees / pointerCount);

两个向量叉乘公式:\(\boldsymbol v_1*\boldsymbol v_2=\sin\alpha|\boldsymbol v_1||\boldsymbol v_2|=v_{x1}*v_{y2}-v_{y1}*v_{x2}\),那么两个向量之间的夹角:

\[\begin{flalign*}
&\alpha=arcsin(\frac{\boldsymbol v_1*\boldsymbol v_2}{|\boldsymbol v_1||\boldsymbol v_2|})&
\end{flalign*}
\]
private static double cross(float vx1, float vy1, float vx2, float vy2) {
return vx1 * vy2 - vy1 * vx2;
} private static double angleBetweenVectors(float vx1, float vy1, float vx2, float vy2) {
double dist1 = Math.sqrt(vx1 * vx1 + vy1 * vy1);
double dist2 = Math.sqrt(vx2 * vx2 + vy2 * vy2); if (dist1 < 1e-5 || dist2 < 1e-5) {
return 0;
}
return Math.asin(cross(vx1, vy1, vx2, vy2) / dist1 / dist2);
}

不管是移动、缩放和旋转都需要保证本次触摸事件手指数量与上一次相等,如果不相等计算出来的值则跳动较大。

四、完整示例

本文的完整示例放在github仓库中,主要实现了图片的移动、旋转和缩放效果:

Android移动、缩放和旋转手势实现的更多相关文章

  1. 微信小程序的拖拽、缩放和旋转手势

    在开发中,有时会遇到像App中的手势那样的效果,下面就仿照App实现了一下. wxml部分: <view class="touch-container"> <vi ...

  2. Android动画及图片的缩放和旋转

    Android动画有2种,一种是Tween Animation,另一种是Frame Animation,先说说Tween动画吧. Tween动画是对视图对象中的内容进行一系列简单的转换,比如位置的移动 ...

  3. 使用手势对UIImageView进行缩放、旋转和移动

    // 添加所有的手势 - (void) addGestureRecognizerToView:(UIView *)view { // 旋转手势 UIRotationGestureRecognizer  ...

  4. 使用手势对UIImageView进行缩放、旋转和移动(转)

    原文地址:http://blog.csdn.net/crazy_frog/article/details/8664108 // 添加所有的手势 - (void) addGestureRecognize ...

  5. iOS--------手势识别的详细使用:拖动、缩放、旋转、点击、手势依赖、自定义手势

    1.UIGestureRecognizer介绍 手势识别在iOS上非常重要,手势操作移动设备的重要特征,极大的增加了移动设备使用便捷性. iOS系统在3.2以后,为方便开发这使用一些常用的手势,提供了 ...

  6. [Android] 使用Matrix矩阵类对图像进行缩放、旋转、对照度、亮度处理

        前一篇文章讲述了Android拍照.截图.保存并显示在ImageView控件中,该篇文章继续讲述Android图像处理技术,主要操作包含:通过打开相冊里的图片,使用Matrix对图像进行缩放. ...

  7. android学习笔记51——SQLite 手势Gesture

    手势Gesture 所谓手势,是指用户手指或触摸笔在触摸屏幕上的连续触碰行为. Androi对两种手势行为都提供了支持: 1.对于第一种手势而言,android提供了手势检测,并为手势检测提供了相应的 ...

  8. iOS 七大手势之轻拍,长按,旋转手势识别器方法

    一.监听触摸事件的做法   如果想监听一个view上面的触摸事件,之前的做法通常是:先自定义一个view,然后再实现view的touches方法,在方法内部实现具体处理代码 通过touches方法监听 ...

  9. 【原创】窥视懒人的秘密---android下拉刷新开启手势的新纪元

    小飒的成长史原创作品:窥视懒人的秘密---android下拉刷新开启手势的新纪元转载请注明出处 **************************************************** ...

  10. View的平移、缩放、旋转以及位置、坐标系

    原创 2015年05月12日 13:15:29 标签: Android / Scroll / Scale / Translation / Rotation 24733 Android开发中,经常会接触 ...

随机推荐

  1. Solution -「营业」「ABC 209F」Deforestation

    Description   Link.   有 \(n\) 棵树,每棵的高度为 \(a(i)\),看到一棵树对答案的贡献为 \(a(i-1)+a(i)+a(i+1)\)(未定义范围为 \(0\)),求 ...

  2. Solution Set -「CF 1486」

    「CF 1486A」Shifting Stacks Link. 考虑最少需要操作多少次后判断. #include<map> #include<cstdio> using nam ...

  3. Kubeflow基础知识

    kubeflow 基础知识 kubeflow 简介 kubeflow是谷歌开源的MLOps开源平台,其中包含的不同组件代表了机器学习生命周期的不同阶段. 下图是kubeflow组织ML工作流程: ku ...

  4. Composite 组合模式简介与 C# 示例【结构型3】【设计模式来了_8】

    〇.简介 1.什么是组合设计模式? 一句话解释:   针对树形结构的任意节点,都实现了同一接口,他们具有相同的操作,可以通过某一操作来遍历全部节点. 组合模式通过使用树形结构来组合对象,用来表示部分以 ...

  5. python-显示张量(tensorflow)的具体的值

    ------------恢复内容开始------------ # 方法1 a = tf. random.normal ([4,4],mean=0.1,stddev=1) with tf.Session ...

  6. 归并排序 nO(lgn) 审核中

    大家好,我是蓝胖子,我一直相信编程是一门实践性的技术,其中算法也不例外,初学者可能往往对它可望而不可及,觉得很难,学了又忘,忘其实是由于没有真正搞懂算法的应用场景,所以我准备出一个系列,囊括我们在日常 ...

  7. Lucky Array 题解

    Lucky Array 题目大意 维护一个序列,支持以下操作: 区间加一个大于 \(0\) 的数. 区间查询有多少个数位上只包含 \(4\) 或 \(7\) 的数. 思路分析 看起来很不可做,但考虑到 ...

  8. 钉钉OA自定义审批流的创建和使用

    前言 大家好!我是sum墨,一个一线的底层码农,平时喜欢研究和思考一些技术相关的问题并整理成文,限于本人水平,如果文章和代码有表述不当之处,还请不吝赐教. 钉钉作为一款办公软件,审批功能是它的核心功能 ...

  9. docker本地仓库-registry

    Docker本地私有仓库实战: docker仓库主要用于存放docker镜像,docker仓库分为公有仓库和私有仓库,基于registry可以搭建本地私有仓库,使用私有仓库的优点如下: 节省网络带宽, ...

  10. Unity anchoredPosition转localPosition

    参考 https://zhuanlan.zhihu.com/p/119442308 在已经有结果的情况下,先捋一下unity对相关字段的注释就能得出很多公式 (rectMinPos表示左下角在父节点坐 ...