以下是之前事件学习的文章:
《Android Touch事件学习 2 触发点击事件的地方》
《Android Touch事件学习 3 区分各种手势基础知识》
《Android Touch事件学习 4 获取手指触摸位置》上一篇分析了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事件系列中
一、 效果图
二、代码
在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 添加可能遇到的问题
2014-02-04 添加到事件学习系列中