android旋转缩放布局,Android学习笔记(一):双指缩放及旋转计算
请尊重原创,转载请注明来源。
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学习笔记(一):双指缩放及旋转计算相关推荐
- Android Jetpack Components of ViewModel 学习笔记
Android Jetpack Components of Lifecycle 学习笔记 Android Jetpack Components of LiveData 学习笔记 Android Jet ...
- Android开源项目SlidingMenu本学习笔记(两)
我们已经出台SlidingMenu使用:Android开源项目SlidingMenu本学习笔记(一个),接下来再深入学习下.依据滑出项的Menu切换到相应的页面 文件夹结构: 点击Bluetooth能 ...
- Android Jetpack Components of LiveData 学习笔记
Android Jetpack Components of Lifecycle 学习笔记 Android Jetpack Components of LiveData 学习笔记 Android Jet ...
- ITK学习笔记(七) ITK旋转方向位置不变
ITK学习笔记(七) ITK旋转方向位置不变 sitk使用的使LPS坐标系,与slicer中使用的RAS坐标系不同. 在sitk中的方向(1,1,1)(1,1,1)(1,1,1),在slicer中的方 ...
- Greedy Match学习笔记二 —— 安慰剂检验与置信区间计算
Greedy Match学习笔记二 -- 安慰剂检验与置信区间计算 上一节我们介绍了Greedy Match的基本原理.本节中,我们将在不同样本量的情况下分别进行安慰剂检验,以验证Greedy Mat ...
- Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十三章:计算着色器(The Compute Shader)...
Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十三章:计算着色器(The Compute Shader) 原文: Int ...
- 【OpenGL学习笔记⑥】——3D变换【旋转的正方体 实现地月系统 旋转+平移+缩放】
✈️ 文章目录 零. 成果预览图 一.3D立方体的顶点数组 二.纹理旋转 三.纹理缩放 四.画n个3D图形 五.轨道的数学公式 六.深度缓冲(Z 缓冲) 七.完整代码 八.参考附录: 神器的正方体 ☁ ...
- android spi读写不通,Android-SPI学习笔记
概述 SPI(Service Provider Interface, 服务提供方接口),服务通常是指一个接口或者一个抽象类,服务提供方是对这个接口或者抽象类的具体实现,由第三方来实现接口提供具体的服务 ...
- 《Android开发高手课》学习笔记
最近在学习张绍文老师的<Android开发高手课>课程,学习到了很多的干货,特别是在处理问题的策略和知识的广度方面给了我很多的启发,对未来的学习也提供了方向. 目前,技术的发展有两个趋势. ...
最新文章
- sequence oracle mysql,oracle sequence转mysql(mysql实现oracle的sequence功能)
- 分支限界法实现最优装载c++_分支限界法
- 把有无线网卡的机子当作无线ap无需任何软件
- 工作405-关于vue组件开发过程中一直报错:This relative module was not found:
- 【往届已EI检索-IEEE技术支持】第三届-信息技术与计算机应用 多主题征稿!
- oracle迁移mysql注意_从MySQL到ORACLE程序迁移的注意事项
- C#基础笔记(第十天)
- ccna学习指南第七版
- 有什么软件可以测试汽车的噪音,汽车噪声测试,汽车通过噪声测试
- 微信小程序ios倒计时时间不显示,安卓能正常显示,小程序倒计时兼容问题
- 面包板电源线怎么接_面包板的怎么使用
- 使用CSS将图标进行旋转无效
- 抖音只有几十个播放量的原因是什么?
- yolov3/yolov4/yolov5/yolov6/yolov7/lite/fastdet/efficientdet各系列模型开发、项目交付、组合改造创新之—桥梁基建隧道裂痕裂缝检测实战
- ISO 14229、ISO 15765、ISO 11898的区别
- python列表增加行_openpyxl追加行、指定位置插入行
- Marshmallow 快速文档
- PCL教程-点云滤波之体素滤波器(下采样)
- 上线两个月,微信小程序给那些用身体支持它的人带来了什么?
- C语言中i++==1是什么意思?
热门文章
- 这位超级电脑之父,年近 90 仍不愿退休
- @IT老司机 云服务、BI大数据、协同办公等五大技术选型研讨会,震撼来袭!
- GitHub 热点速览:不可思议的浏览器 Browser-2020 周涨 Star 超 3 千
- 三个关键词,看懂AI未来趋势丨不仅仅是高薪
- JavaScript 败北,TypeScript 大势所趋?
- “上云”很 fashion 的今天,GeekPwn 搞了个比赛……
- 一文详解 Try 和异常的区别
- 豆瓣评分 9.7,等了好久的《操作系统导论》Operating Systems 终于来了
- 反垃圾江湖风云纪事 | 技术头条
- 果断 Mark!27 个免费、低成本 Python 学习资源入手!