1、当用户触摸屏幕的时候,会产生许多手势,例如down,up,scroll,filing等等,我们知道View类有个View.OnTouchListener内部接口,通过重写他的onTouch(View v, MotionEvent event)方法,我们可以处理一些touch事件,但是这个方法太过简单,如果需要处理一些复杂的手势,用这个接口就会很麻烦(因为我们要自己根据用户触摸的轨迹去判断是什么手势)Android sdk给我们提供了GestureDetector(Gesture:手势Detector:识别)类,通过这个类我们可以识别很多的手势,主要是通过他的onTouchEvent(event)方法完成了不同手势的识别。虽然他能识别手势,但是不同的手势要怎么处理,应该是提供给程序员实现的,因此这个类对外提供了两个接口:OnGestureListener,OnDoubleTapListener,还有一个内部类SimpleOnGestureListener,SimpleOnGestureListener类是GestureDetector提供给我们的一个更方便的响应不同手势的类,这个类实现了上述两个接口(但是所有的方法体都是空的),该类是static class,也就是说它实际上是一个外部类。程序员可以在外部继承这个类,重写里面的手势处理方法。
通过GestureDetector的构造方法可以将SimpleOnGestureListener对象传递进去,这样GestureDetector能处理不同的手势了。
2.、具体用法:
001 | /** |
002 | * 当我们捕捉到Touch操作的时候,如何识别出用户的Gesture?这里我们需要GestureDetector.OnGestureListener接口的帮助 |
003 | * |
004 | * <a href="http://my.oschina.net/arthor" class="referer" target="_blank">@author</a> HB |
005 | */ |
006 | public class MySurfaceView extends SurfaceView implements OnGestureListener, |
007 | OnTouchListener, Callback { |
008 | GestureDetector gd; |
009 | Context context; |
010 | |
011 | public MySurfaceView(Context context) { |
012 | super (context); |
013 | this .context = context; |
014 | setFocusable( true ); |
015 | requestFocus(); |
016 | this .setLongClickable( true ); |
017 | this .setOnTouchListener( this ); |
018 | setFocusable( true ); |
019 | gd = new GestureDetector( new MySimpleGesture()); // 这里或者可以直接传递this参数,因为本类已经继承了OnGestureListener接口,也可以把new |
020 | // MySimpleGesture(),它继承了SimpleOnGestureListener类;,两种方法都可以,只是把两种方法归在一个里面,方便学习; |
021 | // 。实惠屏幕触控事件; |
022 | gd.setIsLongpressEnabled( true ); |
023 | // TODO Auto-generated constructor stub |
024 | } |
025 | |
026 | // 用户轻触触摸屏,由1个MotionEvent ACTION_DOWN触发 |
027 | @Override |
028 | public boolean onDown(MotionEvent e) { |
029 | // TODO Auto-generated method stub |
030 | System.out.println( "onDown" ); |
031 | return false ; |
032 | } |
033 | |
034 | /* |
035 | * 用户轻触触摸屏,尚未松开或拖动,由一个1个MotionEvent ACTION_DOWN触发 |
036 | * 注意和onDown()的区别,强调的是没有松开或者拖动的状态 (单击没有松开或者移动时候就触发此事件,再触发onLongPress事件) |
037 | */ |
038 | @Override |
039 | public void onShowPress(MotionEvent e) { |
040 | // TODO Auto-generated method stub |
041 | System.out.println("onShowPress"); |
042 | } |
043 | |
044 | // 用户(轻触触摸屏后)松开,由一个1个MotionEvent ACTION_UP触发 |
045 | @Override |
046 | public boolean onSingleTapUp(MotionEvent e) { |
047 | // TODO Auto-generated method stub |
048 | System.out.println("onSingleTopUp"); |
049 | return false; |
050 | } |
051 | |
052 | // 用户按下触摸屏,并拖动,由1个MotionEvent ACTION_DOWN, 多个ACTION_MOVE触发 |
053 | @Override |
054 | public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, |
055 | float distanceY) { |
056 | System.out.println("onScroll"); |
057 | // TODO Auto-generated method stub |
058 | return false; |
059 | } |
060 | |
061 | // 用户长按触摸屏,由多个MotionEvent ACTION_DOWN触发 |
062 | @Override |
063 | public void onLongPress(MotionEvent e) { |
064 | // TODO Auto-generated method stub |
065 | System.out.println("onLongPress"); |
066 | } |
067 | |
068 | /* |
069 | * 用户按下触摸屏、快速移动后松开,由1个MotionEvent ACTION_DOWN, 多个ACTION_MOVE, |
070 | * 1个ACTION_UP触发(non-Javadoc) |
071 | * Fling事件的处理代码:除了第一个触发Fling的ACTION_DOWN和最后一个ACTION_MOVE中包含的坐标等信息外 |
072 | * ,我们还可以根据用户在X轴或者Y轴上的移动速度作为条件 |
073 | * 比如下面的代码中我们就在用户移动超过100个像素,且X轴上每秒的移动速度大于200像素时才进行处理。 |
074 | * |
075 | * <a href="http://my.oschina.net/u/244147" class="referer" target="_blank">@see</a> android.view.GestureDetector.OnGestureListener#onFling(android.view. |
076 | * MotionEvent, android.view.MotionEvent, float, float) |
077 | * 这个例子中,tv.setLongClickable( true )是必须的,因为 |
078 | * 只有这样,view才能够处理不同于Tap(轻触)的hold(即ACTION_MOVE,或者多个ACTION_DOWN) |
079 | * ,我们同样可以通过layout定义中的android:longClickable来做到这一点 |
080 | */ |
081 | @Override |
082 | public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, |
083 | float velocityY) { |
084 | // TODO Auto-generated method stub |
085 | System.out.println("onFling"); |
086 | // 参数解释: |
087 | // e1:第1个ACTION_DOWN MotionEvent |
088 | // e2:最后一个ACTION_MOVE MotionEvent |
089 | // velocityX:X轴上的移动速度,像素/秒 |
090 | // velocityY:Y轴上的移动速度,像素/秒 |
091 | |
092 | // 触发条件 : |
093 | // X轴的坐标位移大于FLING_MIN_DISTANCE,且移动速度大于FLING_MIN_VELOCITY个像素/秒 |
094 | final int FLING_MIN_DISTANCE = 100, FLING_MIN_VELOCITY = 200; |
095 | if (e1.getX() - e2.getX() > FLING_MIN_DISTANCE |
096 | && Math.abs(velocityX) > FLING_MIN_VELOCITY) { |
097 | // Fling left |
098 | Log.i("MyGesture", "Fling left"); |
099 | Toast.makeText(context, "Fling Left", Toast.LENGTH_SHORT).show(); |
100 | } else if (e2.getX() - e1.getX() > FLING_MIN_DISTANCE |
101 | && Math.abs(velocityX) > FLING_MIN_VELOCITY) { |
102 | // Fling right |
103 | Log.i("MyGesture", "Fling right"); |
104 | Toast.makeText(context, "Fling Right", Toast.LENGTH_SHORT).show(); |
105 | } |
106 | return true; |
107 | } |
108 | |
109 | /** |
110 | * 当发行屏幕触控事件的时候,首先出发此方法,再通过此方法,监听具体的整件 |
111 | * 在onTouch()方法中,我们调用GestureDetector的onTouchEvent |
112 | * ()方法,将捕捉到的MotionEvent交给GestureDetector 来分析是否有合适的callback函数来处理用户的手势 |
113 | */ |
114 | @Override |
115 | public boolean onTouch(View v, MotionEvent event) { |
116 | // TODO Auto-generated method stub |
117 | System.out.println("onTouch"); |
118 | return gd.onTouchEvent(event); |
119 | } |
120 | |
121 | @Override |
122 | public void surfaceCreated(SurfaceHolder holder) { |
123 | // TODO Auto-generated method stub |
124 | |
125 | } |
126 | |
127 | @Override |
128 | public void surfaceChanged(SurfaceHolder holder, int format, int width, |
129 | int height) { |
130 | // TODO Auto-generated method stub |
131 | |
132 | } |
133 | |
134 | @Override |
135 | public void surfaceDestroyed(SurfaceHolder holder) { |
136 | // TODO Auto-generated method stub |
137 | |
138 | } |
139 | |
140 | /** |
141 | * SimpleOnGestureListener implements GestureDetector.OnDoubleTapListener, |
142 | * GestureDetector.OnGestureListener |
143 | */ |
144 | private class MySimpleGesture extends SimpleOnGestureListener { |
145 | // 双击的第二下Touch down时触发 |
146 | public boolean onDoubleTap(MotionEvent e) { |
147 | Log.i("MyGesture", "onDoubleTap"); |
148 | return super.onDoubleTap(e); |
149 | } |
150 | |
151 | // 双击的第二下Touch down和up都会触发,可用e.getAction()区分 |
152 | public boolean onDoubleTapEvent(MotionEvent e) { |
153 | Log.i("MyGesture", "onDoubleTapEvent"); |
154 | return super.onDoubleTapEvent(e); |
155 | } |
156 | |
157 | // Touch down时触发 |
158 | public boolean onDown(MotionEvent e) { |
159 | Log.i("MyGesture", "onDown"); |
160 | return super.onDown(e); |
161 | } |
162 | |
163 | // Touch了滑动一点距离后,up时触发 |
164 | public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, |
165 | float velocityY) { |
166 | Log.i("MyGesture", "onFling"); |
167 | return super.onFling(e1, e2, velocityX, velocityY); |
168 | } |
169 | |
170 | // Touch了不移动一直Touch down时触发 |
171 | public void onLongPress(MotionEvent e) { |
172 | Log.i("MyGesture", "onLongPress"); |
173 | super.onLongPress(e); |
174 | } |
175 | |
176 | // Touch了滑动时触发 |
177 | public boolean onScroll(MotionEvent e1, MotionEvent e2, |
178 | float distanceX, float distanceY) { |
179 | Log.i("MyGesture", "onScroll"); |
180 | return super.onScroll(e1, e2, distanceX, distanceY); |
181 | } |
182 | |
183 | /* |
184 | * Touch了还没有滑动时触发 (1)onDown只要Touch Down一定立刻触发 (2)Touch |
185 | * Down后过一会没有滑动先触发onShowPress再触发onLongPress So: Touch Down后一直不滑动,onDown |
186 | * -> onShowPress -> onLongPress这个顺序触发。 |
187 | */ |
188 | public void onShowPress(MotionEvent e) { |
189 | Log.i("MyGesture", "onShowPress"); |
190 | super.onShowPress(e); |
191 | } |
192 | |
193 | /* |
194 | * 两个函数都是在Touch Down后又没有滑动(onScroll),又没有长按(onLongPress),然后Touch Up时触发 |
195 | * 点击一下非常快的(不滑动)Touch Up: onDown->onSingleTapUp->onSingleTapConfirmed |
196 | * 点击一下稍微慢点的(不滑动)Touch Up://确认是单击事件触发 |
197 | * onDown->onShowPress->onSingleTapUp->onSingleTapConfirmed |
198 | */ |
199 | public boolean onSingleTapConfirmed(MotionEvent e) { |
200 | Log.i( "MyGesture" , "onSingleTapConfirmed" ); |
201 | return super .onSingleTapConfirmed(e); |
202 | } |
203 | |
204 | public boolean onSingleTapUp(MotionEvent e) { |
205 | Log.i( "MyGesture" , "onSingleTapUp" ); |
206 | return super .onSingleTapUp(e); |
207 | } |
208 | } |
209 | } |
结果:
1、当单击屏幕的时候,输出:
2、当单击等一会再松开时候输出(触触摸屏,比单击慢):
3、当用户按住不放的时候,输出:
4、当用户按住不放,拖动鼠标的时候输出:
5、当用户按住不放,快速拖动鼠标的时候:
6、当用户双击的时候(观察与单击的区别---onDubleTab在第二次按下Down的时候才出发,说明 是双击事件在onDown事件之前;):