【摘】捕获触摸事件
重写onTouchEvent()回调函数
public class MainActivity extends Activity { @Override public 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<TouchEvent>
型函数,其中,这些函数都返回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<Value>
系列函数,来获得事件之前的位置、尺寸、时间以及按压力(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; @Override public 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; }
相关推荐
在Android平台上,实现类似有道云笔记的原笔迹手写功能是一项技术挑战,涉及到图形绘制、触摸事件处理以及手势识别等多个方面的知识。这个例子源码提供了一个完整的解决方案,可以帮助开发者理解和实现这样的功能。 ...
4. **事件处理**:学习Android中的触摸事件、键盘事件以及各种控件的监听事件,如OnClickListener、OnCheckedChangeListener等,以及如何通过Handler和Runnable实现异步消息处理。 5. **数据存储**:了解SQLite...
开发者可以通过重写`Layer`或`Scene`的触摸事件回调函数来捕获和处理这些事件。 一、单点触摸事件处理 1. `onTouchesBegan`: 当用户首次触摸屏幕时,这个回调会被调用。参数是一个`std::vector*>`类型的数组,包含...
7. **触摸事件处理(Touch Events)**:Android通过MotionEvent对象处理触摸事件,包括ACTION_DOWN、ACTION_UP、ACTION_MOVE等。学习如何在活动中捕获并处理这些事件,可以实现更丰富的用户交互。 8. **权限管理...
用户在屏幕上触控时,应用需要捕获触摸事件,并通过`Canvas`将轨迹绘制出来。 3. **触摸事件处理**:Android的`MotionEvent`类用于处理触摸屏事件,包括ACTION_DOWN(按下)、ACTION_MOVE(移动)和ACTION_UP(抬起...
在Android中,触摸事件(MotionEvent)是通过View或者Activity来捕获的。当用户在屏幕上滑动手指时,系统会生成一系列ACTION_DOWN、ACTION_MOVE和ACTION_UP等事件,开发者可以通过重写`onTouchEvent()`方法来处理...
本学习笔记将深入探讨cocos2d-x中的触摸事件处理机制,以及如何在项目中有效地利用它们。 首先,cocos2d-x是一个跨平台的2D游戏开发框架,它支持多种操作系统,包括iOS、Android、Windows等。在这些平台上,触摸...
其中包括了自定义View的绘制流程,以及与触摸事件、手势识别、视图滑动等相关的处理。 2.8 进程 Android应用是由一个或多个进程构成的,进程的生命周期和多进程的管理都是重要的知识点。进程存活受系统资源分配影响...
- **--pct-touch**:设置触摸事件的比例。 - **--pct-motion**:设置运动事件的比例。 - **--pct-trackball**:设置轨迹球事件的比例。 - **--pct-syskeys**:设置系统按键事件的比例。 - **--pct-appswitch**:设置...
总的来说,仿印象笔记抽屉菜单效果的实现涉及了Android的基础知识,包括自定义视图、触摸事件处理、动画以及界面交互设计。这个过程不仅可以提升你的编程技能,也能帮助你理解如何在实际项目中构建动态、交互性强的...
为了实现这样的功能,我们需要继承`View`类或`SurfaceView`类,并重写其`onTouchEvent`方法来捕获用户的触摸事件,以及`onDraw`方法来进行实际的绘图操作。 在`onTouchEvent`方法中,我们需要记录下每次触摸屏幕时...
2. **触摸事件处理**:监听用户的触摸事件,当用户在屏幕边缘滑动时,捕获这些事件并进行相应的响应。可以通过重写`onTouchEvent()`方法实现。 3. **动画效果**:为了使侧滑菜单的展开和收起更加平滑,可以使用`...
总的来说,通过QT开发的无触摸屏系统按键解决方案,不仅能够锻炼开发者对QT绘图和事件处理的理解,还能够展示如何在嵌入式系统中实现高效的人机交互。这在工程实践中具有很高的实用价值,特别是在那些物理按键或非...
通过研究这个"android 便签 手写签到demo",开发者可以学习到如何处理触摸事件、如何绘制和保存手写路径,以及如何将这些功能整合到实际应用中。这个项目对于初学者来说是一份很好的实践教程,对于有经验的开发者来...
- **触摸事件**:包括点击、滑动、长按等。 - **按键事件**:如物理按键的按下、抬起等。 - **事件分发机制**:主要包括事件捕获、事件目标、事件响应三个阶段。 - **事件监听器**:通过为View设置监听器(如 `...
一旦QEMU捕获到鼠标事件,这些事件会被转换为Android的MotionEvent,并由InputDispatcher分发到相应的应用程序或服务。 在具体实现时,可能会遇到一些问题,比如在Emulator中,系统可能默认模拟电池状态为无电,...
3. **屏幕截图(Screenshots)**:可以捕获设备当前显示的视图,这对于对比测试结果和预期画面非常有用。 Calabash与Selenium WebDriver类似,但两者的主要区别在于,与桌面电脑上的Web应用交互相比,通过触摸屏与...
学习如何添加监听器,捕获用户在屏幕上的触摸、按钮点击等事件,并编写相应的处理逻辑。此外,还要学习使用Adapter将数据绑定到ListView、RecyclerView等可滚动视图,实现数据的展示。 了解了基本的编程概念后,你...
- `MotionEvent`是Android中处理触摸事件的类。在`DrawView`中,你需要重写`onTouchEvent()`方法来捕获用户的触摸动作,如ACTION_DOWN、ACTION_MOVE和ACTION_UP,这些动作对应于手指触摸屏幕、移动和离开屏幕。 3....