第19天 Android Touch事件学习 6 手势识别

来源:互联网 发布:网络技术交流 编辑:程序博客网 时间:2024/05/22 01:28


触摸事件学习系列文章详见:

《Android Touch事件学习系列汇总》


    上一篇分析了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时触发@Overridepublic boolean onDown(MotionEvent e) {updateLog("onDown");return super.onDown(e);}// 在Touch down之后一定时间(115ms)触发@Overridepublic void onShowPress(MotionEvent e) {updateLog("onShowPress");}@Overridepublic boolean onSingleTapUp(MotionEvent e) {updateLog("onSingleTapUp");return super.onSingleTapUp(e);}// 滑动时触发@Overridepublic boolean onScroll(MotionEvent e1, MotionEvent e2,float distanceX, float distanceY) {updateLog("onScroll");return super.onScroll(e1, e2, distanceX, distanceY);}// 滑动一段距离,up时触发@Overridepublic boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,float velocityY) {updateLog("onFling");return super.onFling(e1, e2, velocityX, velocityY);}// 长按后触发(Touch down之后一定时间(500ms))@Overridepublic 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());    }@Overridepublic 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());    }@Overridepublic 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时触发@Overridepublic boolean onDown(MotionEvent e) {updateLog("onDown");return super.onDown(e);}// 在Touch down之后一定时间(115ms)触发@Overridepublic void onShowPress(MotionEvent e) {updateLog("onShowPress");}@Overridepublic boolean onSingleTapUp(MotionEvent e) {updateLog("onSingleTapUp");return super.onSingleTapUp(e);}// 滑动时触发@Overridepublic boolean onScroll(MotionEvent e1, MotionEvent e2,float distanceX, float distanceY) {updateLog("onScroll");return super.onScroll(e1, e2, distanceX, distanceY);}// 滑动一段距离,up时触发@Overridepublic boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,float velocityY) {updateLog("onFling");return super.onFling(e1, e2, velocityX, velocityY);}// 长按后触发(Touch down之后一定时间(500ms))@Overridepublic 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时触发@Overridepublic boolean onDown(MotionEvent e) {updateLog("onDown");return super.onDown(e);}// 在Touch down之后一定时间(115ms)触发@Overridepublic void onShowPress(MotionEvent e) {updateLog("onShowPress");}@Overridepublic boolean onSingleTapUp(MotionEvent e) {updateLog("onSingleTapUp");return super.onSingleTapUp(e);}// 滑动时触发@Overridepublic boolean onScroll(MotionEvent e1, MotionEvent e2,float distanceX, float distanceY) {updateLog("onScroll");return super.onScroll(e1, e2, distanceX, distanceY);}// 滑动一段距离,up时触发@Overridepublic boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,float velocityY) {updateLog("onFling");return super.onFling(e1, e2, velocityX, velocityY);}// 长按后触发(Touch down之后一定时间(500ms))@Overridepublic 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());    }@Overridepublic 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());    }@Overridepublic 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时触发@Overridepublic boolean onDown(MotionEvent e) {updateLog("onDown");return super.onDown(e);}// 在Touch down之后一定时间(115ms)触发@Overridepublic void onShowPress(MotionEvent e) {updateLog("onShowPress");}@Overridepublic boolean onSingleTapUp(MotionEvent e) {updateLog("onSingleTapUp");return super.onSingleTapUp(e);}// 滑动时触发@Overridepublic boolean onScroll(MotionEvent e1, MotionEvent e2,float distanceX, float distanceY) {updateLog("onScroll");return super.onScroll(e1, e2, distanceX, distanceY);}// 滑动一段距离,up时触发@Overridepublic boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,float velocityY) {updateLog("onFling");return super.onFling(e1, e2, velocityX, velocityY);}// 长按后触发(Touch down之后一定时间(500ms))@Overridepublic 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  添加到事件学习系列中



1 0
原创粉丝点击