手机屏幕事件的处理方法onTouchEvent。该方法在View类中的定义,并且所有的View子类全部重写了该方法,应用程序可以通过该方法处理手机屏幕的触摸事件。该方法的签名如下所示。
public boolean onTouchEvent(MotionEvent event)
参数event:参数event为手机屏幕触摸事件封装类的对象,其中封装了该事件的所有信息,例如触摸的位置、触摸的类型以及触摸的时间等。该对象会在用户触摸手机屏幕时被创建。
返回值:该方法的返回值机理与键盘响应事件的相同,同样是当已经完整地处理了该事件且不希望其他回调方法再次处理时返回true,否则返回false。
该方法并不像之前介绍过的方法只处理一种事件,一般情况下以下三种情况的事件全部由onTouchEvent方法处理,只是三种情况中的动作值不同。
屏幕被按下:当屏幕被按下时,会自动调用该方法来处理事件,此时MotionEvent.getAction()的值为MotionEvent.ACTION_DOWN,如果在应用程序中需要处理屏幕被按下的事件,只需重新该回调方法,然后在方法中进行动作的判断即可。
屏幕被抬起:当触控笔离开屏幕时触发的事件,该事件同样需要onTouchEvent方法来捕捉,然后在方法中进行动作判断。当MotionEvent.getAction()的值为MotionEvent.ACTION_UP时,表示是屏幕被抬起的事件。
在屏幕中拖动:该方法还负责处理触控笔在屏幕上滑动的事件,同样是调用MotionEvent.getAction()方法来判断动作值是否为MotionEvent.ACTION_MOVE再进行处理。
Android Touch Screen 与传统Click Touch Screen不同,会有一些手势(Gesture),例如Fling,Scroll等等。这些Gesture会使用户体验大大提升。
Android中的Gesture识别(detector)是通过GestureDetector.OnGestureListener接口实现的。
首先,Android事件处理机制是基于Listener实现的,比如触摸屏相关的事件,就是通过onTouchListener实现;
其次,所有View的子类都可以通过setOnTouchListener()、setOnKeyListener()等方法来添加对某一类事件的Listener;
第三,Listener一般会以Interface的方式来提供,其中包含一个或多个abstract方法,我们需要实现这些方法来完成 onTouch()、onKey()等操作。这样,程序便可以在特定的事件被dispatch到该view的时候,通过callback函数给予适当的响 应。
1. Touch Screen Click举例
1 public class MyGesture extends Activity implements OnTouchListener { 2 public void onCreate(Bundle savedInstanceState) { 3 super.onCreate(savedInstanceState); 4 5 setContentView(R.layout.main); 6 7 TextView tv = (TextView) findViewById(R.id.tv); 8 9 tv.setOnTouchListener(this); 10 } 11 12 public boolean onTouch(View v, MotionEvent event) 13 14 { 15 Toast.makeText(this, "Touch Touch", Toast.LENGTH_SHORT).show(); 16 17 return false; 18 } 19 20 }
我们可以通过MotionEvent的getAction()方法来获取Touch事件的类型,包括 ACTION_DOWN(按下触摸屏), ACTION_MOVE(按下触摸屏后移动受力点), ACTION_UP(松开触摸屏)和ACTION_CANCEL(不会由用户直接触发)。借助对于用户不同操作的判断,结合getRawX()、 getRawY()、getX()和getY()等方法来获取坐标后,我们可以实现诸如拖动某一个按钮,拖动滚动条等功能。2. 当我们捕捉到Touch操作的时候,如何识别出用户的Gesture?这里我们需要GestureDetector.OnGestureListener接口的帮助,代码如下:
1 public class MyGesture extends Activity implements OnTouchListener, OnGestureListener 2 3 { 4 5 private GestureDetector mGestureDetector; 6 7 public MyGesture() 8 9 { 10 11 mGestureDetector = new GestureDetector(this); 12 13 } 14 15 public void onCreate(Bundle savedInstanceState) 16 17 { 18 19 super.onCreate(savedInstanceState); 20 21 setContentView(R.layout.main); 22 23 TextView tv = (TextView) findViewById(R.id.tv); 24 25 tv.setOnTouchListener(this); 26 27 tv.setFocusable(true); 28 29 tv.setClickable(true); 30 31 tv.setLongClickable(true); 32 33 mGestureDetector.setIsLongpressEnabled(true); 34 35 } 36 37 /* 38 * *在onTouch()方法中,我们调用GestureDetector的onTouchEvent()方法, 39 * 将捕捉到的MotionEvent交给GestureDetector * 来分析是否有合适的callback函数来处理用户的手势 40 */ 41 42 public boolean onTouch(View v, MotionEvent event) 43 44 { 45 46 return mGestureDetector.onTouchEvent(event); 47 48 } 49 50 // 用户轻触触摸屏,由1个MotionEvent ACTION_DOWN触发 51 52 public boolean onDown(MotionEvent arg0) 53 54 { 55 56 Log.i("MyGesture", "onDown"); 57 58 Toast.makeText(this, "onDown", Toast.LENGTH_SHORT).show(); 59 60 return true; 61 62 } 63 64 /* 65 * * 用户轻触触摸屏,尚未松开或拖动,由一个1个MotionEvent ACTION_DOWN触发 * 66 * 注意和onDown()的区别,强调的是没有松开或者拖动的状态 67 */ 68 69 public void onShowPress(MotionEvent e) 70 71 { 72 73 Log.i("MyGesture", "onShowPress"); 74 75 Toast.makeText(this, "onShowPress", Toast.LENGTH_SHORT).show(); 76 77 } 78 79 // 用户(轻触触摸屏后)松开,由一个1个MotionEvent ACTION_UP触发 80 81 public boolean onSingleTapUp(MotionEvent e) 82 83 { 84 85 Log.i("MyGesture", "onSingleTapUp"); 86 87 Toast.makeText(this, "onSingleTapUp", Toast.LENGTH_SHORT).show(); 88 89 return true; 90 91 } 92 93 // 用户按下触摸屏、快速移动后松开,由1个MotionEvent ACTION_DOWN, 多个ACTION_MOVE, 1个ACTION_UP触发 94 95 public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) 96 97 { 98 99 Log.i("MyGesture", "onFling"); 100 101 Toast.makeText(this, "onFling", Toast.LENGTH_LONG).show(); 102 103 return true; 104 105 } 106 107 // 用户按下触摸屏,并拖动,由1个MotionEvent ACTION_DOWN, 多个ACTION_MOVE触发 108 109 public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) 110 111 { 112 113 Log.i("MyGesture", "onScroll"); 114 115 Toast.makeText(this, "onScroll", Toast.LENGTH_LONG).show(); 116 117 return true; 118 119 } 120 121 // 用户长按触摸屏,由多个MotionEvent ACTION_DOWN触发 122 123 public void onLongPress(MotionEvent e) 124 125 { 126 127 Log.i("MyGesture", "onLongPress"); 128 129 Toast.makeText(this, "onLongPress", Toast.LENGTH_LONG).show(); 130 131 } 132 133 }
3. Fling事件的处理代码:除了第一个触发Fling的ACTION_DOWN和最后一个ACTION_MOVE中包含的坐标等信息外,我们还可以根据用 户在X轴或者Y轴上的移动速度作为条件。比如下面的代码中我们就在用户移动超过100个像素,且X轴上每秒的移动速度大于200像素时才进行处理。
1 public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, 2 float velocityY) 3 4 { 5 6 // 参数解释: 7 8 // e1:第1个ACTION_DOWN MotionEvent 9 10 // e2:最后一个ACTION_MOVE MotionEvent 11 12 // velocityX:X轴上的移动速度,像素/秒 13 14 // velocityY:Y轴上的移动速度,像素/秒 15 16 // 触发条件 : 17 18 // X轴的坐标位移大于FLING_MIN_DISTANCE,且移动速度大于FLING_MIN_VELOCITY个像素/秒 19 20 final int FLING_MIN_DISTANCE = 100, FLING_MIN_VELOCITY = 200; 21 22 if (e1.getX() - e2.getX() > FLING_MIN_DISTANCE 23 && Math.abs(velocityX) > FLING_MIN_VELOCITY) 24 25 { 26 27 // Fling left 28 29 Log.i("MyGesture", "Fling left"); 30 31 Toast.makeText(this, "Fling Left", Toast.LENGTH_SHORT).show(); 32 33 } 34 35 else if (e2.getX() - e1.getX() > FLING_MIN_DISTANCE 36 && Math.abs(velocityX) > FLING_MIN_VELOCITY) 37 38 { 39 40 // Fling right 41 42 Log.i("MyGesture", "Fling right"); 43 44 Toast.makeText(this, "Fling Right", Toast.LENGTH_SHORT).show(); 45 46 } 47 48 return false; 49 50 }
这个例子中,tv.setLongClickable(true)是必须的,因为 只有这样,view才能够处理不同于Tap(轻触)的hold(即ACTION_MOVE,或者多个ACTION_DOWN),我们同样可以通过layout定义中的android:longClickable来做到这一点.