MotionEvent分析及ImageView缩放实现
这个类在各种View和用户的手势操作之间的交互存在很大的自定义空间。要理解清楚这个类的一些特性和意义,对自定义的新型控件很有帮助
先翻译一下开发者文档的描述
Overview
Motion events describe movements in terms of an action code and a set of axis values. The action code specifies the state change that occurred such as a pointer going down or up. The axis values describe the position and other movement properties.
运动事件描述了关于一个动作代码和一组轴值的动作。动作代码描述的是状态改变的发生,例如一个触点按下或者抬起。轴值描述的是位置和其他运动属性。
For example, when the user first touches the screen, the system delivers a touch event to the appropriate View
with the action code ACTION_DOWN
and a set of axis values that include the X and Y coordinates of the touch and information about the pressure, size and orientation of the contact area.
例如,当用户第一次按下屏幕,系统将带有动作代码为ACTION_DOWN和一族含有x、y坐标和压力、大小、方向轴值的触摸时间传递给适当的View。
Some devices can report multiple movement traces at the same time. Multi-touch screens emit one movement trace for each finger. The individual fingers or other objects that generate movement traces are referred to as pointers. Motion events contain information about all of the pointers that are currently active even if some of them have not moved since the last event was delivered.
一些设备能够同时反馈多个运动。多点触控屏能够为每一根手指生成单独的运动轨迹。单独的手指或者其他能够产生运动轨迹的对象都被叫做pointers。运动事件包含了所有这些当前活动的pointers即使其中一些并没有移动。
The number of pointers only ever changes by one as individual pointers go up and down, except when the gesture is canceled.
当pointer按下或者抬起的时候pointers的数量每次只会以1为单位进行变化
Each pointer has a unique id that is assigned when it first goes down (indicated by ACTION_DOWN
or ACTION_POINTER_DOWN
). A pointer id remains valid until the pointer eventually goes up (indicated by ACTION_UP
or ACTION_POINTER_UP
) or when the gesture is canceled (indicated by ACTION_CANCEL
).
当按下时,每个pointer都会有一个独立的id。这个id会一直保持直到抬起或者姿势被取消。
The MotionEvent class provides many methods to query the position and other properties of pointers, such as getX(int)
, getY(int)
, getAxisValue(int)
,getPointerId(int)
, getToolType(int)
, and many others. Most of these methods accept the pointer index as a parameter rather than the pointer id. The pointer index of each pointer in the event ranges from 0 to one less than the value returned by getPointerCount()
.
这些方法中的大多数都以pointer的index作为参数而不是pointer的id。各个pointer的index在事件中从0变到getPointerCount()-1.
The order in which individual pointers appear within a motion event is undefined. Thus the pointer index of a pointer can change from one event to the next but the pointer id of a pointer is guaranteed to remain constant as long as the pointer remains active. Use the getPointerId(int)
method to obtain the pointer id of a pointer to track it across all subsequent motion events in a gesture. Then for successive motion events, use the findPointerIndex(int)
method to obtain the pointer index for a given pointer id in that motion event.
pointer的index从一个事件到另一个事件的时候会变化,但是其id会保持不变。调用getPointerId就能够获取到id了。有了这个ID之后我们就能够在整个运动的过程中一直监控这个pointer了,findPointerIndex就可以通过id去得到index了,也就是说index和id是有办法互换的。
Mouse and stylus buttons can be retrieved using getButtonState()
. It is a good idea to check the button state while handling ACTION_DOWN
as part of a touch event. The application may choose to perform some different action if the touch event starts due to a secondary button click, such as presenting a context menu.
这类触摸的功能都是以一种状态机设计存在,针对两根手指进行缩放操作
Event的getAction()的操作可以检测到pointer1、pointer2的按下和抬起动作事件。
那我们针对这个状态机需要设计的状态有:DONE(初始状态或者说是完成状态),POINTER_1_DOWN_2_UP(pointer1按下,pointer2抬起),POINTER_1_UP_2_DOWN(pointer1抬起,pointer2按下),ZOOMING(放大状态).一共四个状态
状态机就是要在每一种状态下,收到一个新的事件时状态如何变迁。但写程序的时候是反过来,收到某种事件,然后根据不同的状态做变迁
然后在状态变迁之后,根据当前的状态来做实际的图片的显示操作或者是一些变量状态的更新。
针对图像缩放的原理,最基本的就是以两根手指触点的中心作为缩放的原点;然后以两根手指的初始距离作为基准,在移动过程中实时计算两根手指之间的距离,用这个实时距离和基准的比例作为缩放的一个scale幅度
public class MyImageView extends ImageView implements View.OnTouchListener { private static final String TAG = "MyImageView"; private static final int DONE = 0;
private static final int POINTER_1_DOWN_2_UP = 1;
private static final int POINTER_1_UP_2_DOWN = 2;
private static final int ZOOMING = 3; private float pointer_1_x;
private float pointer_1_y; private float pointer_2_x;
private float pointer_2_y; private float pointer_center_x;
private float pointer_center_y; private int focus_state = 0; private int current_state = DONE;
private float init_distance = 0;
private float current_distance = 0; private Matrix matrix = new Matrix();
private Matrix currentMatrix = new Matrix();
private Matrix originalMatrix = new Matrix(); public MyImageView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
this.setOnTouchListener(this);
} public MyImageView(Context context) {
super(context);
this.setOnTouchListener(this);
} private float distance(float x1, float y1, float x2, float y2) {
return (float) Math.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
} @Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
int cnt = event.getPointerCount();
Log.d("MyImageView","pointer cnt is:"+cnt+" action is:"+event.getAction()); //起来的时候切换缩放的原点;按下的时候,记录位置以及两个pointer之间的距离
switch(event.getAction()) {
case MotionEvent.ACTION_DOWN:
//状态变迁过程done->pointer_1_down
//这个action_down在整个过程中只会出现一次
current_state = POINTER_1_DOWN_2_UP;
focus_state = 1;
//记录这个pointer的坐标位置
pointer_1_x = event.getX(event.getPointerCount()-1);
pointer_1_y = event.getY(event.getPointerCount()-1); currentMatrix.set(this.getImageMatrix());
originalMatrix.set(this.getImageMatrix());
Log.d(TAG,"current state is:"+current_state);
Log.d(TAG,"x_1:"+pointer_1_x+";y_1:"+pointer_1_y);
break;
case MotionEvent.ACTION_POINTER_1_DOWN:
if(current_state == POINTER_1_UP_2_DOWN ) {
current_state = ZOOMING;
pointer_1_x = event.getX(event.getPointerCount()-1);
pointer_1_y = event.getY(event.getPointerCount()-1);
pointer_center_x = (pointer_1_x + pointer_2_x)/2;
pointer_center_y = (pointer_1_y + pointer_2_y)/2;
init_distance = distance(pointer_1_x,pointer_1_y,pointer_2_x,pointer_2_y);
Log.d(TAG,"current state is:"+current_state);
Log.d(TAG,"init distance is:"+init_distance);
}
break;
case MotionEvent.ACTION_POINTER_1_UP:
if(current_state == ZOOMING) {
focus_state = 2;
current_state = POINTER_1_UP_2_DOWN; }
else if(current_state == POINTER_1_DOWN_2_UP) {
focus_state = 0;
current_state = DONE;
this.setImageMatrix(originalMatrix);
}
init_distance = 0;
Log.d(TAG,"current state is:"+current_state);
Log.d(TAG,"init distance is:"+init_distance);
break;
case MotionEvent.ACTION_POINTER_2_DOWN:
if(current_state == POINTER_1_DOWN_2_UP) {
current_state = ZOOMING;
pointer_2_x = event.getX(event.getPointerCount()-1);
pointer_2_y = event.getY(event.getPointerCount()-1);
pointer_center_x = (pointer_1_x + pointer_2_x)/2;
pointer_center_y = (pointer_1_y + pointer_2_y)/2;
init_distance = distance(pointer_1_x,pointer_1_y,pointer_2_x,pointer_2_y);
Log.d(TAG,"current state is:"+current_state);
Log.d(TAG,"init distance is:"+init_distance);
}
break;
case MotionEvent.ACTION_POINTER_2_UP:
if(current_state == ZOOMING) {
focus_state = 1;
current_state = POINTER_1_DOWN_2_UP; }
else if(current_state == POINTER_1_UP_2_DOWN) {
focus_state = 0;
current_state = DONE;
this.setImageMatrix(originalMatrix);
}
init_distance = 0;
Log.d(TAG,"current state is:"+current_state);
Log.d(TAG,"init distance is:"+init_distance);
break;
case MotionEvent.ACTION_MOVE:
if(current_state == ZOOMING) {
//1st update the 2 pointer coordinate
if(focus_state == 1) {
pointer_1_x = event.getX(0);
pointer_1_y = event.getY(0);;
pointer_2_x = event.getX(1);;
pointer_2_y = event.getY(1);;
}
else if(focus_state == 2) {
pointer_1_x = event.getX(1);
pointer_1_y = event.getY(1);
pointer_2_x = event.getX(0);
pointer_2_y = event.getY(0);
}
//2nd count the current distance
current_distance = distance(pointer_1_x,pointer_1_y,pointer_2_x,pointer_2_y); float scale = current_distance/init_distance;
Log.d(TAG,"current state is:"+current_state);
Log.d(TAG,"current distance is:"+current_distance);
Log.d("Scale","scale is :"+scale);
matrix.set(currentMatrix);
matrix.postScale(scale, scale,pointer_center_x,pointer_center_y);
this.setImageMatrix(matrix);
}
break;
} return true;
} }
总结一下这类view的扩展:
1.要增加触摸交互的方式,核心是要在原有的基础上新增view的OnTouchListener类,中间常用的就是状态机的设计,下拉刷新的实现也同理是实现了listview的onTouchListener与此同时设计了4个状态的状态机
2.针对这种后台数据并不复杂的view,就没有必要考虑那种adapter的操作,但是那种列表性质的view,在触摸操作的过程中还需要考虑对后台数据的更改,比如下拉刷新中就要在出于refreshing的时候增加adapter中的数据
MotionEvent分析及ImageView缩放实现的更多相关文章
- Android 坐标系和 MotionEvent 分析、滑动
1.Android坐标系 在Android中,屏幕最左上角的顶点作为Android坐标系的原点,这个点向左是X轴正方向,这个点向下是Y轴正方向. 系统提供了getLocationOnScreen(in ...
- ImageView 缩放
<ImageView android:id="@+id/imageview" android:layout_width="wrap_content" an ...
- ImageView缩放选项
ImageView.ScaleType 将图片边界缩放到所在view边界时的缩放选项. Options for scaling the bounds of an image to the bounds ...
- 手势 触摸【缩放】GestureDetector MotionEvent 案例
GestureDetector和ScaleGestureDetector示例 /** * 演示[单点触摸手势识别器] * 演示[缩放手势识别器]最简单的使用 * @author 白乾涛 */ ...
- 【腾讯Bugly干货分享】Android ImageView 正确使用姿势
本文来自于腾讯bugly开发者社区,未经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/5832602d7196970d65901d76 导语 本文主要介绍了ImageV ...
- 自定义圆形头像CircleImageView的使用和源码分析
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0806/3268.html tools:context="com.ex ...
- Android ImageView 详述
结构 继承关系 public classView.OnClickListner extendsView java.lang.Object android.view.View android.widge ...
- Android 单指触控拖拽,两指触控缩放
import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.view. ...
- PhotoView开源项目剖析
http://blog.csdn.net/wu928320442/article/details/43056731 介绍 上一节呢,我们介绍了怎么下载和编译Android源码,这节呢,我们来讨论Pho ...
随机推荐
- js-异步请求音频完成后页面显示
var ajax = new XMLHttpRequest(); ajax.open("get", "http://gzmylike.wedei.com/zt/gzyan ...
- iNeuOS云操作系统,.NET Core全系打造
iNeuOS云操作系统,.NET Core全系打造 目录 一.演示地址... 2 二.技术体系... 2 三.iNeuOS整体介绍... 2 四.iNeuView概述... 3 五.iNeuView操 ...
- Codeforces Gym 100431D Bubble Sort 水题乱搞
原题链接:http://codeforces.com/gym/100431/attachments/download/2421/20092010-winter-petrozavodsk-camp-an ...
- spring-cloud - 基础环境搭建
spring-cloud中文文档:https://springcloud.cc/ spring-cloud中文导航:http://springcloud.fun/ 文章纯属用于个人学习的一个归纳,哪里 ...
- ios- nil NULL 和 NSNull
因为objective-c的集合对象,比如nsarray, nsdictionary, nsset等,都有可能包含nsnull对象,所以,如果以下代码中的item为nsnull,则会引起程序崩溃. N ...
- Android传统View动画与Property动画基础及比较
前言:关于动画方面的知识也整理一段时间了,如题,这篇文章简单的介绍了View和Property动画的概念,如何在项目中创建资源文件,以及如何在代码中使用它们,本次整理动画的重点放在了Property动 ...
- TCP通过滑动窗口和拥塞窗口实现限流,能抵御ddos攻击吗
tcp可以通过滑动窗口和拥塞算法实现流量控制,限制上行和下行的流量,但是却不能抵御ddos攻击. 限流只是限制访问流量的大小,是无法区分正常流量和异常攻击流量的. 限流可以控制本软件或者应用的流量大小 ...
- python为不同的对象如何分配内存的小知识
id方法的返回值就是对象的内存地址. python中会为每个出现的对象分配内存,哪怕他们的值完全相等(注意是相等不是相同).如执行a=2.0,b=2.0这两个语句时会先后为2.0这个Float类型对象 ...
- 强大的开源网络侦查工具:IVRE
IVRE简介 IVRE(又名DRUNK)是一款开源的网络侦查框架工具,IVRE使用Nmap.Zmap进行主动网络探测.使用Bro.P0f等进行网络流量被动分析,探测结果存入数据库中,方便数据的查询.分 ...
- System::String *,char*,string 等的类型转换 [转]
在VC 的编程中,经常会用到各种类型的转换,在MFC中textbox等控件得到的返回类型是System::String *,而写入的文件要求是 const char *类型的,下面介绍一些转换的方法: ...