在使用自定義視圖的時候,對觸屏事件的處理是比不可少的,有能力的能夠本身寫代碼處理,這樣更加的靈活。若是不想這麼麻煩,Android提供了一個手勢監聽類GestureDetector,能夠供咱們使用。GestureDetector使用很方便,提供了單擊,雙擊,長按等操做的處理,可是通常的定義界面都比較複雜,還用不少須要注意的地方,在這兒總結一下GestureDetector的使用。android
首先新建一個空白的工程,主界面的layout中只須要添加一個按鈕就行web
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
緩存
xmlns:tools="http://schemas.android.com/tools"
app
android:layout_width="match_parent"
框架
android:layout_height="match_parent"
ide
android:paddingBottom="@dimen/activity_vertical_margin"
函數
android:paddingLeft="@dimen/activity_horizontal_margin"
測試
android:paddingRight="@dimen/activity_horizontal_margin"
this
android:paddingTop="@dimen/activity_vertical_margin"
spa
tools:context=".MainActivity" >
<Button
android:id="@+id/btn_textgesture"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:text="@string/app_name" />
</RelativeLayout>
複製代碼
因爲要測試的觸屏事件,全部這個按鈕比較大,主界面爲以下效果: 首先介紹一下觸屏事件處理的基本思路。觸屏通常有三個基本事件,down按下,move移動,up離開,經過對這三個基本事件的監聽,判斷用戶執行了何種操做。一個標準的觸屏操做通常都是一系列基本事件的組合,在Android的框架中,經過onTouch()函數能夠獲取基本的觸屏事件,而像onClick這樣的函數,已是一系列基本事件的組合。好比,發生了Down事件,在up事件以前沒有發生move事件,或者move的範圍很小,而且down事件和up事件的間隔很短,這就是一個click或者singelTap事件,對比實體鍵盤按鍵的事件,實體鍵盤是在down事件發生後進行操做,而觸屏事件通常是up事件發生後進行操做。下面是activity的代碼
package com.example.testgesture;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.Button;
public class MainActivity extends Activity {
private Button mButton;
private GestureDetector mGestureDetector;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mGestureDetector = new GestureDetector(this, new MyOnGestureListener());
mButton = (Button) findViewById(R.id.btn_textgesture);
mButton.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.i(getClass().getName(), "onTouch-----" + getActionName(event.getAction()));
mGestureDetector.onTouchEvent(event);
// 必定要返回true,否則獲取不到完整的事件
return true;
}
});
}
private String getActionName(int action) {
String name = "";
switch (action) {
case MotionEvent.ACTION_DOWN: {
name = "ACTION_DOWN";
break;
}
case MotionEvent.ACTION_MOVE: {
name = "ACTION_MOVE";
break;
}
case MotionEvent.ACTION_UP: {
name = "ACTION_UP";
break;
}
default:
break;
}
return name;
}
class MyOnGestureListener extends SimpleOnGestureListener {
@Override
public boolean onSingleTapUp(MotionEvent e) {
Log.i(getClass().getName(), "onSingleTapUp-----" + getActionName(e.getAction()));
return false;
}
@Override
public void onLongPress(MotionEvent e) {
Log.i(getClass().getName(), "onLongPress-----" + getActionName(e.getAction()));
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
Log.i(getClass().getName(),
"onScroll-----" + getActionName(e2.getAction()) + ",(" + e1.getX() + "," + e1.getY() + ") ,("
+ e2.getX() + "," + e2.getY() + ")");
return false;
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
Log.i(getClass().getName(),
"onFling-----" + getActionName(e2.getAction()) + ",(" + e1.getX() + "," + e1.getY() + ") ,("
+ e2.getX() + "," + e2.getY() + ")");
return false;
}
@Override
public void onShowPress(MotionEvent e) {
Log.i(getClass().getName(), "onShowPress-----" + getActionName(e.getAction()));
}
@Override
public boolean onDown(MotionEvent e) {
Log.i(getClass().getName(), "onDown-----" + getActionName(e.getAction()));
return false;
}
@Override
public boolean onDoubleTap(MotionEvent e) {
Log.i(getClass().getName(), "onDoubleTap-----" + getActionName(e.getAction()));
return false;
}
@Override
public boolean onDoubleTapEvent(MotionEvent e) {
Log.i(getClass().getName(), "onDoubleTapEvent-----" + getActionName(e.getAction()));
return false;
}
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
Log.i(getClass().getName(), "onSingleTapConfirmed-----" + getActionName(e.getAction()));
return false;
}
}
}
複製代碼
首先是聲明一個GestureDetector,而後重寫Button的onTouch函數,將觸屏事件交給GestureDetector處理。 首先作一個對按鈕作一個單擊 onSingleTapUp被調用,說明發生了單擊事件,onSingleTapConfirmed被調用,說明確認發生了一個單擊事件,不是雙擊的事件。須要注意的是onSingleTapUp已是一click事件,onSingleTapUp觸發的時候是 ACTION_UP事件。onSingleTapConfirmed是在用戶手指 離開屏幕後觸發,全部up並非全部觸屏事件的結束。 作一個雙擊的操做 首先發生了一個onSingleTapUp,說明完成了一次單擊事件,而後發生了onDoubleTap,至此,一次雙擊事件已經完成。咱們能夠看到,onDoubleTap發生的時候是 ACTION_DOWN事件,也就是說雙擊事件是第二次按下屏幕的時候觸發,而不是第二次離開屏幕的時候觸發,在onDoubleTap發生以後,就能夠在onDoubleTapEvent中監聽到雙擊事件發生後從按下到彈起的全部觸屏事件。onDoubleTap發生後不會觸發onSingleTapUp和onSingleTapConfirmed。 作一個長按的操做 onLongPress實在 ACTION_DOWN時發生,onLongPress發生後在up以前不會用其餘事件觸發,能夠在 onShowPress處理狀態的改變,好比按鈕的按下狀態。 作一個滑動操做 onScroll事件是拖動,onFling是拋。結合log來了解一下。首先是ACTION_DOWN,以後屢次ACTION_MOVE,移動超過必定距離,觸發了onScroll,若是onScroll被觸發, 在up以前是不會有長按,單擊,雙擊等事件的。看一下onScroll的參數public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY)
複製代碼
e1爲第一次按下的事件,和onDown事件裏面的同樣,e2爲當前的事件,distanceX爲本次onScroll移動的X軸距離,distanceY爲移動的Y軸距離,移動的距離是相對於上一次onScroll事件的移動距離,而不是當前點和按下點的距離。此次滑動最後觸發了onFling事件,可是onFling事件的觸發不是必定的,onFling是在ACTION_UP觸發,平時列表在離開屏幕是繼續滾動,就是經過這種方式觸發。public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)
複製代碼
onFling的前兩個參數和onScroll相同,e2爲用戶拖動完離開屏幕時的點。veloctiyX,velocitY爲離開屏幕時的初始速度,以這兩個速度爲初始速度作勻減速運動,就是如今拖動列表和拖動圖片的各類緩存滾動的效果。 函數的返回值除了onLongPress,這些函數都是有返回值的,mButton.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.i(getClass().getName(), "onTouch-----" + getActionName(event.getAction()));
mGestureDetector.onTouchEvent(event);
// 必定要返回true,否則獲取不到完整的事件
return true;
}
});
複製代碼
這些返回值會經過 mGestureDetector.onTouchEvent(event); 傳遞給onTouch。