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

别裁伪体亲风雅,转益多师是汝师。这篇文章主要讲述Android 触摸手势基础 官方文档概览2相关的知识,希望能为你提供帮助。
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; @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; } }; }

   
 
根据官网上说:
关于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; } }

   
滚动手势如果一个标准的布局有可能会超出它的容器的边界,可以把它嵌套在一个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:
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
【Android 触摸手势基础 官方文档概览2】http://developer.android.com/training/gestures/index.html

    推荐阅读