Tìm hiểu về GestureDetector trong Android

1. Giới thiệu

Một "Touch Gestures" được xảy ra khi người dùng đặt 1 hay nhiều ngón tay lên màn hình cảm ứng và các ứng dụng giải thích rằng mô hình của Touch Gestures là một sự kiện đặc biệt. Nó tương ứng với mới 2 giai đoạn nhận diện Gestures :

  • Thu thập dữ liệu về các sự kiện chạm (touches)
  • Phân tích dữ liệu để xem nếu nó đáp ứng các tiêu chí về bất cứ cử chỉ nào mà ứng dụng của bạn hỗ trợ.

2. Các thư viện hỗ trợ

Tại bài viết này tôi sử dụng 2 class GestureDetectorCompatMotionEventCompat. Bạn nên sử dụng các class ở Thư viện hỗ trợ của Android. Các class ở trong Support Library được cung cấp để có thể hỗ trợ tương thích với các phiên bản android từ 1.6 trở lên. Chú ý rằng class MotionEventCompat không thay thế cho class MonitonEvent mà nó chỉ cung cấp thêm các tiện ích mà class MonitonEvent không có.

3. Thu thập dữ liệu về các sự kiện chạm

Khi người dùng đặt một hay nhiều ngón tay lên màn hình thì trên các View sẽ được nhận về 1 callback có tên là onTouchEvent. Đối với mỗi sự kiện chạm vào màn hình (vị trí, thời gian, lực ép, bổ sung thêm ngón tay khác mà cuối cùng sẽ xác định được cử chỉ (gesture) của người dùng. Callback onTouchEvent sẽ được bắn nhiều lần.

Hành động đầu tiên mà người dùng tương tác được đó là chạm vào màn hình. Sau đó hệ thống sẽ theo dõi vị trí các ngón tay của người sử dụng khi chạm vào màn hình và kết thúc bắng cách bắt sự kiến cuối cùng của ngón tay người sử dụng rời khỏi màn hình. Trong suốt các sự tương tác này các MonitonEvent chuyển giao cho onTouchEvent cung cấp mọi chi tiết tương tác. Ứng dụng của bạn có thể sử dụng các dữ liệu được cung cấp bởi các MotionEvent để xác định xem một cử chỉ quan tâm đến nó đã xảy ra.

4. Bắt sự kiện touch tại Activity hoặc View

1. Bắt sự kiện touch tại Activity

Để bắt sự kiện touch trong Activity hoặc View, bạn có thể ghi đè ghi đè lên callback onTouchEvent ().

Đoạn code dưới đấy sử dụng hàm getActionMasked () để trích xuất các hành động của người sử dụng thực hiện từ các tham số sự kiện. Điều này cung cấp cho bạn các dữ liệu thô mà bạn cần phải xác định một cử chỉ mà bạn quan tâm đã xảy ra:

public class MainActivity extends Activity {
...
// This example shows an Activity, but you would use the same approach if
// you were subclassing a View.
@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);
    }
}

Sau khi bắt callback onTouchEvent như trên bạn đã có thể customize được sự kiện của riêng bạn mong muốn khi sự kiện này nó sảy ra.Tuy nhiên, nếu ứng dụng của bạn sử dụng cử chỉ thông thường như double tap, long press, fling, và như vậy, bạn có thể tận dụng lợi thế của class GestureDetector. GestureDetector được tạo ra để hỗ trợ bạn dễ dàng phát hiện các thao tác phổ biến mà không cần xử lý các sự kiện chạm cá nhân cho mình. Điều này sẽ được thảo luận dưới đây trong phần Phát hiện Gesture.

2. Bắt sự kiện touch tại một View

Để thay thế cho onTouchEvent mặc định của một view, bạn có thể set một đối tượng View.OnTouchListener cho bất kỳ đối tượng View nào thông qua hàm setOnTouchListener.Ví dụ :

View myView = findViewById(R.id.my_view);
myView.setOnTouchListener(new OnTouchListener() {
    public boolean onTouch(View v, MotionEvent event) {
        // ... Respond to touch events
        return true;
    }
});

Hãy coi chừng của việc tạo ra một listener mà trả về false cho sự kiện ACTION_DOWN. Nếu bạn làm điều này, người nghe sẽ không được gọi cho ACTION_MOVE tiếp theo và ACTION_UP chuỗi các sự kiện. Điều này là do ACTION_DOWN là điểm khởi đầu cho tất cả các sự kiện chạm.

Nếu bạn đang tạo ra một Custom View, bạn có thể ghi đè lên onTouchEvent (), như mô tả ở trên.

5. Phát hiện Gesture

Android cung cấp class GestureDetector để detect một số hành động thông thường. Một trong số các cử chỉ mà nó hỗ trợ bao gồm onDown (), onLongPress (), onFling (). Và như vậy bạn có thể dùng kết hợp GestureDetector với onTouchEvent đã được nói ở trên.

1. Detecting tất cả những cử chỉ được hỗ trợ

Khi bạn tạo một đối tượng GestureDetectorCompat. Một trong những việc làm đầu tiên tại View hoặc Activity là bạn phải implement GestureDetector.OnGestureListener interface. GestureDetector.OnGestureListener thông báo cho người dùng khi một Touch event đặc biệt đã xảy ra. Để làm cho nó có thể cho đối tượng GestureDetector của bạn để nhận các sự kiện, bạn Override hàm onTouchEvent () lên các View hoặc Activity. Chạy đoạn mã sau đây để có được một cảm giác về cách hành động được kích hoạt khi bạn tương tác với màn hình cảm ứng, và những gì nội dung của MotionEvent là cho mỗi sự kiện chạm. Bạn sẽ nhận ra bao nhiêu dữ liệu được tạo ra cho dù tương tác đơn giản.

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;
    }
}

2. Detecting một tập con hỗ trợ Gestures

Nếu bạn chỉ muốn xử lý một vài cử chỉ, bạn có thể extend GestureDetector.SimpleOnGestureListener thay cho việc implement cả interface GestureDetector.OnGestureListener có rất nhiều hàm. Class GestureDetector.SimpleOnGestureListener đã implement tất cả các hàm có trong GestureDetector.OnGestureListener và thực thi một cách đơn giản rồi. nếu bạn muốn custom sự kiện gì thì bạn có thể override hàm đó và custom. ví dụ như đoạn code dưới tôi đã override lại 2 hàm là onDownonFling

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;
        }
    }
}

Nguồn Developer Android (http://developer.android.com/intl/vi/training/gestures/detector.html)