【转】android手势处理揭秘
当滑动(fling)比移动(scroll)有更高的效率时,为什么要让用户使用scroll操作呢?在面积很小而数据又很多的移动设备上,要显示远在后面的那些内容scroll是很困难的,这种情况下fling更适合。
注:scroll表示手指滑动多少距离,界面跟着显示多少距离,而fling是根据你的滑动方向与轻重,还会自动滑动一段距离。
filing 手势在android交互设计中应用非常广泛:电子书的滑动翻页、ListView滑动删除item、滑动解锁等。所以如何检测用户的fling手势是非 常重要的。但是我们如何获得fling消息呢?如何知道fling的方向,从哪里开始从哪里结束?又如何确定当前手势是fling 而不是scroll?
在我最近的项目Room5中就面临这样的问题,这是一个已经有了的ios项目,现在要做一个android版本。
在 我们处理滑动动画之前,我们需要检测fling事件,对初学者而言,这要比看上去复杂一些。这是因为touch、scroll、fling三个事件之间并 没有明显的界限。scroll总是发生在fling之前,而touch总是发生在scroll之前,滑动屏幕(fling)总是需要先触摸屏幕 (touch)并且在屏幕上移动(scroll)。我们如何知道滑动(fling)没有被touch事件拦截呢?答案是使用几个关于手势 (gesture)处理的类。

我写了一个演示这几个类是如何配合使用的例子程序,你可以在这里得到完整的源码:https://github.com/ericacooksey/FlingDemo 这个demo描述了捕捉fling事件的整个过程。下面是运行apk之后初始界面的截图:

当接收到touch,scroll或者fling事件,事件的名称会显示在界面上,最近发生的显示在最上面。下面是某一次从上往下划动(fling)时,界面上输出的文字:

这幅图给了你关于事件流的一个大概认识:view收到了好几个scroll 事件,每个scroll事件之前都伴随着一个touch事件。scroll事件过程中y轴方向上的速度飞快增长,直到最终fling事件被触发。让我们来看看关键代码, 这里。
首 先,我们实现了View.OnTouchListener来拦截view的touch事件。我这里暂时省略了对滑动速度追踪的代码(省略号),我们将在后 面讨论。这里主要是实现了touch事件触发的时候将会调用的回到方法,将此方法注册给相应的view(这里为TextView),下面是代码片段。
mTouchListener = new VelocityTrackingTouchListener();
// Initialize the TextView which will be used to display the logged events
mTextView = (TextView) findViewById(R.id.mytextview);
mTextView.setOnTouchListener(mTouchListener);
private class VelocityTrackingTouchListener implements View.OnTouchListener {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
......
mGestureDetector.onTouchEvent(motionEvent);
return true;
}
}
mGestureDetector 是GestureDetector的实例。onTouch回调方法在将事件派发给view之前接收到了touch事件。任何可能跟touch事件相关的事 件(比如Click)都会被这个回调方法拦截。我们将touch事件交给GestureDetector,因此这里的作用其实就是在将touch事件传递 下去之前,将MotionEvent传递给GestureDetector的onTouchEvent方法,先判断当前到底是什么手势。下面是声明 GestureDetector变量的代码片段:
// Instantiate a gesture listener to consume scroll and fling events
FlingDetector flingDetector = new FlingDetector();
// Pass the FlingDetector to mGestureDetector to receive the appropriate callbacks
mGestureDetector = new GestureDetector(this, flingDetector);
其中FlingDetector是我们继承自SimpleOnGestureListener的一个类。使用SimpleOnGestureListener的好处是它完成了所有对GestureDetector.OnGestureListener所有接口的空实现,因此我们只需重写需要的回调方法。
private class FlingDetector extends GestureDetector.SimpleOnGestureListener {
public FlingDetector() {
super();
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
updateText("in onFling");
return true;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
updateText(String.format("onScroll velocity = (%f, %f)", mTouchListener.xVelocity, mTouchListener.yVelocity));
return false;
}
}
作为总结,下面是对上面一系列过程的回顾:
(1)view所注册的OnTouchListener拦截了touch事件。
(2)OnTouchListener的回调方法onTouch(View view, MotionEvent motionEvent)将MotionEvent传递给了GestureDetector。
(3)实现一个OnGestureListener,并将他注册给GestureDetector,这样OnGestureListener中处理具体手势的回调方法就能被触发。
注 意OnGestureListener的每个方法都会返回一个boolean值,这个返回值表示当前方法结束之后,MotionEvent是否被“消费” 掉,也就是说是否继续传递下去,true表示被消费掉,反之则没有,可以继续传递。回想上面我们提到的filing发生在scroll之后,scroll 发生在touch之后,而我们希望接收到fling手势,因此我们在onScroll中返回false,在fling中返回true。
但是 现在问题是如果我们希望一个view既可以scroll也可以fling怎么办?比如一个账单界面我们希望sroll查看账单而用fling操作表示 swipe-to-pay。如果你参考上面的fling事件的截图会发现onScroll在onFLing触发之前发生了5次。因此如果我们响应 onScroll事件,那么用户在fling操作的时候会感到不自然,因为scroll的对我们预期的交互产生了干扰。
我们可以直接忽略掉 scroll,将onScroll中的实现留为空,但是这样的话,如果用户手指慢慢滑动查看后面的内容就得不到响应。最好是能在scroll的时候判断这 个scroll是否会导致fling的发生。这种情况下android的VelocityTracker 就派上用场了。先来瞄一眼scroll的时候的手势输出日志:

让我们将这张图中(只有scroll事件的截图)y方向上的速度和上一张图(产生了fling事件的截图)y方向上的速度做一个对比。之所以用y速度是因为两次实验中我们都是从上到下的滑动。
| Scroll y-velocity | Fling y-velocity |
|---|---|
| 65 | 30085 |
| 140 | 23359 |
| 424 | 13787 |
| 660 | 10414 |
| 847 | 7449 |
如你所见, 产生了fling事件的滚动(scroll)事件中y-velocity要比没有产生fling的滚动(scroll)高很多。我们在OnTouchListener的实现中跟踪速度,每收到一个触摸事件就将之添加给 mVelocityTracker
switch (action) {
case MotionEvent.ACTION_DOWN:
if (mVelocityTracker == null) {
// Retrieve a new VelocityTracker object to watch the velocity of a motion.
mVelocityTracker = VelocityTracker.obtain();
} else {
// Reset the velocity tracker back to its initial state.
mVelocityTracker.clear();
}
// Add a user's movement to the tracker.
mVelocityTracker.addMovement(motionEvent);
break;
case MotionEvent.ACTION_MOVE:
mVelocityTracker.addMovement(motionEvent);
// When you want to determine the velocity, call
// computeCurrentVelocity(). Then call getXVelocity()
// and getYVelocity() to retrieve the velocity for each pointer ID.
mVelocityTracker.computeCurrentVelocity(1000);
// Log velocity of pixels per second
xVelocity = mVelocityTracker.getXVelocity(pointerId);
yVelocity = mVelocityTracker.getYVelocity(pointerId);
break;
case MotionEvent.ACTION_CANCEL:
// Return a VelocityTracker object back to be re-used by others.
mVelocityTracker.recycle();
break;
}
然后再将触摸事件传递给FlingDetector去分析,在FlingDetector打印出mVelocityTracker的速度以及对应的状态(scroll还是fling),经过多次实验可以找到一个合理的决定scroll是否会导致fling的临界值。从事判断是否相应onScroll。
【转】android手势处理揭秘的更多相关文章
- Android 手势水平监听判断
package com.zihao.ui; import com.zihao.R; import android.os.Bundle; import android.app.Activity; imp ...
- Android手势锁实现
最终效果如下 整体思路 a.自定义了一个RelativeLayout(GestureLockViewGroup)在里面会根据传入的每行的个数,生成多个GestureLockView(就是上面一个个小圈 ...
- Android手势源码浅析-----手势绘制(GestureOverlayView)
Android手势源码浅析-----手势绘制(GestureOverlayView)
- Android 手势检测实战 打造支持缩放平移的图片预览效果(下)
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/39480503,本文出自:[张鸿洋的博客] 上一篇已经带大家实现了自由的放大缩小图 ...
- Android手势滑动Tab
Android手势滑动Tab //MainActivity.java public class MainActivity extends TabActivity { ; ; ; private Ges ...
- Android 手势操作识别
(转自:http://jcodecraeer.com/a/anzhuokaifa/androidkaifa/2012/1020/448.html) 首先,在Android系统中,每一次手势交互都会依照 ...
- android手势创建及识别
使用一些浏览器或者输入法应用时会有一些手势操作,还可以自定义手势.这些神奇的操作是怎么做的呢?这一篇重点记录手势的识别和创建.这篇的内容使用到的是android.gesture包,具体的例子参考的是S ...
- Android手势解锁, 九宫格解锁
给大家介绍一个很好用的手势解锁控件ShapleLocker, 废话不多先上效果图: 这是一个第三方库, 可自己根据UI需求替换图标: 圆圈, 小箭头等等.. github地址: http://pane ...
- Android手势操作
xml文件 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:to ...
随机推荐
- CAN总线标准帧
CAN总线是一种串行数据通信协议,其通信接口中集成了CAN协议的物理层和数据链路层功能,可完成对通信数据的成帧处理,包括位填充.数据块编码.循环冗余检验.优先级判别等项工作. CAN总线结构 CAN总 ...
- (转)Oracle存储过程中的事务
本文转载自:http://www.cnblogs.com/linjiqin/archive/2011/04/18/2019990.html 1.事务用于确保数据的一致性,由一组相关的DML语句组成,该 ...
- 【转】JMeter基础之——录制脚本
Jmeter 是一个非常流行的性能测试工具,虽然与LoadRunner相比有很多不足,比如:它结果分析能力没有LoadRunner详细:很它的优点也有很多: ● 开源,他是一款开源的免费软件,使用它你 ...
- selenium 定位元素可以用多个findElement
selenium 定位元素可以用多个findElement,如driver.findElement(By.id"XXX").findElement(By.linkText(XXX) ...
- PTA 说反话-加强版(20 分)(字符串处理)
说反话-加强版(20 分) 给定一句英语,要求你编写程序,将句中所有单词的顺序颠倒输出. 输入格式: 测试输入包含一个测试用例,在一行内给出总长度不超过500 000的字符串.字符串由若干单词和若干空 ...
- thinkphp中的volist标签
属性: name(必须):要输出的数据模板变量 id(必须):循环变量 offset(可选):要输出数据的offset length(可选):输出数据的长度 key(可选):循环的key变量,默认值为 ...
- Zabbix自定义监控网站服务是否能够正常响应
监测tcp连接数文件名: /etc/zabbix/zabbix_agentd.conf.d/count_tcp.conf UserParameter=count.tcp,netstat -s|g ...
- 三级联动第二种方法 三级联动数据.js
var data = { '0':{2:'北京',25:'天津',24:'上海',31:'重庆',130000:'河北省',140000:'山西省',150000:'内蒙古自治区',210000:'辽 ...
- 自定义inputformat和outputformat
1. 自定义inputFormat 1.1 需求 无论hdfs还是mapreduce,对于小文件都有损效率,实践中,又难免面临处理大量小文件的场景,此时,就需要有相应解决方案 1.2 分析 小文件的优 ...
- KinderEditor编辑器 在Asp.Net中的使用
KinderEditor编辑器的使用 分为简单的三步. 1:添加引用部分 <script src="/KinderEditor/kindeditor-min.js">& ...