Android 触摸手势基础 官方文档概览

触摸手势检测基础

  手势检测一般包含两个阶段:

  1.获取touch事件数据

  2.解析这些数据,看它们是否满足你的应用所支持的某种手势。

  相关API:

  MotionEvent

  兼容版的:

  MotionEventCompat 

  (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;@Overrideprotected 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);}@Overridepublic boolean onTouchEvent(MotionEvent event) {// 在onTouchEvent方法中将事件传递给手势检测对象,否则手势监听对象中的回调函数是不会被调用的mGestureDetector.onTouchEvent(event);return super.onTouchEvent(event);}private OnGestureListener mOnGestureListener = new OnGestureListener() {@Overridepublic boolean onSingleTapUp(MotionEvent e) {Log.i(LOG_TAG, "onSingleTapUp: " + e.toString());mGestureTextView.setText("onSingleTapUp: ");return false;}@Overridepublic void onShowPress(MotionEvent e) {Log.i(LOG_TAG, "onShowPress: " + e.toString());mGestureTextView.setText("onShowPress: ");}@Overridepublic boolean onScroll(MotionEvent e1, MotionEvent e2,float distanceX, float distanceY) {Log.i(LOG_TAG, "onScroll: " + e1.toString() + ", " + e2.toString());mGestureTextView.setText("onScroll ");return false;}@Overridepublic void onLongPress(MotionEvent e) {Log.i(LOG_TAG, "onLongPress: " + e.toString());mGestureTextView.setText("onLongPress: ");}@Overridepublic boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,float velocityY) {Log.i(LOG_TAG, "onFling: " + e1.toString() + ", " + e2.toString());mGestureTextView.setText("onFling ");return false;}@Overridepublic boolean onDown(MotionEvent e) {Log.i(LOG_TAG, "onDown: " + e.toString());mGestureTextView.setText("onDown: ");return false;}};private OnDoubleTapListener mDoubleTapListener = new OnDoubleTapListener() {@Overridepublic boolean onSingleTapConfirmed(MotionEvent e) {Log.i("LOG_TAG", "onSingleTapConfirmed: " + e.toString());mDoubleTapTextView.setText("onSingleTapConfirmed: ");return false;}@Overridepublic boolean onDoubleTapEvent(MotionEvent e) {Log.i("LOG_TAG", "onDoubleTapEvent: " + e.toString());mDoubleTapTextView.setText("onDoubleTapEvent: ");return false;}@Overridepublic boolean onDoubleTap(MotionEvent e) {Log.i("LOG_TAG", "onDoubleTap: " + e.toString());mDoubleTapTextView.setText("onDoubleTap: ");return false;}};
}

 

 

  根据官网上说:

  关于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;@Overridepublic 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;}
}

 

滚动手势

  如果一个标准的布局有可能会超出它的容器的边界,可以把它嵌套在一个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 the MotionEvent.
  • ACTION_POINTER_DOWN—For extra pointers that enter the screen beyond the first. The pointer data for this pointer is at the index returned by getActionIndex().
  • 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:

  IndexMotionEvent会把每一个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 IDmActivePointerId = event.getPointerId(0);// ... Many touch events later...// Use the pointer ID to find the index of the active pointer // and fetch its positionint pointerIndex = event.findPointerIndex(mActivePointerId);// Get the pointer's current positionfloat 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

转载于:https://www.cnblogs.com/Free-Thinker/p/5504125.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/418507.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端学习(1934)vue之电商管理系统电商系统之分析实现默认勾选权限

目录结构 router.js import Vue from vue import Router from vue-router import Login from ./components/Login.vue import Home from ./components/Home.vue import Welcome from ./components/Welcome.vue import Users from ./components/user/Users.vue import Right fr…

C 高级编程 1

内存管理层次:硬件层次:内存结构管理内核算层次:内存映射堆扩展数据结构层次:智能指针:stl :在多线程,共享内存有问题SGI公司实现了STL ,开发了OPENGL库 语言层次:C:mallocc:newmalloc…

JS - JSON对象与JSON字符串相互转换的几种方法

https://blog.csdn.net/yup1212/article/details/80102050

前端学习(1935)vue之电商管理系统电商系统之实现权限的默认勾选功能

目录结构 router.js import Vue from vue import Router from vue-router import Login from ./components/Login.vue import Home from ./components/Home.vue import Welcome from ./components/Welcome.vue import Users from ./components/user/Users.vue import Right fr…

android开发之 SQLite(数据库)

SQLite数据库存储:SQLite是一款轻量级的关系型数据库,它的运算速度非常快, 占用资源很少,通常只需要几百 K的内存就足够了,因而特别适合在移动设备上使用。 第一: 创建一个数据库。(Android为了…

mysql查询重复数据

https://www.cnblogs.com/LDDXFS/p/9867928.html

前端学习(1936)vue之电商管理系统电商系统之再关闭对话框defkeys

目录结构 router.js import Vue from vue import Router from vue-router import Login from ./components/Login.vue import Home from ./components/Home.vue import Welcome from ./components/Welcome.vue import Users from ./components/user/Users.vue import Right fr…

[转]unity3D游戏开发之GUI

转自:http://blog.csdn.net/kuloveyouwei/article/details/23598171 GUI在游戏的开发中占有重要的地位,游戏的GUI是否友好,使用是否方便,很大程度上决定了玩家的游戏体验。Unity内置了一套完整地GUI系 统,提供了从布局…

swagger2如何测试单个文件或者多文件上传

http://www.pianshen.com/article/9445265955/

JS 构造图片Image对象

var imagenew Image(); image.src""; console.log(image.width); 转载于:https://www.cnblogs.com/danlis/p/5508354.html

前端学习(1937)vue之电商管理系统电商系统之渲染分配角色的对话框并请求数据

目录结构 router.js import Vue from vue import Router from vue-router import Login from ./components/Login.vue import Home from ./components/Home.vue import Welcome from ./components/Welcome.vue import Users from ./components/user/Users.vue import Right fr…

使用JWT实现单点登录(完全跨域方案)

https://blog.csdn.net/weixin_42873937/article/details/82460997

Javascript 获取和设置日期

关于JavaScript中日期对象使用方法: 一般有两种设置时间的方式: 一种是直接指定,如:date.setHours(13);    //指定date的时间为下午1点。 第二种是先获取时间进行计算,然后再进行设置,如: d…

前端学习(1938)vue之电商管理系统电商系统之下拉框环境

目录结构 router.js import Vue from vue import Router from vue-router import Login from ./components/Login.vue import Home from ./components/Home.vue import Welcome from ./components/Welcome.vue import Users from ./components/user/Users.vue import Right fr…

cookie和session的区别和用法

https://www.cnblogs.com/wangzhongqiu/p/8970935.html

javascript判断图片是否加载完成方法整理

有时候我们在前端开发工作中为了获取图片的信息,需要在图片加载完成后才可以正确的获取到图片的大小尺寸,并且执行相应的回调函数使图片产生某种显示效果。本文主要整理了几种常见的javascipt判断图片加载完成时的方法,并通过代码与实际应用相…

前端学习(1939)vue之电商管理系统电商系统之完成全部功能

目录结构 router.js import Vue from vue import Router from vue-router import Login from ./components/Login.vue import Home from ./components/Home.vue import Welcome from ./components/Welcome.vue import Users from ./components/user/Users.vue import Right fr…

response 中OutputStream和PrintWriter区别

https://blog.csdn.net/feipeng8848/article/details/56286399 https://blog.csdn.net/lhanson/article/details/83893999 https://blog.csdn.net/qq_16605855/article/details/78260355 public class RenderUtil {/*** 渲染json对象*/public static void renderJson(HttpSe…

为PHP7安装Windows Server 2012 R2过程记录

因为要安装php-7.0.6-Win32-VC14-x64,需要先安装vcredist2015_x64_14.0.23026.0。 之前安装了Windows Server 2012 R2后,一直无法成功安装vcredist2015_x64_14.0.23026.0。 据说是因为没有打几个补丁。但是一直都打不上,索性直接安装了个带补…