【摘】捕获触摸事件

 

重写onTouchEvent()回调函数

 

 

public class MainActivity extends Activity {@Overridepublic boolean onTouchEvent(MotionEvent event){        int action = MotionEventCompat.getActionMasked(event);        switch(action) {                case (MotionEvent.ACTION_DOWN) :                Log.d(DEBUG_TAG,"Action was DOWN");                return true;        case (MotionEvent.ACTION_MOVE) :                Log.d(DEBUG_TAG,"Action was MOVE");                return true;        case (MotionEvent.ACTION_UP) :                Log.d(DEBUG_TAG,"Action was UP");                return true;        case (MotionEvent.ACTION_CANCEL) :                Log.d(DEBUG_TAG,"Action was CANCEL");                return true;        case (MotionEvent.ACTION_OUTSIDE) :                Log.d(DEBUG_TAG,"Movement occurred outside bounds " +                        "of current screen element");                return true;        default :                return super.onTouchEvent(event);        }}

 

 

然后,我们可以对这些事件做些自己的处理,以判断某个手势是否出现了。这种是针对自定义手势,我们所需要进行的处理。然而,如果我们的app仅仅需要一些常见的手势,如双击,长按,快速滑动(fling)等,那么我们可以使用GestureDetector类来完成。

 

 

 

public class MainActivity extends Activity implements        GestureDetector.OnGestureListener,        GestureDetector.OnDoubleTapListener{    private static final String DEBUG_TAG = "Gestures";    private GestureDetectorCompat mDetector;    // Called when the activity is first created.    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        // Instantiate the gesture detector with the        // application context and an implementation of        // GestureDetector.OnGestureListener        mDetector = new GestureDetectorCompat(this,this);        // Set the gesture detector as the double tap        // listener.        mDetector.setOnDoubleTapListener(this);    }    @Override    public boolean onTouchEvent(MotionEvent event){        this.mDetector.onTouchEvent(event);        // Be sure to call the superclass implementation        return super.onTouchEvent(event);    }    @Override    public boolean onDown(MotionEvent event) {        Log.d(DEBUG_TAG,"onDown: " + event.toString());        return true;    }    @Override    public boolean onFling(MotionEvent event1, MotionEvent event2,            float velocityX, float velocityY) {        Log.d(DEBUG_TAG, "onFling: " + event1.toString()+event2.toString());        return true;    }    @Override    public void onLongPress(MotionEvent event) {        Log.d(DEBUG_TAG, "onLongPress: " + event.toString());    }    @Override    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,            float distanceY) {        Log.d(DEBUG_TAG, "onScroll: " + e1.toString()+e2.toString());        return true;    }    @Override    public void onShowPress(MotionEvent event) {        Log.d(DEBUG_TAG, "onShowPress: " + event.toString());    }    @Override    public boolean onSingleTapUp(MotionEvent event) {        Log.d(DEBUG_TAG, "onSingleTapUp: " + event.toString());        return true;    }    @Override    public boolean onDoubleTap(MotionEvent event) {        Log.d(DEBUG_TAG, "onDoubleTap: " + event.toString());        return true;    }    @Override    public boolean onDoubleTapEvent(MotionEvent event) {        Log.d(DEBUG_TAG, "onDoubleTapEvent: " + event.toString());        return true;    }    @Override    public boolean onSingleTapConfirmed(MotionEvent event) {        Log.d(DEBUG_TAG, "onSingleTapConfirmed: " + event.toString());        return true;    }}

 

如果我们只想处理几种手势,那么可以选择继承 GestureDetector.SimpleOnGestureListener 类,而不是实现GestureDetector.OnGestureListener 接口。

GestureDetector.SimpleOnGestureListener 类实现了所有的 on 型函数,其中,这些函数都返回false。因此,我们可以仅仅重写我们需要的函数。比如,下面的代码段中,创建了一个继承自GestureDetector.SimpleOnGestureListener 的类,并重写了 onFling() 和 onDown() 函数。

无论我们是否使用GestureDetector.OnGestureListener类,最好都实现 onDown() 函数并且返回 true。这是因为所有的手势都是由 onDown() 消息开始的。如果让 onDown() 函数返回 false,就像GestureDetector.SimpleOnGestureListener类中默认实现的那样,系统会假定我们想忽略剩余的手势,GestureDetector.OnGestureListener中的其他函数也就永远不会被调用。这可能会导致我们的app出现意想不到的问题。仅仅当我们真的想忽略全部手势时,我们才应该让 onDown() 函数返回 false

 

public class MainActivity extends Activity {    private GestureDetectorCompat mDetector;    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mDetector = new GestureDetectorCompat(this, new MyGestureListener());    }    @Override    public boolean onTouchEvent(MotionEvent event){        this.mDetector.onTouchEvent(event);        return super.onTouchEvent(event);    }    class MyGestureListener extends GestureDetector.SimpleOnGestureListener {        private static final String DEBUG_TAG = "Gestures";        @Override        public boolean onDown(MotionEvent event) {            Log.d(DEBUG_TAG,"onDown: " + event.toString());            return true;        }        @Override        public boolean onFling(MotionEvent event1, MotionEvent event2,                float velocityX, float velocityY) {            Log.d(DEBUG_TAG, "onFling: " + event1.toString()+event2.toString());            return true;        }    }}

 

根据应用的需求,有多种追踪手势移动的方式可以选择。比如:

  • 追踪手指的起始和终止位置(比如,把屏幕上的对象从A点移动到B点)
  • 根据x、y轴坐标,追踪手指移动的方向。
  • 追踪历史状态。我们可以通过调用MotionEvent的getHistorySize()方法,来获得一个手势的历史尺寸。我们可以通过移动事件的getHistorical系列函数,来获得事件之前的位置、尺寸、时间以及按压力(pressures)。当我们需要绘制用户手指痕迹时,历史状态非常有用,比如触摸绘图。查看MotionEvent来了解更多细节。
  • 追踪手指在触摸屏上滑过的速度。Android提供了VelocityTracker类以及Support Library中的VelocityTrackerCompat类。VelocityTracker类可以帮助我们追踪触摸事件中的速度因素。
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;    }}

 

 

 

追踪多点

当多个手指同时触摸屏幕时,系统会产生如下的触摸事件:

  • ACTION_DOWN - 针对触摸屏幕的第一个点。此事件是手势的开端。第一触摸点的数据在MotionEvent中的索引总是0。
  • ACTION_POINTER_DOWN - 针对第一点后,出现在屏幕上额外的点。这个点的数据在MotionEvent中的索引,可以通过getActionIndex()获得。
  • ACTION_MOVE - 在按下手势期间发生变化。
  • ACTION_POINTER_UP - 当非主要点(non-primary pointer)离开屏幕时,发送此事件。
  • ACTION_UP - 当最后一点离开屏幕时发送此事件。

 

拖拽一个对象

对于触摸手势来说,一个很常见的操作是在屏幕上拖拽一个对象。接下来的代码段让用户可以拖拽屏幕上的图片。需要注意以下几点:

  • 拖拽操作时,即使有额外的手指放置到屏幕上了,app也必须保持对最初的点(手指)的追踪。比如,想象在拖拽图片时,用户放置了第二根手指在屏幕上,并且抬起了第一根手指。如果我们的app只是单独地追踪每个点,它会把第二个点当做默认的点,并且把图片移到该点的位置。
  • 为了防止这种情况发生,我们的app需要区分初始点以及随后任意的触摸点。要做到这一点,它需要追踪处理多触摸手势章节中提到过的 ACTION_POINTER_DOWN  ACTION_POINTER_UP 事件。每当第二根手指按下或拿起时,ACTION_POINTER_DOWN  ACTION_POINTER_UP 事件就会传递给onTouchEvent()回调函数。
  • 当ACTION_POINTER_UP事件发生时,示例程序会移除对该点的索引值的引用,确保操作中的点的ID(the active pointer ID)不会引用已经不在触摸屏上的触摸点。这种情况下,app会选择另一个触摸点来作为操作中(active)的点,并保存它当前的x、y值。由于在ACTION_MOVE事件时,这个保存的位置会被用来计算屏幕上的对象将要移动的距离,所以app会始终根据正确的触摸点来计算移动的距离。

 

 

// The ‘active pointer’ is the one currently moving our object.private int mActivePointerId = INVALID_POINTER_ID;@Overridepublic boolean onTouchEvent(MotionEvent ev) {    // Let the ScaleGestureDetector inspect all events.    mScaleDetector.onTouchEvent(ev);    final int action = MotionEventCompat.getActionMasked(ev);    switch (action) {    case MotionEvent.ACTION_DOWN: {        final int pointerIndex = MotionEventCompat.getActionIndex(ev);        final float x = MotionEventCompat.getX(ev, pointerIndex);        final float y = MotionEventCompat.getY(ev, pointerIndex);        // Remember where we started (for dragging)        mLastTouchX = x;        mLastTouchY = y;        // Save the ID of this pointer (for dragging)        mActivePointerId = MotionEventCompat.getPointerId(ev, 0);        break;    }    case MotionEvent.ACTION_MOVE: {        // Find the index of the active pointer and fetch its position        final int pointerIndex =                MotionEventCompat.findPointerIndex(ev, mActivePointerId);        final float x = MotionEventCompat.getX(ev, pointerIndex);        final float y = MotionEventCompat.getY(ev, pointerIndex);        // Calculate the distance moved        final float dx = x - mLastTouchX;        final float dy = y - mLastTouchY;        mPosX += dx;        mPosY += dy;        invalidate();        // Remember this touch position for the next move event        mLastTouchX = x;        mLastTouchY = y;        break;    }    case MotionEvent.ACTION_UP: {        mActivePointerId = INVALID_POINTER_ID;        break;    }    case MotionEvent.ACTION_CANCEL: {        mActivePointerId = INVALID_POINTER_ID;        break;    }    case MotionEvent.ACTION_POINTER_UP: {        final int pointerIndex = MotionEventCompat.getActionIndex(ev);        final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);        if (pointerId == mActivePointerId) {            // This was our active pointer going up. Choose a new            // active pointer and adjust accordingly.            final int newPointerIndex = pointerIndex == 0 ? 1 : 0;            mLastTouchX = MotionEventCompat.getX(ev, newPointerIndex);            mLastTouchY = MotionEventCompat.getY(ev, newPointerIndex);            mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);        }        break;    }    }    return true;}

 

更多相关文章

  1. android 同一个TextView不同文字的点击事件
  2. Android事件传递机制
  3. Android studio实现按钮的点击事件
  4. Android中点击事件之FocusChangedListener实现步骤
  5. Android EditText获取焦点事件
  6. Android view手势缩放与移动

随机推荐

  1. Android连接上google
  2. android sdk manager安装SDK出现错误解决
  3. 简单学习Android(安卓)TextView
  4. android第二天课程
  5. 2018年Android版本分布(市场占有率、市场
  6. Android的UI组件复选框控件CheckBox
  7. app的好用框架
  8. NDK版本与Android固件要求对应表
  9. Android面试系列文章2018之Android部分As
  10. Android自定义View(自定义控件)