【转】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),下面是代码片段。

 1 mTouchListener = new VelocityTrackingTouchListener();
 2 // Initialize the TextView which will be used to display the logged events
 3 mTextView = (TextView) findViewById(R.id.mytextview);
 4 mTextView.setOnTouchListener(mTouchListener);
 5 private class VelocityTrackingTouchListener implements View.OnTouchListener {
 6     @Override
 7     public boolean onTouch(View view, MotionEvent motionEvent) {
 8         ......
 9         mGestureDetector.onTouchEvent(motionEvent);
10         return true;
11     }
12 }

mGestureDetector 是GestureDetector的实例。onTouch回调方法在将事件派发给view之前接收到了touch事件。任何可能跟touch事件相关的事 件(比如Click)都会被这个回调方法拦截。我们将touch事件交给GestureDetector,因此这里的作用其实就是在将touch事件传递 下去之前,将MotionEvent传递给GestureDetector的onTouchEvent方法,先判断当前到底是什么手势。下面是声明 GestureDetector变量的代码片段:

1 // Instantiate a gesture listener to consume scroll and fling events
2 FlingDetector flingDetector = new FlingDetector();
3 // Pass the FlingDetector to mGestureDetector to receive the appropriate callbacks
4 mGestureDetector = new GestureDetector(this, flingDetector);

其中FlingDetector是我们继承自SimpleOnGestureListener的一个类。使用SimpleOnGestureListener的好处是它完成了所有对GestureDetector.OnGestureListener所有接口的空实现,因此我们只需重写需要的回调方法。

 1 private class FlingDetector extends GestureDetector.SimpleOnGestureListener {
 2     public FlingDetector() {
 3         super();
 4     }
 5     @Override
 6     public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
 7     float velocityY) {
 8         updateText("in onFling");
 9         return true;
10     }
11     @Override
12     public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
13         updateText(String.format("onScroll velocity = (%f, %f)", mTouchListener.xVelocity, mTouchListener.yVelocity));
14         return false;
15     }
16 }

作为总结,下面是对上面一系列过程的回顾:

(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

 1 switch (action) {
 2     case MotionEvent.ACTION_DOWN:
 3         if (mVelocityTracker == null) {
 4             // Retrieve a new VelocityTracker object to watch the velocity of a motion.
 5             mVelocityTracker = VelocityTracker.obtain();
 6         } else {
 7             // Reset the velocity tracker back to its initial state.
 8             mVelocityTracker.clear();
 9         }
10         // Add a user‘s movement to the tracker.
11         mVelocityTracker.addMovement(motionEvent);
12     break;
13     case MotionEvent.ACTION_MOVE:
14         mVelocityTracker.addMovement(motionEvent);
15         // When you want to determine the velocity, call
16         // computeCurrentVelocity(). Then call getXVelocity()
17         // and getYVelocity() to retrieve the velocity for each pointer ID.
18         mVelocityTracker.computeCurrentVelocity(1000);
19         // Log velocity of pixels per second
20         xVelocity = mVelocityTracker.getXVelocity(pointerId);
21         yVelocity = mVelocityTracker.getYVelocity(pointerId);
22     break;
23     case MotionEvent.ACTION_CANCEL:
24         // Return a VelocityTracker object back to be re-used by others.
25         mVelocityTracker.recycle();
26     break;
27 }

然后再将触摸事件传递给FlingDetector去分析,在FlingDetector打印出mVelocityTracker的速度以及对应的状态(scroll还是fling),经过多次实验可以找到一个合理的决定scroll是否会导致fling的临界值。从事判断是否相应onScroll。

时间: 2024-10-13 12:15:45

【转】android手势处理揭秘的相关文章

Android手势源码浅析-----手势绘制(GestureOverlayView)

Android手势源码浅析-----手势绘制(GestureOverlayView)

android手势事件 快速移动 长按触摸屏 按下触摸屏,并拖动

/* 用户按下触摸屏.快速移动后松开 public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { //参数解释: // e1:第1个ACTION_DOWN MotionEvent // e2:最后一个ACTION_MOVE MotionEvent // velocityX:X轴上的移动速度,像素/秒 // velocityY:Y轴上的移动速度,像素/秒 // 触发条件 : /

Android手势操作使用Fling,Scroll等Gesture

Android手势操作  一盏灯, 一片昏黄: 一简书, 一杯淡茶. 守着那一份淡定, 品读属于自己的寂寞. 保持淡定, 才能欣赏到最美丽的风景! 保持淡定, 人生从此不再寂寞. 前言利用手势操作在现在的APP中越来越普及,大多数时候使用Fling,Scroll等Gesture能大幅度提高用户的操作体验,特别是大屏手机返回键程越来越大的现状下.  在Android系统下,手势识别是通过Gestur... douban.com/doulist/38811866/douban.com/doulist

Android 手势检测实战 打造支持缩放平移的图片预览效果(下)

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/39480503,本文出自:[张鸿洋的博客] 上一篇已经带大家实现了自由的放大缩小图片,简单介绍了下Matrix:具体请参考:Android 手势检测实战 打造支持缩放平移的图片预览效果(上):本篇继续完善我们的ImageView~~ 首先加入放大后的移动~~ 1.自由的进行移动 我们在onTouchEvent里面,加上移动的代码,当然了,必须长或宽大于屏幕才可以移动~~~ @Ov

【视频】嵌入式Linux/Android驱动开发揭秘(1)触摸屏驱动开发

嵌入式Linux/Android驱动开发揭秘(1)触摸屏驱动开发 专题简介:自1971年,美国人SamHurst发明了世界上第一个触摸传感器以来,触摸屏技术不断革新,给了程序设计师和UI工程师无限的想象空间,它极大改善了终端用户对各种设备的操作方便程度,现在我们的日常生活如手机.平板等,已经很大程度上依赖于和习惯于使用和操作触摸屏.做为工程师,我们很有必要掌握触摸屏的工作原理和软件驱动方法,如果您对一窥如何在嵌入式中操控和使用触摸屏这一司空见惯却又神奇的技术感兴趣,敬请关注! 1.LINUX驱动

【工作记录】android手势事件操作记录

/* 用户按下触摸屏.快速移动后松开 public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { //参数解释: // e1:第1个ACTION_DOWN MotionEvent // e2:最后一个ACTION_MOVE MotionEvent // velocityX:X轴上的移动速度,像素/秒 // velocityY:Y轴上的移动速度,像素/秒 // 触发条件 : /

Android 手势锁的实现 让自己的应用更加安全吧

今天偶遇以github上gesturelock关于手势锁的一个例子(有兴趣的去搜索下看看),于是下载下来研究,无奈基本没有注释,代码上存在一些问题(当设置gravity=center_vertical无法进行手势选择,无意中发现的),于是借鉴这位仁兄的代码,自己重写写了一个,修复了一些问题,加入一些基本的自定义属性,在此先感谢这位兄弟~. 先上图,默认效果图: 当然可以自定义数量啊,颜色神马的,自定义效果图: 如果你有艺术细胞,可以给我推荐几个颜色,无奈个人审美有问题~ 1.整体思路 a.自定义

Android手势缩放图片以及图片黏贴在手指随手势移动

一个Android手势缩放图片的工具类:同时,此类还实现另外一个功能:当手指按在触屏上移动时候,图片"黏贴"在手指上随手指移动而整体移动. 具体使用方法可以是这样:先new一个此类的实例,然后在ImageView的方法setOnTouchListener(new ImageViewOnMultiTouchListener()); 例如: ImageViewOnMultiTouchListener listener=new ImageViewOnMultiTouchListener();

Android手势密码LockPatternView、LockPasswordUtils、LockPatternUtils等分析

Android手势密码LockPatternView.LockPasswordUtils.LockPatternUtils 在使用别人写的这个手势密码的时候,我们通常是有自己的需求,可能这里的代码很多也很复杂,有没有什么很多注释,要把整个代码弄明白是要花很多时间而且基础要非常好的,可能在赶项目的时候没有时间去研究,下面我列出一些关键的地方,方便修改代码. 1.在创建手势密码的时候CreateGesturePasswordActivity中 下面是手势密码设置的时候界面的一些变化,和要处理的一些事