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. mysqlbinlog输出sql

    ./mysqlbinlog -v --base64-output=DECODE-ROWS ~/Downloads/tymysql2|grep -A4 'ALTER' >~/Downloads/a ...

  2. Solution Set -「CF 1514」

    「CF 1514A」Perfectly Imperfect Array Link. 就看序列中是否存在不为平方数的元素即可. #include<bits/stdc++.h> using n ...

  3. 国庆微信头像DIY:轻松打造个性化头像

    前言 国庆节马上要到了,今天就教你如何从0到1使用canvas生成国庆风微信头像. 本文包含以下内容: vue3项目搭建,需求分析 canvas合成图片原理 github自动化部署 开发过程遇到的问题 ...

  4. Http协议之libcurl实现

    一.libcurl简介 libcurl是一个跨平台的网络协议库,支持http, https, ftp, gopher, telnet, dict, file, 和ldap 协议.libcurl同样支持 ...

  5. HexConversion 二进制 八进制 十六进制 十进制

    public class HexConversion { // TODO Auto-generated method stub /** * TODO 进制转换. * * @param cc * htt ...

  6. 9. 用Rust手把手编写一个wmproxy(代理,内网穿透等), HTTP2改造篇之HPACK示例, 了解http2头信息如何处理

    9. 用Rust手把手编写一个wmproxy(代理,内网穿透等), HTTP2改造篇之HPACK示例, 了解http2头信息如何处理 项目 ++wmproxy++ gite: https://gite ...

  7. Python3中的printable

    import string characters = string.printable # printable 是用作字符串常量的预初始化字符串.里面包含所有的标点符号,数字 print(charac ...

  8. 唱衰这么多年,PHP 仍然还是你大爷!

    PHP 是个庞然大物. 尽管有人不断宣称 PHP "即将消亡". 但无法改变的事实是:互联网依然大量依赖 PHP.本文将通过大量的数据和事实告诉你为何 PHP 仍然在统治着互联网, ...

  9. mysql出现10061错误解决方法

    首先要关闭MYSQL服务 关闭你现在正在运行的mysql数据库,用结束mysql进程或者直接关闭mysql服务器都可以 1.开始菜单->运行(cmd)->寻径到MySQL文件中的bin目录 ...

  10. 搞懂Event Loop

    本文关键: V8是单线程的 任务队列排队执行 抽出io命令抽出到evenloop线程,消息线程,区别与主线程.(同步和异步) 微任务和宏任务执行顺序 重绘和回流 以上流程无限循环 可以这样理解,一个人 ...