Android 触摸手势基础 官方文档概览
Android 触摸手势基础 官方文档概览
触摸手势检测基础
手势检测一般包含两个阶段:
1.获取touch事件数据
2.解析这些数据,看它们是否满足你的应用所支持的某种手势。
相关API:
兼容版的:
(Note that MotionEventCompat is not a replacement for the MotionEvent class. Rather, it provides static utility methods to which you pass your MotionEvent object in order to receive the desired action associated with that event.)
一般的Activity或View中的touch事件处理
Activity或View类的onTouchEvent()
回调函数会接收到touch事件。
为了截获touch事件,你需要覆写Activity或View的onTouchEvent方法。
View中还可以使用setOnTouchListener()方法添加点击事件的 View.OnTouchListener
监听对象。这样就可以不继承View而处理点击事件。
但是如果需要处理双击、长按、fling(快滑)等手势,你需要利用 GestureDetector
类。
onTouchEvent方法的返回值
onTouchEvent方法的返回值,如果返回true,意味着你已经处理过了touch事件;如果返回false,将会继续通过view stack传递事件,直到事件被处理。
这里需要注意 ACTION_DOWN
事件,如果返回false,则该listener将不会被告知后面的一系列 ACTION_MOVE
和 ACTION_UP
事件。
检测手势
Android提供了GestureDetector
类来检测一般的手势。
基本使用:
1.生成GestureDetector
对象(或GestureDetectorCompat对象),构造时的参数传入监听器对象。
监听器对象实现GestureDetector.OnGestureListener
接口。
如果你仅仅是想利用其中的一些手势而不是全部,那么你可以选择继承GestureDetector.SimpleOnGestureListener类,这是一个适配器模式,即这个类实现了GestureDetector.OnGestureListener
接口,为其中所有的方法提供了空实现(返回值都是false),当继承GestureDetector.SimpleOnGestureListener类时,子类只需要覆写感兴趣的方法,其他方法是空实现。
2.为了让 GestureDetector
对象接收到事件,需要覆写View或Activity中的 onTouchEvent()
方法,将事件传递给detector对象。
一个例子:
package com.example.hellogesturedetector; import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.GestureDetector;
import android.view.GestureDetector.OnDoubleTapListener;
import android.view.GestureDetector.OnGestureListener;
import android.view.Menu;
import android.view.MotionEvent;
import android.view.ViewGroup;
import android.widget.TextView; public class HelloGestureDetectorActivity extends Activity { private static final String LOG_TAG = "HelloGesture";
private GestureDetector mGestureDetector = null; private TextView mGestureTextView = null;
private TextView mDoubleTapTextView = null; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_hello_gesture_detector); mGestureTextView = (TextView) findViewById(R.id.gesture);
mDoubleTapTextView = (TextView) findViewById(R.id.doubleTap); // 构造GestureDetector对象,传入监听器对象
mGestureDetector = new GestureDetector(this, mOnGestureListener); // 传入双击监听器对象
mGestureDetector.setOnDoubleTapListener(mDoubleTapListener); } @Override
public boolean onTouchEvent(MotionEvent event) {
// 在onTouchEvent方法中将事件传递给手势检测对象,否则手势监听对象中的回调函数是不会被调用的
mGestureDetector.onTouchEvent(event);
return super.onTouchEvent(event);
} private OnGestureListener mOnGestureListener = new OnGestureListener() { @Override
public boolean onSingleTapUp(MotionEvent e) {
Log.i(LOG_TAG, "onSingleTapUp: " + e.toString());
mGestureTextView.setText("onSingleTapUp: ");
return false; } @Override
public void onShowPress(MotionEvent e) {
Log.i(LOG_TAG, "onShowPress: " + e.toString());
mGestureTextView.setText("onShowPress: ");
} @Override
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
Log.i(LOG_TAG, "onScroll: " + e1.toString() + ", " + e2.toString());
mGestureTextView.setText("onScroll ");
return false;
} @Override
public void onLongPress(MotionEvent e) {
Log.i(LOG_TAG, "onLongPress: " + e.toString());
mGestureTextView.setText("onLongPress: ");
} @Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
Log.i(LOG_TAG, "onFling: " + e1.toString() + ", " + e2.toString());
mGestureTextView.setText("onFling ");
return false;
} @Override
public boolean onDown(MotionEvent e) { Log.i(LOG_TAG, "onDown: " + e.toString());
mGestureTextView.setText("onDown: "); return false; }
}; private OnDoubleTapListener mDoubleTapListener = new OnDoubleTapListener() { @Override
public boolean onSingleTapConfirmed(MotionEvent e) {
Log.i("LOG_TAG", "onSingleTapConfirmed: " + e.toString());
mDoubleTapTextView.setText("onSingleTapConfirmed: ");
return false;
} @Override
public boolean onDoubleTapEvent(MotionEvent e) {
Log.i("LOG_TAG", "onDoubleTapEvent: " + e.toString());
mDoubleTapTextView.setText("onDoubleTapEvent: ");
return false;
} @Override
public boolean onDoubleTap(MotionEvent e) {
Log.i("LOG_TAG", "onDoubleTap: " + e.toString());
mDoubleTapTextView.setText("onDoubleTap: ");
return false;
}
};
}
HelloGestureDetectorActivity.java
根据官网上说:
关于onDown()方法的返回值,最好是返回true,因为所有的手势都是从onDown()信息开始的。
如果像 GestureDetector.SimpleOnGestureListener
默认实现一样返回false,系统就会认为你想要忽略之后的其他手势,然后GestureDetector.OnGestureListener
的其他方法就不会被调用。
但是实际程序验证的时候,发现返回true还是false好像没有什么影响。(??)
跟踪运动 速度
有很多不同的方法来记录手势中的运动,比如pointer的起始位置和终止位置;pointer运动的方向;手势的历史(通过 getHistorySize()
方法得到);还有pointer的运动速度。
Android提供了VelocityTracker
类和VelocityTrackerCompat类,来记录touch事件的速度。
代码例子:
public class MainActivity extends Activity {
private static final String DEBUG_TAG = "Velocity";
...
private VelocityTracker mVelocityTracker = null;
@Override
public boolean onTouchEvent(MotionEvent event) {
int index = event.getActionIndex();
int action = event.getActionMasked();
int pointerId = event.getPointerId(index); switch(action) {
case MotionEvent.ACTION_DOWN:
if(mVelocityTracker == null) {
// Retrieve a new VelocityTracker object to watch the velocity of a motion.
mVelocityTracker = VelocityTracker.obtain();
}
else {
// Reset the velocity tracker back to its initial state.
mVelocityTracker.clear();
}
// Add a user's movement to the tracker.
mVelocityTracker.addMovement(event);
break;
case MotionEvent.ACTION_MOVE:
mVelocityTracker.addMovement(event);
// When you want to determine the velocity, call
// computeCurrentVelocity(). Then call getXVelocity()
// and getYVelocity() to retrieve the velocity for each pointer ID.
mVelocityTracker.computeCurrentVelocity(1000);
// Log velocity of pixels per second
// Best practice to use VelocityTrackerCompat where possible.
Log.d("", "X velocity: " +
VelocityTrackerCompat.getXVelocity(mVelocityTracker,
pointerId));
Log.d("", "Y velocity: " +
VelocityTrackerCompat.getYVelocity(mVelocityTracker,
pointerId));
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
// Return a VelocityTracker object back to be re-used by others.
mVelocityTracker.recycle();
break;
}
return true;
}
}
Tracking Velocity
滚动手势
如果一个标准的布局有可能会超出它的容器的边界,可以把它嵌套在一个ScrollView中,这样就会得到一个可以滚动的布局,由framewok处理。
实现一个自定义的scroller应该只在一些特殊情况下需要。
Scroller用来随时间制造滚动动画,使用平台标准的滚动物理参数(摩擦力、速度等)。
Scroller自己本身实际上并不绘制任何东西。
Scroller记录滚动的偏移值,但是它并不会将这些位置应用到你的View,你需要自己动手。
详见:http://developer.android.com/training/gestures/scroll.html
多点触摸手势
当多个pointer同时触摸屏幕,系统会生成如下事件:
ACTION_DOWN
—For the first pointer that touches the screen. This starts the gesture. The pointer data for this pointer is always at index 0 in theMotionEvent
.ACTION_POINTER_DOWN
—For extra pointers that enter the screen beyond the first. The pointer data for this pointer is at the index returned bygetActionIndex()
.ACTION_MOVE
—A change has happened during a press gesture.ACTION_POINTER_UP
—Sent when a non-primary pointer goes up.ACTION_UP
—Sent when the last pointer leaves the screen.
你可以依靠每一个pointer的index和ID来追踪每一个pointer:
Index:MotionEvent会把每一个pointer的信息放在一个数组里,index即是这个数组索引。大多数你用的MotionEvent方法是以这个index作为参数的。
ID:每一个pointer还有一个ID映射,在touch事件中保持恒定一致(persistent),这样就可以在整个手势中跟踪一个单独的pointer。
pointer在一个motion event中出现的顺序是未定的,所以pointer的index在不同的事件中是可变的,但是只要pointer保持active,它的ID是保持不变的。
通过getPointerId()
获得ID,这样就可以在多个motion event中追踪pointer。然后对于连续的motion event,可以使用findPointerIndex()方法来获得指定ID的pointer在当前事件中的index。
比如:
private int mActivePointerId; public boolean onTouchEvent(MotionEvent event) {
....
// Get the pointer ID
mActivePointerId = event.getPointerId(0); // ... Many touch events later... // Use the pointer ID to find the index of the active pointer
// and fetch its position
int pointerIndex = event.findPointerIndex(mActivePointerId);
// Get the pointer's current position
float x = event.getX(pointerIndex);
float y = event.getY(pointerIndex);
}
获取MotionEvent的动作应该使用getActionMasked()方法(或者是兼容版的MotionEventCompat.getActionMasked())。
与旧版的getAction()不同,getActionMasked()
方法是被设计为可以多个pointer工作的。
它会返回带掩模的动作,不带pointer用于index的那些位。
你可以使用getActionIndex()来得到index。
拖动和缩放
拖动一个对象:
如果是Android 3.0以上,可以使用View的新接口View.OnDragListener
参见:Drag and Drop。
其他参见:http://developer.android.com/training/gestures/scale.html
缩放可以使用 ScaleGestureDetector
。
ScaleGestureDetector
可以和GestureDetector
一起使用。
ViewGroup中的Touch事件处理
处理 ViewGroup
的touch事件要麻烦一些,因为很可能各种touch事件的目标不是ViewGroup而是它的child。
为了确保每一个child正确地接收到touch events,需要覆写ViewGroup的onInterceptTouchEvent()方法。
如果onInterceptTouchEvent()方法返回true,说明MotionEvent被截获了,它将不会被传递给child,而是传递给parent的 onTouchEvent()
方法。
如果你在parent的onInterceptTouchEvent()方法中返回了true,先前还在处理touch event的child view将会接收到一个 ACTION_CANCEL
,之后的事件就会全传递到parent的onTouchEvent中。
如果 onInterceptTouchEvent()
方法返回false,则事件继续顺着view结构向下传递,parent不会截获事件,也不会调用parent的onTouchEvent()方法。
另:
ViewConfiguration提供一些常量。
TouchDelegate类可以用来设置View的触摸区域。
用法见:http://developer.android.com/training/gestures/viewgroup.html
参考资料
Training: Using Touch Gestures
http://developer.android.com/training/gestures/index.html
Android 触摸手势基础 官方文档概览的更多相关文章
- Android 触摸手势基础 官方文档概览2
Android 触摸手势基础 官方文档概览 触摸手势检测基础 手势检测一般包含两个阶段: 1.获取touch事件数据 2.解析这些数据,看它们是否满足你的应用所支持的某种手势. 相关API: Moti ...
- gRPC官方文档(概览)
文章来自gRPC 官方文档中文版 概览 开始 欢迎进入 gRPC 的开发文档,gRPC 一开始由 google 开发,是一款语言中立.平台中立.开源的远程过程调用(RPC)系统. 本文档通过快速概述和 ...
- 【Android API】Android 4.1 API官方文档详解
原文:http://android.eoe.cn/topic/summary 翻译:[eoeAndroid原创团队]kris.流风而逝.贼寇在何方.snowxwyo.lsy4833406 更新日期:2 ...
- Android File类 根据官方文档理解(转)
File有四个构造函数 public File(File dir,String name) 参数为File和String,File制定构造的新的File对象的路径 ...
- Android File类 根据官方文档理解
File有四个构造函数 public File(File dir,String name) 参数为File和String,File制定构造的新的File对象的路径 ...
- Redis基础(官方文档总结)
REmote DIctionary Server(Redis) Redis是一个开源的使用ANSI C语言编写.遵守BSD协议.支持网络.可基于内存亦可持久化的日志型.Key-Value数据库,并提供 ...
- Akka Typed 官方文档之随手记
️ 引言 近两年,一直在折腾用FP与OO共存的编程语言Scala,采取以函数式编程为主的方式,结合TDD和BDD的手段,采用Domain Driven Design的方法学,去构造DDDD应用(Dom ...
- OpenGL ES着色器语言之着色概览(官方文档)
OpenGL ES着色器语言之着色概览(官方文档第二章) 事实上,OpenGL ES着色语言是两种紧密关联的语言.这些语言用来在OpenGL ES处理管线的可编程处理器创建着色器. 在本文档中,除非另 ...
- [翻译]Android官方文档 - 通知(Notifications)
翻译的好辛苦,有些地方也不太理解什么意思,如果有误,还请大神指正. 官方文档地址:http://developer.android.com/guide/topics/ui/notifiers/noti ...
随机推荐
- [转]Struts2数据传输的背后机制:ValueStack(值栈)
1. 数据传输背后机制:ValueStack(值栈) 在这一切的背后,是因为有了ValueStack(值栈)! 2. ValueStack基础:OGNL 要了解ValueStack,必须先理解OGNL ...
- BitCoinJ之Hello World示例程序
简介 比特币(BitCoin)是一种基于区块链(BlockChain)技术的数字化货币. 本文介绍了使用基于Java语言的BitCoinJ API实现一个简单的收取和支付比特币的示例程序. 开发环境 ...
- Bootstrap+angularjs+MVC3+分页技术+角色权限验证系统
1.Bootstrap使用教程 相关教程: http://www.bootcss.com/components.html 页面使用代码: <script src="@Url.Conte ...
- 【Swift学习】Swift编程之旅---函数(十)
函数是一组用于执行特定任务的独立的代码段,你用一个名字来标识函数,这个名字是用来“调用”函数来执行它的任务. swift统一函数的语法具有足够的灵活性来表达任何一个简单的不带参数的名称与本地和外部的每 ...
- jQuery源码解读-事件分析
最原始的事件注册 addEventListener方法大家应该都很熟悉,它是Html元素注册事件最原始的方法.先看下addEventListener方法签名: element.addEventList ...
- DirectShowLib directshownet 视频
using DirectShowLib; using System; using System.Collections; using System.Windows.Forms; namespace C ...
- 简单横道图Demo
代码(每个月都显示整月): @{ ViewBag.Title = "横道图"; Layout = "~/Views/Shared/_Layout.cshtml" ...
- 如何将CKeditor编辑器的上传和thinkphp结合
转载:THINKPHP代码中 lxepoo的文章 有时候我们会发现,我们需要对上传文件进行日志记录,这样的记录通常写在LOG文件或数据库中,那么,通过TP本身的上传类,可以实现的,但是只能做到编辑器外 ...
- 介绍开源的.net通信框架NetworkComms框架 源码分析(十四)StreamTools
原文网址: http://www.cnblogs.com/csdev Networkcomms 是一款C# 语言编写的TCP/UDP通信框架 作者是英国人 以前是收费的 目前作者已经开源 许可是 ...
- Mysql高并发优化
一.数据库结构的设计 1.数据行的长度不要超过8020字节,如果超过这个长度的话在物理页中这条数据会占用两行从而造成存储碎片,降低查询效率. 2.能够用数字类型的字段尽量选择数字类型而不用字符串类型的 ...