Android 处理手势方法
之前做的 App 是完全没有任何手势支持的,对于现在的程序来说,如果没有一些手势的支持,感觉实在是有点落后了,支持手势的 App 才叫 cool。于是在这次重新搭建 ifood for android 框架的同时下决心让自己的 App 完全支持手势。下面就来看下自己实现的一个全局滑动切换窗口的例子。
在 android 系统中,手势的识别是通过 GestureDetector.OnGestureListener
接口来实现的。如果要自定义手势需要重写这个接口里的一些方法,废话不多说,下面上代码:
自定义的一个 GestureLisntener:
MyGestureListener.java
public class MyGestureListener implements OnGestureListener {
static final String TAG = "MyGestureListener";
private static final int SWIPE_MAX_OFF_PATH = 100;
private static final int SWIPE_MIN_DISTANCE = 100;
private static final int SWIPE_THRESHOLD_VELOCITY = 100;
public Context context;
public MyGestureListener(Context context) {
this.context = context;
}
@Override
public boolean onDown(MotionEvent e) {
// TODO Auto-generated method stub
return false;
}
@Override
public void onShowPress(MotionEvent e) {
// TODO Auto-generated method stub
Log.e(TAG, "onShowPress");
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
float distanceY) {
// TODO Auto-generated method stub
return false;
}
@Override
public void onLongPress(MotionEvent e) {
// TODO Auto-generated method stub
Log.e(TAG, "onLongPress");
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
if (Math.abs(e1.getY() - e2.getY()) > SWIPE_MAX_OFF_PATH)
return false;
if ((e1.getX() - e2.getX()) > SWIPE_MIN_DISTANCE
&& Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
Log.e(TAG, "onFling left");
} else if ((e2.getX() - e1.getX()) > SWIPE_MIN_DISTANCE
&& Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
Log.e(TAG, "onFling right");
((Activity) context).finish();
}
return true;
}
}
在这个类中的 onFling() 方法中从左向右滑动时实现了界面的切换,如果有更复杂的手势支持,同样可以在这个基类中进行添加。
接下来新建一个 GestureActivity 实现 Gesture 滑动切换界面,让支持手势的 Activity 继承它,这样就继承了它的手势支持功能,提高代码复用。
GestureActivity.java
public class GestureActivity extends ActivityBase {
MyGestureListener listener = new MyGestureListener(this);
protected GestureDetector gestureDetector = new GestureDetector(listener);
public boolean onTouchEvent(MotionEvent event) {
if (gestureDetector.onTouchEvent(event))
return true;
else
return false;
}
}
这样就实现了一个简单的滑动切换页面的框架,如果想支持更多的手势,只需要重写 MyGestureListener 的方法就可以了。
不过不要高兴的太早,在一般的 Activity 手势支持是正常的,可是碰到一些包含 ScrollView 或者 ListView 的 Activity 时,手势就不相应了。原因是因为这些滑动的组件本身就已经具有了手势的支持,这样就会产生了冲突,导致自定义的手势没有被识别到。google 了很久,似乎也没个具体的方法,后来看到说用 dispatchTouchEvent(MotionEvent ev)
的方法,果然可以。于是在 GestureActivity 里就多了这样一个方法:
public boolean dispatchTouchEvent(MotionEvent ev) {
gestureDetector.onTouchEvent(ev);
return super.dispatchTouchEvent(ev);
}
此时再试一下,果然所有 Activity 都实现了自定义的手势事件。但是为什么加上这个方法就可以了呢,必须要搞明白。
android 中的事件类型分为按键事件和屏幕触摸事件,Touch 事件是屏幕触摸事件的基础事件,有必要对它进行深入的了解。
一个最简单的屏幕触摸动作触发了一系列 Touch 事件:
ACTION_DOWN->ACTION_MOVE->ACTION_MOVE->ACTION_MOVE...->ACTION_MOVE->ACTION_UP
当屏幕中包含一个 ViewGroup,而这个 ViewGroup 又包含一个子 view,这个时候 android 系统如何处理 Touch 事件呢?到底是 ViewGroup 来处理 Touch 事件,还是子 view 来处理 Touch 事件呢?答案是:不一定。
android 系统中的每个 View 的子类都具有下面三个和 TouchEvent 处理密切相关的方法:
- public boolean dispatchTouchEvent(MotionEvent ev) 这个方法用来分发 TouchEvent
- public boolean onInterceptTouchEvent(MotionEvent ev) 这个方法用来拦截 TouchEvent
- public boolean onTouchEvent(MotionEvent ev) 这个方法用来处理 TouchEvent
当 TouchEvent 发生时,首先 Activity 将 TouchEvent 传递给最顶层的 View, TouchEvent 最先到达最顶层 view 的 dispatchTouchEvent ,然后由 dispatchTouchEvent 方法进行分发,如果 dispatchTouchEvent 返回 true ,则交给这个 view 的 onTouchEvent 处理,如果 dispatchTouchEvent 返回 false ,则交给这个 view 的 interceptTouchEvent 方法来决定是否要拦截这个事件,如果 interceptTouchEvent 返回 true ,也就是拦截掉了,则交给它的 onTouchEvent 来处理,如果 interceptTouchEvent 返回 false ,那么就传递给子 view ,由子 view 的 dispatchTouchEvent 再来开始这个事件的分发。
如果事件传递到某一层的子 view 的 onTouchEvent 上了,这个方法返回了 false ,那么这个事件会从这个 view 往上传递,都是 onTouchEvent 来接收。而如果传递到最上面的 onTouchEvent 也返回 false 的话,这个事件就会 消失
,而且接收不到下一次事件。
看到这终于清楚了上面的疑问, dispatchTouchEvent()
方法直接将触摸事件交给了 gestureDetector 的触摸事件,这样就解决了冲突问题。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论