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 -「洛谷 P6156」简单题

    Description Link. 求 \(\sum\limits_{i=1}^n\sum\limits_{j=1}^n(i+j)^kf(\gcd(i,j))\gcd(i,j)\). Solution ...

  2. Java 21的StringBuilder和StringBuffer新增了一个repeat方法

    发现Java 21的StringBuilder和StringBuffer中多了repeat方法: /** * @throws IllegalArgumentException {@inheritDoc ...

  3. 其它——Redis与Mysql双写一致性方案解析

    文章目录 一 前言 二 一致性方案 三 先更新数据库,再更新缓存 四 先删缓存,再更新数据库 五 先更新数据库,再删缓存 一 前言 首先,缓存由于其高并发和高性能的特性,已经在项目中被广泛使用.在读取 ...

  4. 03-11gR2单机通过RMAN恢复到RAC(未验证)

    1.在单机上做一个完全备份,并将备份集拷贝到RAC的第一个节点上. 2.强行启动到nomount 3.恢复spfile 4.创建pfile,修改pfile,重建spfile #####修改contro ...

  5. js仿百度搜索框

    1.js仿百度搜索框 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> &l ...

  6. 业务出海、高效传输、动态加速,尽在云栖大会「CDN与边缘计算」专场

    2023杭州·云栖大会,即将热力来袭. 一场云计算盛会,500+前沿话题,3000+科技展品,与阿里云一起,共赴72小时的Tech沉浸之旅. 今日,「CDN与边缘计算」Tech专场,重磅议题抢先知晓! ...

  7. 市二模&中考游记

    市二模 过于久远,记不清了.当时由于不想写游记浪费备考时间所以没写. 然后就是我现在真的记不住,算了不写了( 大概是 140+150+135.5+89+68 吧. 中考 内容以各类奇闻逸事为主( \( ...

  8. 自定义过滤器配置 Shiro 认证失败返回 json 数据

    by emanjusaka from ​ https://www.emanjusaka.top/archives/11 彼岸花开可奈何 本文欢迎分享与聚合,全文转载请留下原文地址. Shiro权限框架 ...

  9. 《HelloGitHub》第 91 期

    兴趣是最好的老师,HelloGitHub 让你对编程感兴趣! 简介 HelloGitHub 分享 GitHub 上有趣.入门级的开源项目. https://github.com/521xueweiha ...

  10. CF1401B [Ternary Sequence]

    Problem 题目简述 两个序列 \(A, B\).这两个序列都是由 \(0,1,2\) 这三个数构成. \(x_1,y_1,z_1\) 和 \(x_2,y_2,z_2\) 分别代表 \(A\) 序 ...