触摸事件学习系列文章详见:
上一篇分析了View.onTouchEvent源码,发现点击与长按事件处理的原理,但是如果自定义控件需要其他手势呢?假如需要判断滚动事件,之前学习到的代码提供不了帮助,当前也可以自己根据ACTOIN与当前触摸位置来进行判断(如果想自定义滚动事件可以参考ListView的源码),不过Android系统提供了工具类来支持这些手势。
以下仅仅是演示如何使用,并没有列出所有支持的手势,完整的详见官方文档《SimpleOnGestureListener》 ,也可以阅读其源码学习如何判断各种手势。
一、 效果图
二、代码
在Android中自定义控件通常都会涉及到触摸手势,需要判断用户是点击、滑动、Fling、按下等状态,当前这些都可以通过自己在onTouchEvent中通过MotionEvent回调参数判断得到,但是也可以使用Android系统提供的辅助类SimpleOnGestureListener,以下是继承自此类,主要目的是为了观察各个回调的作用。
如果想了解跟多系统是如何判断是这些手势的可以查看其源码,也可以看下ListView,Gallery的源码前者是在AbsListView中如何判断Tap、Scroll、Fling手势,后者使用的和本文一样,也可以查看其源码更详细的了解当前辅助类的用法。
private class DefaultGestureListener extends SimpleOnGestureListener {
// Touch down时触发
@Override
public boolean onDown(MotionEvent e) {
updateLog("onDown");
return super.onDown(e);
}
// 在Touch down之后一定时间(115ms)触发
@Override
public void onShowPress(MotionEvent e) {
updateLog("onShowPress");
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
updateLog("onSingleTapUp");
return super.onSingleTapUp(e);
}
// 滑动时触发
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
updateLog("onScroll");
return super.onScroll(e1, e2, distanceX, distanceY);
}
// 滑动一段距离,up时触发
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
updateLog("onFling");
return super.onFling(e1, e2, velocityX, velocityY);
}
// 长按后触发(Touch down之后一定时间(500ms))
@Override
public void onLongPress(MotionEvent e) {
updateLog("onLongPress");
}
}
创建与触发手势
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mOutput = (TextView) findViewById(R.id.output);
output("");
mGestureDetector = new GestureDetector(this, new DefaultGestureListener());
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// 按下时清理之前的记录
if (event.getAction() == MotionEvent.ACTION_DOWN) {
mRecordMap.clear();
}
return mGestureDetector.onTouchEvent(event);
}
上完整代码
package loveworld.gesturedetector;
import java.util.LinkedHashMap;
import java.util.Map;
import android.app.Activity;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import android.widget.TextView;
/**
*
* 手势识别
*
*
* 步骤
* 1. 继承自SimpleOnGestureListener创建子类
* 2. 覆写相应的方法,包括长按,滑动之类的
*
* 3. Activity中创建 GestureDetector, 传入自定义子类实例mGestureDetector
* 4. Activity覆写onTouchEvent并返回mGestureDetector.onTouchEvent(event);
*
*/
public class GestureDetectorDemoActivity extends Activity {
// ===========================================================
// Constants
// ===========================================================
// ===========================================================
// Fields
// ===========================================================
private TextView mOutput;
private GestureDetector mGestureDetector;
private LinkedHashMap<String, Integer> mRecordMap = new LinkedHashMap<String, Integer>();
// ===========================================================
// Constructors
// ===========================================================
// ===========================================================
// Public Methods
// ===========================================================
// ===========================================================
// Methods for/from SuperClass/Interfaces
// ===========================================================
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mOutput = (TextView) findViewById(R.id.output);
output("");
mGestureDetector = new GestureDetector(this, new DefaultGestureListener());
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// 按下时清理之前的记录
if (event.getAction() == MotionEvent.ACTION_DOWN) {
mRecordMap.clear();
}
return mGestureDetector.onTouchEvent(event);
}
// ===========================================================
// Private Methods
// ===========================================================
private void updateLog(String name) {
if (TextUtils.isEmpty(name)) {
return;
}
if (mRecordMap == null) {
Log.e("Test", "mRecordMap == null");
}
// 不存在创建新的
boolean containsKey = mRecordMap.containsKey(name);
if (!containsKey) {
mRecordMap.put(name, 0);
}
// 获取之前记录
int oldCount = mRecordMap.get( name );
int count = oldCount + 1;
// 更新记录
mRecordMap.put(name, count);
// 拼接出来
StringBuilder stringBuilder = new StringBuilder();
for (Map.Entry<String, Integer> entry : mRecordMap.entrySet()) {
String key = entry.getKey();
Integer value = entry.getValue();
stringBuilder.append("执行方法 : " + key);
stringBuilder.append(" , 执行次数 : " + value);
stringBuilder.append("\n");
}
output( stringBuilder.toString() );
}
private void output(String output) {
mOutput.setText("手指在屏幕滑动:\n" + output);
}
// ===========================================================
// Inner and Anonymous Classes
// ===========================================================
private class DefaultGestureListener extends SimpleOnGestureListener {
// Touch down时触发
@Override
public boolean onDown(MotionEvent e) {
updateLog("onDown");
return super.onDown(e);
}
// 在Touch down之后一定时间(115ms)触发
@Override
public void onShowPress(MotionEvent e) {
updateLog("onShowPress");
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
updateLog("onSingleTapUp");
return super.onSingleTapUp(e);
}
// 滑动时触发
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
updateLog("onScroll");
return super.onScroll(e1, e2, distanceX, distanceY);
}
// 滑动一段距离,up时触发
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
updateLog("onFling");
return super.onFling(e1, e2, velocityX, velocityY);
}
// 长按后触发(Touch down之后一定时间(500ms))
@Override
public void onLongPress(MotionEvent e) {
updateLog("onLongPress");
}
}
}
布局layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/output"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</LinearLayout>
三、另外一种使用方式
自定义类继承自SimpleOnGestureListener且实现OnTouchListener,在自定义视图创建的时候创建此自定义类且setOnTouchListener( 当前自定义类对象 )。
那自自定义类与当前自定义视图onTouchEvent的调用顺序?
setOnTouchListener 与 onTouchEvent覆写方法 关系 - setOnTouchListener 是在当前视图的dispatchTouchEvent 中调用
四、可能遇到的问题
如果onScroll、onFling不执行可以尝试覆写onDown返回true
2013-04-18 完全重写本篇文章
2013-05-27 添加可能遇到的问题
2013-02-04 整理到Android事件系列中