Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Android

วิธีใช้ Action Up Event (การปล่อยนิ้ว) ใน Android

ภาพรวม

ตัวอย่างนี้สาธิตการจับเหตุการณ์ MotionEvent.ACTION_UP ซึ่งเกิดขึ้นเมื่อผู้ใช้ปล่อยนิ้วจากหน้าจอ (Pointer สุดท้าย) หมายถึงการจบลงของ Gesture ใช้คู่กับ ACTION_DOWN เพื่อทำ Click/Tap detection แบบกำหนดเอง

ขั้นตอนการสร้างโปรเจกต์

ขั้นตอนที่ 1: สร้างโปรเจกต์ใหม่

Android Studio → File → New Project → Empty Activity

ขั้นตอนที่ 2: Layout (res/layout/activity_main.xml)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/actionEvent"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_marginTop="30dp"
        android:textSize="40sp"
        android:gravity="center"
        android:text="กดแล้วปล่อยที่นี่"
        android:background="#FFF3E0"
        android:padding="20dp" />

</LinearLayout>

ขั้นตอนที่ 3: โค้ดหลัก (src/MainActivity.java)

package com.example.myapplication;

import android.os.Build;
import android.os.Bundle;
import android.support.annotation.RequiresApi;
import android.support.v4.view.MotionEventCompat;
import android.support.v7.app.AppCompatActivity;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    TextView textView;

    @SuppressLint("ClickableViewAccessibility")
    @RequiresApi(api = Build.VERSION_CODES.N)
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        TextView actionEvent = findViewById(R.id.actionEvent);
        actionEvent.setText("Action UP Test");
        
        actionEvent.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                int action = MotionEventCompat.getActionMasked(event);
                
                if (action == MotionEvent.ACTION_UP) {
                    // เกิดขึ้นเมื่อปล่อยนิ้ว (Pointer สุดท้าย)
                    Toast.makeText(MainActivity.this, "Action UP - ปล่อยนิ้วแล้ว", Toast.LENGTH_SHORT).show();
                    actionEvent.setText("สถานะ: ปล่อยนิ้ว (UP)");
                    actionEvent.setBackgroundColor(0xFF2196F3); // กลับสีปกติ
                    return true;
                }
                
                if (action == MotionEvent.ACTION_DOWN) {
                    actionEvent.setText("สถานะ: กดลง (DOWN)");
                    actionEvent.setBackgroundColor(0xFF4CAF50);
                    return true;
                }
                
                return false;
            }
        });
    }
}

ลำดับ Event ใน Gesture ปกติ (Single Tap)

ACTION_DOWN (1 pointer) → ACTION_MOVE* (0 หรือหลายครั้ง) → ACTION_UP

Multi-touch Sequence

Pointer 1: ACTION_DOWN
Pointer 2: ACTION_POINTER_DOWN
Pointer 1: ACTION_MOVE...
Pointer 2: ACTION_MOVE...
Pointer 2: ACTION_POINTER_UP
Pointer 1: ACTION_MOVE...
Pointer 1: ACTION_UP  ← จบ Gesture

Action Up vs OnClickListener

คุณสมบัติOnTouchListener (ACTION_UP)OnClickListener
Triggerปล่อยนิ้วทุกครั้ง (แม้จะลากนอก View)ปล่อยนิ้วภายใน View เท่านั้น
Drag Cancelยังได้รับ UPไม่ได้รับ Click
Timingทันทีเมื่อปล่อยหลัง UP + Validation
Custom Logicยืดหยุ่นสูงสุดจำกัด

กรณีการใช้งาน ACTION_UP

  • Custom Click Handling: ต้องการตรวจสอบเงื่อนไขก่อน Click
  • Drag & Drop End: จบการลาก วาง Item (Drop zone detection)
  • Drawing Complete: จบ Stroke, บันทึก Path
  • Swipe/Fling Detection: วัดความเร็ว/ทิศทางจาก DOWN ถึง UP
  • Long Press Detection: วัดเวลาระหว่าง DOWN ถึง UP

ตัวอย่าง: คำนวณ Swipe Velocity

private long downTime;
private float downX, downY;

view.setOnTouchListener((v, event) -> {
    switch (event.getActionMasked()) {
        case MotionEvent.ACTION_DOWN:
            downTime = System.currentTimeMillis();
            downX = event.getX();
            downY = event.getY();
            return true;
            
        case MotionEvent.ACTION_UP:
            long upTime = System.currentTimeMillis();
            float upX = event.getX();
            float upY = event.getY();
            
            long duration = upTime - downTime;
            float distance = (float) Math.hypot(upX - downX, upY - downY);
            float velocity = distance / duration; // px/ms
            
            if (distance > 100 && velocity > 0.5f) {
                // Swipe detected
                float angle = (float) Math.toDegrees(Math.atan2(upY - downY, upX - downX));
                handleSwipe(angle, velocity);
            }
            return true;
    }
    return false;
});

ข้อควรระวัง

  • ต้อง return true ใน ACTION_DOWN ถึงจะได้รับ ACTION_UP
  • ACTION_UP อาจไม่เกิด ถ้ามี ACTION_CANCEL (System interrupt: Dialog, Phone call, ANR)
  • Multi-touch: ACTION_UP = Pointer สุดท้ายปล่อย | ใช้ ACTION_POINTER_UP สำหรับ Pointer อื่นๆ
  • พิกัดใน UP อาจต่างจาก DOWN หากลาก (ใช้สำหรับ Swipe detection)
  • สำหรับ Click ปกติ ใช้ OnClickListener ดีกว่า (มี Ripple, Accessibility, Haptic feedback อัตโนมัติ)

Best Practice: ใช้ GestureDetector

GestureDetector detector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
    @Override
    public boolean onSingleTapUp(MotionEvent e) {
        // Tap ปกติ
        return true;
    }
    @Override
    public boolean onDoubleTap(MotionEvent e) {
        // Double tap
        return true;
    }
    @Override
    public void onLongPress(MotionEvent e) {
        // Long press
    }
    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float vX, float vY) {
        // Swipe/Fling
        return true;
    }
});

view.setOnTouchListener((v, event) -> detector.onTouchEvent(event));