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


以下是之前事件学习的文章:

《Andorid Touch事件学习 1 点击事件》

《Android Touch事件学习 2 触发点击事件的地方》

《Android Touch事件学习 3 区分各种手势基础知识》

《Android Touch事件学习 4 获取手指触摸位置》

《Android Touch事件学习 5 点击与长按原理》


    上一篇分析了View.onTouchEvent源码,发现点击与长按事件处理的原理,但是如果自定义控件需要其他手势呢?假如需要判断滚动事件,之前学习到的代码提供不了帮助,当前也可以自己根据ACTOIN与当前触摸位置来进行判断(如果想自定义滚动事件可以参考ListView的源码),不过Android系统提供了工具类来支持这些手势。

    以下仅仅是演示如何使用,并没有列出所有支持的手势,完整的详见官方文档《SimpleOnGestureListener》 ,也可以阅读其源码学习如何判断各种手势。


一、 效果图

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




二、代码

     在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事件系列中



一、 效果图

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




二、代码

     在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  添加到事件学习系列中



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

上一篇:【QT相关】文件、目录基础操作


下一篇:Android 测试 Intel HAXM 超快模拟器时出现的问题解决