请尊重原创,转载请注明来源。

Android中,很多时候会用到手势判断,判断用户当前的手势是移动,还是双指缩放/旋转,关于Android中的手势和gesturedetector,已经有很多人进行过研究了,因此本博客就不介绍gesturedetector了,本文主要介绍如何判断用户当前是双指缩放还是双指旋转,如果缩放,缩放了多少;如果旋转,又旋转了多少度。

本文有demo,会在文章下方附上下载链接。

为了方便说明,本文使用ImageView进行辅助说明,因为ImageView有个matrix属性,可以很方便的看到计算之后的效果。

废话不多说,直接贴代码。

主要布局如下,很简单,就是一个textView和一个imageview,需要注意,imageview的scaleType属性一定要设置为matrix才能使用矩阵改变imageView的效果

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:paddingBottom="@dimen/activity_vertical_margin"

android:paddingLeft="@dimen/activity_horizontal_margin"

android:paddingRight="@dimen/activity_horizontal_margin"

android:paddingTop="@dimen/activity_vertical_margin"

tools:context="com.example.testcalcrotate.MainActivity" >

android:id="@+id/image"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:scaleType="matrix"

android:src="@drawable/test"

/>

android:id="@+id/text"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:text="@string/hello_world" />

注释都写的很详细,相信你看得懂:

/**

* 移动缩放旋转的demo

*

*/

/**

* @author Administrator

*

*/

public class MainActivity extends ActionBarActivity implements OnTouchListener{

private TextView textView;

private ImageView imageView;

/**

* 旋转角度:相对于默认状态

*/

private double angle=0;

/**

* 缩放比例:相对于默认状态

*/

private double scale=1.0;

/**

* 移动位置:相对于默认状态

*/

private PointF position=new PointF(0, 0);

/**

* 存有操作过程中的手指坐标信息

*/

List m_list_coords;

/**

* 用来显示各效果的矩阵类

*/

private Matrix matrix;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

textView = (TextView) findViewById(R.id.text);

imageView = (ImageView) findViewById(R.id.image);

m_list_coords = new ArrayList();

matrix = new Matrix();

imageView.setOnTouchListener(this);

}

/**

* 显示手势计算后的效果,即更新textView和imageView

*/

private void showResult()

{

imageView.setImageMatrix(matrix);

textView.setText("angle:"+angle+";scale"+scale+"trans:"+position.x+";"+position.y);

}

@Override

public boolean onCreateOptionsMenu(Menu menu) {

// Inflate the menu; this adds items to the action bar if it is present.

getMenuInflater().inflate(R.menu.main, menu);

return true;

}

@Override

public boolean onOptionsItemSelected(MenuItem item) {

// Handle action bar item clicks here. The action bar will

// automatically handle clicks on the Home/Up button, so long

// as you specify a parent activity in AndroidManifest.xml.

int id = item.getItemId();

if (id == R.id.action_settings) {

return true;

}

return super.onOptionsItemSelected(item);

}

/**

* 计算移动

*/

void calcMove()

{

int listSize = m_list_coords.size();

if (listSize < 2) {

return;

}

PointF spt = m_list_coords.get(listSize - 2);

PointF ept = m_list_coords.get(listSize - 1);

PointF v1 = new PointF(ept.x-spt.x,ept.y-spt.y);

position.x += v1.x;

position.y += v1.y;

matrix.postTranslate(v1.x, v1.y);

showResult();

}

/**

* 计算双指缩放和旋转

*/

void calcAngleScale()

{

int listSize = m_list_coords.size();

if (listSize < 4) {

return;

}

//根据移动前和移动后的位置构建出 第一条线和第二条线段。

PointF spt1 = m_list_coords.get(listSize - 4);

PointF ept1 = m_list_coords.get(listSize - 3);

PointF spt2 = m_list_coords.get(listSize - 2);

PointF ept2 = m_list_coords.get(listSize - 1);

//两个线段之间的运动幅度过小,删除。

//此处要注意,为什么幅度过小要删除呢,因为在某些手机屏幕,在按压时灵敏度过高,

//或者硬件计算手指位置算法不够好,造成用户感觉手指没动而显示出来的坐标一直在抖动

if (getLineLength(spt1, spt2)+getLineLength(ept1, ept2) < 5) {

m_list_coords.remove(listSize-1);

m_list_coords.remove(listSize-2);

return;

}

//旋转和缩放中点位置,认为是第二线段的中点.

PointF centerPoint = new PointF((spt2.x+ept2.x)/2, (spt2.y+ept2.y)/2);

//根据触点 构建两个向量,计算两个向量角度.

PointF v1 = new PointF(ept1.x-spt1.x,ept1.y-spt1.y);

PointF v2 = new PointF(ept2.x-spt2.x,ept2.y-spt2.y);

//计算两个向量的夹角.

double v1Len =getLineLength(v1);

double v2Len = getLineLength(v2);

double cosAlpha = (v1.x*v2.x + v1.y*v2.y) / (v1Len * v2Len);

//由于计算误差,可能会带来略大于1的cos,例如

if (cosAlpha > 1.0f) {

cosAlpha = 1.0f;

}

//本次的角度已经计算出来。

double dAngle = Math.acos(cosAlpha) * 180.0 / 3.14;

System.out.println(""+dAngle);

//判断顺时针和逆时针.

//判断方法其实很简单,这里的v1v2其实相差角度很小的。

//v1v2先Normalize,

v1.x /= v1Len;

v1.y /= v1Len;

v2.x /= v2Len;

v2.y /= v2Len;

//作v2的逆时针垂直向量。

PointF v2Vec = new PointF(v2.y, -v2.x);

//判断这个垂直向量和v1的点积,点积>0表示俩向量夹角锐角。=0表示垂直,<0表示钝角

float vDot = v1.x*v2Vec.x + v1.y* v2Vec.y;

if (vDot > 0) {

//v2的逆时针垂直向量和v1是锐角关系,说明v1在v2的逆时针方向。

}

else {

dAngle = -dAngle;

}

angle += dAngle;

//角度你懂的。

if (angle >= 360) {

angle -= 360;

}

if (angle < 0) {

angle +=360;

}

matrix.postRotate((float) dAngle,centerPoint.x,centerPoint.y);

//判断缩放.

double lineLen1 = getLineLength(spt1,ept1);

double lineLen2 = getLineLength(spt2, ept2);

//当两指距离过近,不要再进行缩放了。

if (lineLen1 >= 5 && lineLen2 >=5) {

double dScale = lineLen2 / lineLen1;

scale *= dScale;

matrix.postScale((float) dScale, (float)dScale, centerPoint.x,centerPoint.y);

}

showResult();

//textView.setText(String.valueOf(angle));

}

/**

* 获取p1到p2的线段的长度

* @param p1

* @param p2

* @return

*/

double getLineLength(PointF p1,PointF p2)

{

return Math.sqrt((p1.x-p2.x) * (p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y));

}

/**

* 获取原点到p的线段的长度

* @param p

* @return

*/

double getLineLength(PointF p)

{

return Math.sqrt(p.x*p.x + p.y*p.y);

}

@Override

public boolean onTouch(View view, MotionEvent event) {

//此处只处理imageView的touch事件

if (view.getId() == R.id.image) {

int touchSize = event.getPointerCount();

//只处理单指和双指消息,超过2个手指不处理,此处也可以放开,

//超过2个手指只处理前面两个手指的消息

if (touchSize >= 3) {

return false;

}

switch (event.getAction()&event.getActionMasked()) {

case MotionEvent.ACTION_DOWN:

//一个手指按下,记录按下的位置

m_list_coords.add(new PointF(event.getX(0), event.getY(0)));

//System.out.println("down"+event.getX(0)+ ":"+event.getY(0));

break;

case MotionEvent.ACTION_POINTER_DOWN:

//第二个手指按下,记录按下的位置

if (m_list_coords.isEmpty()) {

//数据有误或者第二个手指拿起又放下

//do nothing

}

else {

m_list_coords.add(new PointF(event.getX(1), event.getY(1)));

}

break;

case MotionEvent.ACTION_MOVE:

if (touchSize > 1) {

m_list_coords.add(new PointF(event.getX(0), event.getY(0)));

m_list_coords.add(new PointF(event.getX(1), event.getY(1)));

calcAngleScale();

}

else {

m_list_coords.add(new PointF(event.getX(0), event.getY(0)));

calcMove();

}

break;

case MotionEvent.ACTION_UP:

//UP中暂不处理消息,如果想做松手后的惯性滑动/缩放/旋转等,需要在此处理

//并且在event事件中需要记录时间,才能判断松手时的速度。

//本博客仅用来说明如何计算缩放旋转,因此不讲解这个处理。

m_list_coords.clear();

break;

case MotionEvent.ACTION_POINTER_UP:

//认为2指操作结束

m_list_coords.clear();

break;

default:

break;

}

return true;

}

return false;

}

}

其实上面的统计偏移的位置是有bug的,因为双指在缩放和旋转的时候,指定了中心点,也会导致平移。所以平移了多少,应该是从矩阵中取出来才是最精确的,包括缩放的比例和旋转的角度,都应该是从矩阵中计算出来,才是最精确的

源码下载地址:http://download.csdn.net/detail/gongminghbsz/9064683

android旋转缩放布局,Android学习笔记(一):双指缩放及旋转计算相关推荐

  1. Android Jetpack Components of ViewModel 学习笔记

    Android Jetpack Components of Lifecycle 学习笔记 Android Jetpack Components of LiveData 学习笔记 Android Jet ...

  2. Android开源项目SlidingMenu本学习笔记(两)

    我们已经出台SlidingMenu使用:Android开源项目SlidingMenu本学习笔记(一个),接下来再深入学习下.依据滑出项的Menu切换到相应的页面 文件夹结构: 点击Bluetooth能 ...

  3. Android Jetpack Components of LiveData 学习笔记

    Android Jetpack Components of Lifecycle 学习笔记 Android Jetpack Components of LiveData 学习笔记 Android Jet ...

  4. ITK学习笔记(七) ITK旋转方向位置不变

    ITK学习笔记(七) ITK旋转方向位置不变 sitk使用的使LPS坐标系,与slicer中使用的RAS坐标系不同. 在sitk中的方向(1,1,1)(1,1,1)(1,1,1),在slicer中的方 ...

  5. Greedy Match学习笔记二 —— 安慰剂检验与置信区间计算

    Greedy Match学习笔记二 -- 安慰剂检验与置信区间计算 上一节我们介绍了Greedy Match的基本原理.本节中,我们将在不同样本量的情况下分别进行安慰剂检验,以验证Greedy Mat ...

  6. Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十三章:计算着色器(The Compute Shader)...

    Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十三章:计算着色器(The Compute Shader) 原文: Int ...

  7. 【OpenGL学习笔记⑥】——3D变换【旋转的正方体 实现地月系统 旋转+平移+缩放】

    ✈️ 文章目录 零. 成果预览图 一.3D立方体的顶点数组 二.纹理旋转 三.纹理缩放 四.画n个3D图形 五.轨道的数学公式 六.深度缓冲(Z 缓冲) 七.完整代码 八.参考附录: 神器的正方体 ☁ ...

  8. android spi读写不通,Android-SPI学习笔记

    概述 SPI(Service Provider Interface, 服务提供方接口),服务通常是指一个接口或者一个抽象类,服务提供方是对这个接口或者抽象类的具体实现,由第三方来实现接口提供具体的服务 ...

  9. 《Android开发高手课》学习笔记

    最近在学习张绍文老师的<Android开发高手课>课程,学习到了很多的干货,特别是在处理问题的策略和知识的广度方面给了我很多的启发,对未来的学习也提供了方向. 目前,技术的发展有两个趋势. ...

最新文章

  1. sequence oracle mysql,oracle sequence转mysql(mysql实现oracle的sequence功能)
  2. 分支限界法实现最优装载c++_分支限界法
  3. 把有无线网卡的机子当作无线ap无需任何软件
  4. 工作405-关于vue组件开发过程中一直报错:This relative module was not found:
  5. 【往届已EI检索-IEEE技术支持】第三届-信息技术与计算机应用 多主题征稿!
  6. oracle迁移mysql注意_从MySQL到ORACLE程序迁移的注意事项
  7. C#基础笔记(第十天)
  8. ccna学习指南第七版
  9. 有什么软件可以测试汽车的噪音,汽车噪声测试,汽车通过噪声测试
  10. 微信小程序ios倒计时时间不显示,安卓能正常显示,小程序倒计时兼容问题
  11. 面包板电源线怎么接_面包板的怎么使用
  12. 使用CSS将图标进行旋转无效
  13. 抖音只有几十个播放量的原因是什么?
  14. yolov3/yolov4/yolov5/yolov6/yolov7/lite/fastdet/efficientdet各系列模型开发、项目交付、组合改造创新之—桥梁基建隧道裂痕裂缝检测实战
  15. ISO 14229、ISO 15765、ISO 11898的区别
  16. python列表增加行_openpyxl追加行、指定位置插入行
  17. Marshmallow 快速文档
  18. PCL教程-点云滤波之体素滤波器(下采样)
  19. 上线两个月,微信小程序给那些用身体支持它的人带来了什么?
  20. C语言中i++==1是什么意思?

热门文章

  1. 这位超级电脑之父,年近 90 仍不愿退休
  2. @IT老司机 云服务、BI大数据、协同办公等五大技术选型研讨会,震撼来袭!
  3. GitHub 热点速览:不可思议的浏览器 Browser-2020 周涨 Star 超 3 千
  4. 三个关键词,看懂AI未来趋势丨不仅仅是高薪
  5. JavaScript 败北,TypeScript 大势所趋?
  6. “上云”很 fashion 的今天,GeekPwn 搞了个比赛……
  7. 一文详解 Try 和异常的区别
  8. 豆瓣评分 9.7,等了好久的《操作系统导论》Operating Systems 终于来了
  9. 反垃圾江湖风云纪事 | 技术头条
  10. 果断 Mark!27 个免费、低成本 Python 学习资源入手!