MyException - 我的异常网
当前位置:我的异常网» Android » 【Android每周课题】触摸屏手势

【Android每周课题】触摸屏手势

www.MyException.Cn  网友分享于:2013-10-09  浏览:91次
【Android每周专题】触摸屏手势

本系列文章均为A2BGeek原创,转载务必在明显处注明:
转载自A2BGeek的【Android每周专题】系列,原文链接:http://blog.csdn.net/benbmw2008/article/details/11367631

这篇专题来研究一下Android的触摸屏手势Gesture,Android的手势有两种,一种是View和Activity的(基于触摸屏事件,所以Activity也能添加手势),一种是手写输入法或者一些手机浏览器的快捷手势那样的带笔迹的手势识别。这一点从API文档中就能体现出来:分别有android.view.GestureDetector和android.gesture.Gesture。


我们先来介绍View和Activity的手势,再介绍输入法手势识别。

View和Activity的手势

这一部分想必大家都已经非常熟悉了,现在很多APP都会加入手势来提高交互体验,其中在某个界面向右Fling关闭该界面这一操作貌似成了APP的标配了。废话不多说了,直接进入正题。
为View和Activity加入手势操作的步骤如下:
1、为View或者Activity实现OnGestureListener接口。
2、覆写View或者Activity的OnTouchEvent方法,这里要返回GestureDetector.onTouchEvent()。
3、覆写你需要的手势的回调方法。
这里解释一下各种回调方法的含义:
按下(onDown): 刚刚手指接触到触摸屏的那一刹那,就是触的那一下。
抛掷(onFling): 手指在触摸屏上迅速移动,并松开的动作。
长按(onLongPress): 手指按在持续一段时间,并且没有松开。
滚动(onScroll): 手指在触摸屏上滑动。
按住(onShowPress): 手指按在触摸屏上,它的时间范围在按下起效,在长按之前。
抬起(onSingleTapUp):手指离开触摸屏的那一刹那。
除了这些定义之外,鄙人也总结了一点算是经验的经验吧,在这里和大家分享一下。
任何手势动作都会先执行一次按下(onDown)动作。
长按(onLongPress)动作前一定会执行一次按住(onShowPress)动作。
按住(onShowPress)动作和按下(onDown)动作之后都会执行一次抬起(onSingleTapUp)动作。
长按(onLongPress)、滚动(onScroll)和抛掷(onFling)动作之后都不会执行抬起(onSingleTapUp)动作。
我这里贴上一段代码,大家可以看一下实现的步骤,实验代码和触摸屏事件专题的代码差不多,我就不重复上传了。
package com.example.gesturedemo;

import android.content.Context;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.GestureDetector.OnGestureListener;
import android.view.MotionEvent;
import android.widget.TextView;

public class MyTextView extends TextView implements OnGestureListener {

	private GestureDetector mGestureDetector;

	public MyTextView(Context context) {
		super(context);
		// TODO Auto-generated constructor stub
		mGestureDetector = new GestureDetector(context, this);
	}

	public MyTextView(Context context, AttributeSet attrs) {
		super(context, attrs);
		// TODO Auto-generated constructor stub
		mGestureDetector = new GestureDetector(context, this);
	}

	public MyTextView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		// TODO Auto-generated constructor stub
		mGestureDetector = new GestureDetector(context, this);
	}

	@Override
	public boolean dispatchTouchEvent(MotionEvent event) {
		// TODO Auto-generated method stub
		DebugTool.log("MyTextView--->dispatchTouchEvent");
		return super.dispatchTouchEvent(event);
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		// TODO Auto-generated method stub
		DebugTool.log("MyTextView--->onTouchEvent");
		// int action = event.getAction();
		// switch (action) {
		// case MotionEvent.ACTION_DOWN:
		// DebugTool.log("MyTextView--->onTouchEvent--->DOWN");
		// break;
		// case MotionEvent.ACTION_MOVE:
		// DebugTool.log("MyTextView--->onTouchEvent--->MOVE");
		// break;
		// case MotionEvent.ACTION_UP:
		// DebugTool.log("MyTextView--->onTouchEvent--->UP");
		// break;
		// }
		return mGestureDetector.onTouchEvent(event);
	}

	@Override
	public boolean onDown(MotionEvent e) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
			float velocityY) {
		// TODO Auto-generated method stub
		DebugTool.log("MyTextView--->onFling");
		return false;
	}

	@Override
	public void onLongPress(MotionEvent e) {
		// TODO Auto-generated method stub

	}

	@Override
	public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
			float distanceY) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public void onShowPress(MotionEvent e) {
		// TODO Auto-generated method stub

	}

	@Override
	public boolean onSingleTapUp(MotionEvent e) {
		// TODO Auto-generated method stub
		return false;
	}

}

在OnGestureListener的带返回值的几个回调方法默认返回false的情况下,你会发现Fling手势是识别不到的,这是什么原因呢?这需要用上一专题的知识来解释http://blog.csdn.net/benbmw2008/article/details/11143893。大家应该能看出来手势是基于触摸屏事件传递的,对照着上一篇的“默认事件流向”图,读者可以想象其实就是在“MyTextView onTouchEvent”和“MyRelativeLayout onTouchEvent”之间加一个"MyTextView's GestureDetector onTouchEvent",而"MyTextView's GestureDetector onTouchEvent"返回false,事件还是会继续传递给“MyRelativeLayout onTouchEvent”。解决的办法是什么呢?自然是把OnGestureListener的带返回值的几个回调方法返回true,这样触摸屏事件就被“GestureDetector onTouchEvent”所消费,看一下日志会更加清楚:
09-08 10:36:10.714: V/gesturedemo(13130): MainActivity--->dispatchTouchEvent
09-08 10:36:10.718: V/gesturedemo(13130): MyRelativeLayout--->dispatchTouchEvent
09-08 10:36:10.722: V/gesturedemo(13130): MyRelativeLayout--->onInterceptTouchEvent
09-08 10:36:10.722: V/gesturedemo(13130): MyTextView--->dispatchTouchEvent
09-08 10:36:10.722: V/gesturedemo(13130): MyTextView--->onTouchEvent
09-08 10:36:10.824: V/gesturedemo(13130): MainActivity--->dispatchTouchEvent
09-08 10:36:10.828: V/gesturedemo(13130): MyRelativeLayout--->dispatchTouchEvent
09-08 10:36:10.832: V/gesturedemo(13130): MyRelativeLayout--->onInterceptTouchEvent
09-08 10:36:10.835: V/gesturedemo(13130): MyTextView--->dispatchTouchEvent
09-08 10:36:10.835: V/gesturedemo(13130): MyTextView--->onTouchEvent
09-08 10:36:10.863: V/gesturedemo(13130): MainActivity--->dispatchTouchEvent
09-08 10:36:10.878: V/gesturedemo(13130): MyRelativeLayout--->dispatchTouchEvent
09-08 10:36:10.886: V/gesturedemo(13130): MyRelativeLayout--->onInterceptTouchEvent
09-08 10:36:10.886: V/gesturedemo(13130): MyTextView--->dispatchTouchEvent
09-08 10:36:10.886: V/gesturedemo(13130): MyTextView--->onTouchEvent
09-08 10:36:10.898: V/gesturedemo(13130): MainActivity--->dispatchTouchEvent
09-08 10:36:10.898: V/gesturedemo(13130): MyRelativeLayout--->dispatchTouchEvent
09-08 10:36:10.898: V/gesturedemo(13130): MyRelativeLayout--->onInterceptTouchEvent
09-08 10:36:10.898: V/gesturedemo(13130): MyTextView--->dispatchTouchEvent
09-08 10:36:10.898: V/gesturedemo(13130): MyTextView--->onTouchEvent
09-08 10:36:10.917: V/gesturedemo(13130): MainActivity--->dispatchTouchEvent
09-08 10:36:10.917: V/gesturedemo(13130): MyRelativeLayout--->dispatchTouchEvent
09-08 10:36:10.917: V/gesturedemo(13130): MyRelativeLayout--->onInterceptTouchEvent
09-08 10:36:10.917: V/gesturedemo(13130): MyTextView--->dispatchTouchEvent
09-08 10:36:10.917: V/gesturedemo(13130): MyTextView--->onTouchEvent
09-08 10:36:10.917: V/gesturedemo(13130): MyTextView--->onFling
Activity的手势实现和View的是一样的,这里就不重复说了,只不过你不用再修改手势回调方法的返回值了。
下面我又突然想到了一个问题,就是手势区域重复了怎么办?举个例子,有一个只包含一个View的Activity,需要实现在View区域向右Fling关闭Activity,向下Fling做别的一些操作,
1、如果逻辑都实现在View上是非常简单的,在onFling中直接判断就可以了。
2、但是有的情况只允许向右Fling的判断实现在Activity中,向下Fling的判断实现在View中(或者相反,总之是分开判断),这个时候又该怎么办?
对于第二种情况,答案还是很有意思的,读者如果感兴趣的话可以发表评论,有营养的评论超过5条后我会将答案的代码下载地址放在文章的最后。

输入法手势识别

在手写输入中,会为每一个字符定义一个特征码,这些特征码都保存在相应的文件中(可能有一个或多个这样的文件),当用户绘制一个描述字符的图形时,系统会为所绘制的图形提取特征码,然后会在保存特征码文件中查找相对应的特征码,如果找到,就会将对应的字符返回。其中,这些文件被称为手势文件。
我们来做一个简单的手势识别APP。
一、首先需要建立手势文件,这里需要借助SDK自带的Sample,大家在Eclipse中File--->New--->Project--->Android--->Android Sample Project,选择GestureBuilder,运行之,建立几个自己的手势,然后把手势文件拿出来即可(每建立一个手势会有一个Toast弹出,告诉你文件的保存路径)。

其实可以想像gestures手势文件中就是一个一个的键值对。
二、把gestures文件放入工程的res/raw目录下,有关raw和assets的区别,请移步http://blog.csdn.net/benbmw2008/article/details/8754373。
布局文件如下:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <TextView
        android:id="@+id/tv1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:text="@string/hello_world"
        android:textSize="24sp" />

    <android.gesture.GestureOverlayView
        android:id="@+id/gestures"
        android:layout_width="200dip"
        android:layout_height="200dip"
        android:layout_centerInParent="true"
        android:background="#33B5E5" >
    </android.gesture.GestureOverlayView>

</RelativeLayout>

代码如下:
package com.a2bgeek.gesturedemo2;

import java.util.ArrayList;

import android.os.Bundle;
import android.app.Activity;
import android.gesture.Gesture;
import android.gesture.GestureLibraries;
import android.gesture.GestureLibrary;
import android.gesture.GestureOverlayView;
import android.gesture.GestureOverlayView.OnGesturePerformedListener;
import android.gesture.Prediction;
import android.view.Menu;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity {

	private TextView mTextView;
	private GestureOverlayView mGestureOverlayView;
	private GestureLibrary mGestureLibrary;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		initViews();
	}

	private void initViews() {
		mTextView = (TextView) findViewById(R.id.tv1);
		mGestureLibrary = GestureLibraries.fromRawResource(
				getApplicationContext(), R.raw.gestures);
		if (mGestureLibrary.load()) {
			mGestureOverlayView = (GestureOverlayView) findViewById(R.id.gestures);
			mGestureOverlayView
					.addOnGesturePerformedListener(new MyGesturePerformListener());
		}
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

	private class MyGesturePerformListener implements
			OnGesturePerformedListener {

		@Override
		public void onGesturePerformed(GestureOverlayView overlay,
				Gesture gesture) {
			// TODO Auto-generated method stub
			ArrayList<Prediction> list = mGestureLibrary.recognize(gesture);
			if (list.size() > 0) {
				//list是mGestureLibrary中可能与gesture匹配的手势集,匹配的程度会有一个score评分。
				StringBuilder sb = new StringBuilder();
				for (Prediction prediction : list) {
					sb.append(prediction.name);
					sb.append(":");
					sb.append(prediction.score);
					sb.append("\n");
				}
				mTextView.setText(sb.toString());
			} else {
				Toast.makeText(getApplicationContext(), "没有匹配",
						Toast.LENGTH_SHORT).show();
			}
		}

	}

}


好了,今天的内容就到这里了,第一周更新了两篇专题,是个良好的开始,嗯嗯,加油。

实验代码点击下载

文章评论

什么才是优秀的用户界面设计
什么才是优秀的用户界面设计
科技史上最臭名昭著的13大罪犯
科技史上最臭名昭著的13大罪犯
如何区分一个程序员是“老手“还是“新手“?
如何区分一个程序员是“老手“还是“新手“?
一个程序员的时间管理
一个程序员的时间管理
我的丈夫是个程序员
我的丈夫是个程序员
60个开发者不容错过的免费资源库
60个开发者不容错过的免费资源库
代码女神横空出世
代码女神横空出世
程序员最害怕的5件事 你中招了吗?
程序员最害怕的5件事 你中招了吗?
看13位CEO、创始人和高管如何提高工作效率
看13位CEO、创始人和高管如何提高工作效率
程序员的鄙视链
程序员的鄙视链
做程序猿的老婆应该注意的一些事情
做程序猿的老婆应该注意的一些事情
那些争议最大的编程观点
那些争议最大的编程观点
程序员应该关注的一些事儿
程序员应该关注的一些事儿
我是如何打败拖延症的
我是如何打败拖延症的
程序员和编码员之间的区别
程序员和编码员之间的区别
编程语言是女人
编程语言是女人
每天工作4小时的程序员
每天工作4小时的程序员
老程序员的下场
老程序员的下场
为啥Android手机总会越用越慢?
为啥Android手机总会越用越慢?
“肮脏的”IT工作排行榜
“肮脏的”IT工作排行榜
程序员周末都喜欢做什么?
程序员周末都喜欢做什么?
中美印日四国程序员比较
中美印日四国程序员比较
初级 vs 高级开发者 哪个性价比更高?
初级 vs 高级开发者 哪个性价比更高?
10个帮程序员减压放松的网站
10个帮程序员减压放松的网站
当下全球最炙手可热的八位少年创业者
当下全球最炙手可热的八位少年创业者
“懒”出效率是程序员的美德
“懒”出效率是程序员的美德
聊聊HTTPS和SSL/TLS协议
聊聊HTTPS和SSL/TLS协议
为什么程序员都是夜猫子
为什么程序员都是夜猫子
漫画:程序员的工作
漫画:程序员的工作
亲爱的项目经理,我恨你
亲爱的项目经理,我恨你
老美怎么看待阿里赴美上市
老美怎么看待阿里赴美上市
不懂技术不要对懂技术的人说这很容易实现
不懂技术不要对懂技术的人说这很容易实现
程序员必看的十大电影
程序员必看的十大电影
Web开发者需具备的8个好习惯
Web开发者需具备的8个好习惯
写给自己也写给你 自己到底该何去何从
写给自己也写给你 自己到底该何去何从
团队中“技术大拿”并非越多越好
团队中“技术大拿”并非越多越好
总结2014中国互联网十大段子
总结2014中国互联网十大段子
旅行,写作,编程
旅行,写作,编程
程序员的一天:一寸光阴一寸金
程序员的一天:一寸光阴一寸金
要嫁就嫁程序猿—钱多话少死的早
要嫁就嫁程序猿—钱多话少死的早
Web开发人员为什么越来越懒了?
Web开发人员为什么越来越懒了?
程序员都该阅读的书
程序员都该阅读的书
 程序员的样子
程序员的样子
我跳槽是因为他们的显示器更大
我跳槽是因为他们的显示器更大
Java 与 .NET 的平台发展之争
Java 与 .NET 的平台发展之争
软件开发程序错误异常ExceptionCopyright © 2009-2015 MyException 版权所有