ภาพรวม
ตัวอย่างนี้สาธิตการจับเหตุการณ์ MotionEvent.ACTION_DOWN ซึ่งเกิดขึ้นเมื่อผู้ใช้สัมผัสหน้าจอ (Touch down) ใช้สำหรับการตรวจจับการกด ต่างจาก OnClickListener ที่รอจนกว่าจะปล่อย (Action Up)
ขั้นตอนการสร้างโปรเจกต์
ขั้นตอนที่ 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="#E0E0E0"
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 Down Test");
actionEvent.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
// ใช้ MotionEventCompat เพื่อรองรับ Multi-touch
int action = MotionEventCompat.getActionMasked(event);
if (action == MotionEvent.ACTION_DOWN) {
// เกิดขึ้นทันทีเมื่อสัมผัสหน้าจอ
Toast.makeText(MainActivity.this, "Action DOWN - กดลงแล้ว", Toast.LENGTH_SHORT).show();
actionEvent.setText("สถานะ: กดลง (DOWN)");
actionEvent.setBackgroundColor(0xFF4CAF50); // สีเขียว
return true; // Consume event
}
return false; // ให้ระบบจัดการต่อ
}
});
}
}
MotionEvent Action Types
| Action Constant | คำอธิบาย | ลำดับ |
|---|---|---|
ACTION_DOWN | Pointer แรกสัมผัสหน้าจอ (เริ่ม Gesture) | 1 |
ACTION_POINTER_DOWN | Pointer เพิ่มเติมสัมผัส (Multi-touch) | 2+ |
ACTION_MOVE | Pointer เคลื่อนที่บนหน้าจอ | ซ้ำหลายครั้ง |
ACTION_POINTER_UP | Pointer เพิ่มเติมปล่อย (Multi-touch) | - |
ACTION_UP | Pointer สุดท้ายปล่อย (จบ Gesture) | สุดท้าย |
ACTION_CANCEL | Gesture ถูกยกเลิก (โดยระบบ) | - |
Action Down vs OnClickListener
| คุณสมบัติ | OnTouchListener (ACTION_DOWN) | OnClickListener |
|---|---|---|
| เวลาทำงาน | ทันทีเมื่อสัมผัส | เมื่อปล่อย (Down + Up ในพื้นที่เดียวกัน) |
| Drag/Scroll | จับได้ตอนกด | ไม่ทำงานถ้าลาก |
| Long Press | แยกได้ชัดเจน | มี OnLongClickListener แยก |
| Multi-touch | รองรับ (Pointer ID) | ไม่รองรับโดยตรง |
| Visual Feedback | ต้องจัดการเอง | มี Ripple/Background อัตโนมัติ |
กรณีการใช้งาน ACTION_DOWN
- Game Controls: ปุ่มควบคุมเกมที่ต้องตอบสนองทันที
- Drawing Apps: เริ่มวาดทันทีเมื่อสัมผัส
- Custom Buttons: ปุ่มที่มี Visual feedback ทันที
- Drag & Drop: เริ่ม Drag operation
- Instrument Apps: Piano, Drum - เสียงต้องออกทันที
ข้อควรระวัง
- ต้อง return true ใน ACTION_DOWN เพื่อบอกระบบว่า Consume event แล้ว (จะได้รับ MOVE, UP ต่อ)
- หาก return false จะไม่ได้รับ Event ต่อไป (MOVE, UP)
- ใช้
MotionEventCompat.getActionMasked()สำหรับ Multi-touch compatibility - จัดการ
ACTION_CANCELสำหรับกรณีระบบยกเลิก (เช่น Dialog ปิดทับ) - สำหรับ View ธรรมดา ควรใช้
setClickable(true)และ Background selector
ตัวอย่างปุ่มกดที่มี Visual Feedback
button.setOnTouchListener((v, event) -> {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
v.setBackgroundColor(0xFF4CAF50); // สีเข้มตอนกด
v.setScaleX(0.95f); v.setScaleY(0.95f); // ย่อเล็กนิด
return true;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
v.setBackgroundColor(0xFF2196F3); // สีปกติ
v.setScaleX(1f); v.setScaleY(1f);
if (event.getActionMasked() == MotionEvent.ACTION_UP) {
performClick(); // เรียก OnClickListener
}
return true;
case MotionEvent.ACTION_MOVE:
// ตรวจสอบว่าอยู่ใน bounds หรือไม่
if (!isPointInView(v, event.getX(), event.getY())) {
v.setBackgroundColor(0xFF2196F3);
v.setScaleX(1f); v.setScaleY(1f);
}
return true;
}
return false;
});