这个类在各种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缩放实现的更多相关文章

  1. Android 坐标系和 MotionEvent 分析、滑动

    1.Android坐标系 在Android中,屏幕最左上角的顶点作为Android坐标系的原点,这个点向左是X轴正方向,这个点向下是Y轴正方向. 系统提供了getLocationOnScreen(in ...

  2. ImageView 缩放

    <ImageView android:id="@+id/imageview" android:layout_width="wrap_content" an ...

  3. ImageView缩放选项

    ImageView.ScaleType 将图片边界缩放到所在view边界时的缩放选项. Options for scaling the bounds of an image to the bounds ...

  4. 手势 触摸【缩放】GestureDetector MotionEvent 案例

    GestureDetector和ScaleGestureDetector示例 /**  * 演示[单点触摸手势识别器]  * 演示[缩放手势识别器]最简单的使用  * @author 白乾涛  */ ...

  5. 【腾讯Bugly干货分享】Android ImageView 正确使用姿势

    本文来自于腾讯bugly开发者社区,未经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/5832602d7196970d65901d76 导语 本文主要介绍了ImageV ...

  6. 自定义圆形头像CircleImageView的使用和源码分析

    http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0806/3268.html tools:context="com.ex ...

  7. Android ImageView 详述

    结构 继承关系 public classView.OnClickListner extendsView java.lang.Object android.view.View android.widge ...

  8. Android 单指触控拖拽,两指触控缩放

    import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.view. ...

  9. PhotoView开源项目剖析

    http://blog.csdn.net/wu928320442/article/details/43056731 介绍 上一节呢,我们介绍了怎么下载和编译Android源码,这节呢,我们来讨论Pho ...

随机推荐

  1. Gym 101917 E 简单计算几何,I 最大流

    题目链接 https://codeforces.com/gym/101917 E 题意:给定一个多边形(n个点),然后逆时针旋转A度,然后对多边形进行规约,每个点的x规约到[0,w]范围内,y规约到[ ...

  2. ML| EM

    What's xxx The EM algorithm is used to find the maximum likelihood parameters of a statistical model ...

  3. P1136 超车 归并排序 求逆序对个数

    这道题从看到它开始到做出来,已经过了快两周[因为第一次思路完全跑偏写的是暴力模拟想水过]: 题意是这样的:  jzabc除了对多米诺骨牌感兴趣外,对赛车也很感兴趣.上个周末他观看了一场赛车比赛.他总是 ...

  4. restful的认识和用法

    目录 一.restful的认识 1.基本概念 2.规范和约束 3.使用标准的状态码 二.具体使用 1.简单概括 2.根据id查询一个员工 3.查询所有员工 4.保存一个员工 5.根据id修改员工 6. ...

  5. tomcat访问(access)日志配置、记录Post请求参数

    tomcat访问(access)日志配置.记录Post请求参数 一.配置与说明 tomcat访问日志格式配置,在config/server.xml里Host标签下加上 <Valve classN ...

  6. 【bootstrap】Bootstrap Notify的使用步骤

    Bootstrap Notify说明文档:http://bootstrap-notify.remabledesigns.com/ Bootstrap Notify的GitHub地址:https://g ...

  7. WebLogic 11g重置用户密码

    weblogic安装后,很久不用,忘记访问控制台的用户名或者密码,可通过以下步骤来重置用户名密码. 版本:WebLogic Server 11g 说明:%DOMAIN_HOME%:指WebLogic ...

  8. 微服务指南走北(三):Restful API 设计简述

    API的定义取决于选择的IPC通信方式,假设是消息机制(如 AMQP 或者 STOMP).API则由消息频道(channel)和消息类型.假设是使用HTTP机制,则是基于请求/响应(调用http的ur ...

  9. [Guava源代码阅读笔记]-Basic Utilities篇-1

    欢迎訪问:个人博客 写该系列文章的目的是记录Guava源代码中个人感觉不错且值得借鉴的内容. 一.MoreObjects类 //MoreObjects.ToStringHelper类的toString ...

  10. phonegap工程中修改app的名字

    针对phonegap比较高的版本,我的是6.4.0. 在phonegap工程中,当添加了iOS和android平台或多个平台后,工程进行了开发,然后觉得app的名字想修改一下(比如在手机上显示的app ...