Android移动、缩放和旋转手势实现
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}\),那么两个向量之间的夹角:
&\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移动、缩放和旋转手势实现的更多相关文章
- 微信小程序的拖拽、缩放和旋转手势
在开发中,有时会遇到像App中的手势那样的效果,下面就仿照App实现了一下. wxml部分: <view class="touch-container"> <vi ...
- Android动画及图片的缩放和旋转
Android动画有2种,一种是Tween Animation,另一种是Frame Animation,先说说Tween动画吧. Tween动画是对视图对象中的内容进行一系列简单的转换,比如位置的移动 ...
- 使用手势对UIImageView进行缩放、旋转和移动
// 添加所有的手势 - (void) addGestureRecognizerToView:(UIView *)view { // 旋转手势 UIRotationGestureRecognizer ...
- 使用手势对UIImageView进行缩放、旋转和移动(转)
原文地址:http://blog.csdn.net/crazy_frog/article/details/8664108 // 添加所有的手势 - (void) addGestureRecognize ...
- iOS--------手势识别的详细使用:拖动、缩放、旋转、点击、手势依赖、自定义手势
1.UIGestureRecognizer介绍 手势识别在iOS上非常重要,手势操作移动设备的重要特征,极大的增加了移动设备使用便捷性. iOS系统在3.2以后,为方便开发这使用一些常用的手势,提供了 ...
- [Android] 使用Matrix矩阵类对图像进行缩放、旋转、对照度、亮度处理
前一篇文章讲述了Android拍照.截图.保存并显示在ImageView控件中,该篇文章继续讲述Android图像处理技术,主要操作包含:通过打开相冊里的图片,使用Matrix对图像进行缩放. ...
- android学习笔记51——SQLite 手势Gesture
手势Gesture 所谓手势,是指用户手指或触摸笔在触摸屏幕上的连续触碰行为. Androi对两种手势行为都提供了支持: 1.对于第一种手势而言,android提供了手势检测,并为手势检测提供了相应的 ...
- iOS 七大手势之轻拍,长按,旋转手势识别器方法
一.监听触摸事件的做法 如果想监听一个view上面的触摸事件,之前的做法通常是:先自定义一个view,然后再实现view的touches方法,在方法内部实现具体处理代码 通过touches方法监听 ...
- 【原创】窥视懒人的秘密---android下拉刷新开启手势的新纪元
小飒的成长史原创作品:窥视懒人的秘密---android下拉刷新开启手势的新纪元转载请注明出处 **************************************************** ...
- View的平移、缩放、旋转以及位置、坐标系
原创 2015年05月12日 13:15:29 标签: Android / Scroll / Scale / Translation / Rotation 24733 Android开发中,经常会接触 ...
随机推荐
- Denpendcy Injection 8.0新功能——KeyedService
Denpendcy Injection 8.0新功能--KeyedService 本文只介绍 .NET Denpendcy Injection 8.0新功能--KeyedService,假定读者已熟练 ...
- nginx URLRewrite基础配置
环境准备: 主机 ip control01 192.168.29.128 nginx01 192.168.29.101 在两台机器上分别部署nginx control01主机nginx配置的内容如下: ...
- Python面向对象——封装
文章目录 内容回顾 封装 为何要隐藏? 作业 内容回顾 上节课复习: 1.编程范式/思想 面向过程 介绍: 核心是"过程"二字 过程就是"流水线" 过程终极奥义 ...
- Android历史版本
目录 [隐藏] 1 测试版 2 版本列表 2.1 Android 1.0 2.2 Android 1.1 2.3 Android 1.5 Cupcake 2.4 Android 1.6 Donut ...
- Factors 分解质因数
package com.yourself.yours; import java.util.Scanner; /** ****************************************** ...
- MySQL中sql_mode的设置
在升级MySQL版本到8.0的过程中,需要关注sql_mode参数默认值的变化,8.0版本sql_mode不支持 NO_AUTO_CREATE_USER,要避免配置的sql_mode中带有 NO_AU ...
- gitlab ci 集成 eslint/prettier/tsc 做代码审查,并使用 eslint 输出作为显示代码质量
前言 想自动化一下公司里代码的部分审查,最初想用 reviewdog 的,但是公司的域名基本都在 VPN 中访问的,gitlab ci 的容器中是访问不到的,于是乎实验了 gitlab 代码质量功能. ...
- 重温dp——最长上升公共子序列
一道经典的dp了 题目描述 给出 1,2,-,n 的两个排列 P1 和 P2 ,求它们的最长公共子序列. 输入格式 第一行是一个数 n. 接下来两行,每行为 n 个数,为自然数 1,2,-,n 的一 ...
- DeepSpeed: 大模型训练框架
背景: 目前,大模型的发展已经非常火热,关于大模型的训练.微调也是各个公司重点关注方向.但是大模型训练的痛点是模型参数过大,动辄上百亿,如果单靠单个GPU来完成训练基本不可能.所以需要多卡或者分布式训 ...
- 【2023年更新】git 常用口令
1.已关联远程 fatal: remote origin already exists. 先输入$ git remote rm origin(删除关联的origin的远程库) 2.关联新远程 ...