ตรวจหาท่าทางสัมผัสทั่วไป

ลองใช้วิธีแบบ Compose
Jetpack Compose เป็นชุดเครื่องมือ UI ที่แนะนำสำหรับ Android ดูวิธีใช้การสัมผัสและการป้อนข้อมูลใน Compose

ท่าทางสัมผัสจะเกิดขึ้นเมื่อผู้ใช้วางนิ้วอย่างน้อย 1 นิ้วบนหน้าจอสัมผัสและแอปของคุณตีความรูปแบบการสัมผัสนี้เป็นท่าทางสัมผัส การตรวจหาท่าทางสัมผัสมี 2 ระยะดังนี้

  1. การรวบรวมข้อมูลเหตุการณ์การสัมผัส
  2. การตีความข้อมูลเพื่อพิจารณาว่าข้อมูลตรงตามเกณฑ์สำหรับ ท่าทางสัมผัสที่แอปของคุณรองรับหรือไม่

คลาส AndroidX

ตัวอย่างในเอกสารนี้ใช้คลาส GestureDetectorCompat และ MotionEventCompat คลาสเหล่านี้อยู่ในไลบรารี AndroidX ใช้คลาส AndroidX เมื่อเป็นไปได้เพื่อให้เข้ากันได้กับอุปกรณ์รุ่นก่อนๆ MotionEventCompat _ไม่ใช่_คลาสที่มาแทนที่คลาส MotionEvent แต่มีเมธอดสาธารณูปโภคแบบคงที่ซึ่งคุณส่งออบเจ็กต์ MotionEvent ไปยังเมธอดดังกล่าวเพื่อรับการดำเนินการที่เชื่อมโยงกับเหตุการณ์นั้น

รวบรวมข้อมูล

เมื่อผู้ใช้วางนิ้วอย่างน้อย 1 นิ้วบนหน้าจอ ระบบจะทริกเกอร์การเรียกกลับ บนมุมมองที่รับเหตุการณ์การสัมผัสonTouchEvent() สำหรับลำดับเหตุการณ์การสัมผัสแต่ละรายการ เช่น ตำแหน่ง แรงกด ขนาด และการเพิ่มนิ้วอีกนิ้วหนึ่ง ซึ่งระบุว่าเป็นท่าทางสัมผัส ระบบจะเรียก onTouchEvent() หลายครั้ง

ท่าทางสัมผัสจะเริ่มต้นเมื่อผู้ใช้สัมผัสหน้าจอครั้งแรก ดำเนินการต่อเมื่อระบบติดตามตำแหน่งนิ้วของผู้ใช้ และสิ้นสุดด้วยการบันทึกเหตุการณ์สุดท้ายที่นิ้วสุดท้ายของผู้ใช้ออกจากหน้าจอ ตลอดการโต้ตอบนี้ MotionEvent ที่ส่งไปยัง onTouchEvent() จะให้รายละเอียดของการโต้ตอบทุกครั้ง แอปของคุณสามารถใช้ข้อมูลที่ MotionEvent ให้มาเพื่อพิจารณาว่ามีท่าทางสัมผัสที่สนใจเกิดขึ้นหรือไม่

บันทึกเหตุการณ์การสัมผัสสำหรับกิจกรรมหรือมุมมอง

หากต้องการสกัดกั้นเหตุการณ์การสัมผัสใน Activity หรือ View ให้ลบล้างการเรียกกลับ onTouchEvent()

ข้อมูลโค้ดต่อไปนี้ใช้ getAction() เพื่อแยกการดำเนินการที่ผู้ใช้ทำจากพารามิเตอร์ event ซึ่งจะให้ข้อมูลดิบที่จำเป็นแก่คุณเพื่อพิจารณาว่ามีท่าทางสัมผัสที่สนใจเกิดขึ้นหรือไม่

Kotlin

class MainActivity : Activity() {
    ...
    // This example shows an Activity. You can use the same approach if you are 
    // subclassing a View.
    override fun onTouchEvent(event: MotionEvent): Boolean {
        return when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                Log.d(DEBUG_TAG, "Action was DOWN")
                true
            }
            MotionEvent.ACTION_MOVE -> {
                Log.d(DEBUG_TAG, "Action was MOVE")
                true
            }
            MotionEvent.ACTION_UP -> {
                Log.d(DEBUG_TAG, "Action was UP")
                true
            }
            MotionEvent.ACTION_CANCEL -> {
                Log.d(DEBUG_TAG, "Action was CANCEL")
                true
            }
            MotionEvent.ACTION_OUTSIDE -> {
                Log.d(DEBUG_TAG, "Movement occurred outside bounds of current screen element")
                true
            }
            else -> super.onTouchEvent(event)
        }
    }
}

Java

public class MainActivity extends Activity {
...
// This example shows an Activity. You can use the same approach if you are
// subclassing a View.
@Override
public boolean onTouchEvent(MotionEvent event){
    switch(event.getAction()) {
        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);
    }
}

โค้ดนี้จะสร้างข้อความ เช่น ข้อความต่อไปนี้ใน Logcat เมื่อผู้ใช้แตะ สัมผัสค้างไว้ และลาก

GESTURES D   Action was DOWN
GESTURES D   Action was UP
GESTURES D   Action was MOVE

สำหรับท่าทางสัมผัสที่กำหนดเอง คุณสามารถประมวลผลเหตุการณ์เหล่านี้ด้วยตนเองเพื่อพิจารณาว่าเหตุการณ์เหล่านี้แสดงถึงท่าทางสัมผัสที่คุณต้องจัดการหรือไม่ อย่างไรก็ตาม หากแอปของคุณใช้ท่าทางสัมผัสทั่วไป เช่น การแตะ 2 ครั้ง การสัมผัสค้างไว้ การปัด และอื่นๆ คุณสามารถใช้ประโยชน์จากคลาส GestureDetector ได้ GestureDetector ช่วยให้คุณตรวจหาท่าทางสัมผัสทั่วไปได้ง่ายขึ้นโดยไม่ต้องประมวลผลเหตุการณ์การสัมผัสแต่ละรายการด้วยตนเอง เราจะพูดถึงเรื่องนี้เพิ่มเติมในหัวข้อตรวจหาท่าทางสัมผัส

บันทึกเหตุการณ์การสัมผัสสำหรับมุมมองเดียว

คุณสามารถแนบ View.OnTouchListener ออบเจ็กต์กับออบเจ็กต์View ใดก็ได้โดยใช้เมธอด setOnTouchListener() แทนonTouchEvent() ซึ่งจะช่วยให้คุณฟังเหตุการณ์การสัมผัสได้โดยไม่ต้องสร้างคลาสย่อยของ View ที่มีอยู่ ดังที่แสดงในตัวอย่างต่อไปนี้

Kotlin

findViewById<View>(R.id.my_view).setOnTouchListener { v, event ->
    // Respond to touch events.
    true
}

Java

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

โปรดระวังการสร้าง Listener ที่แสดงผล false สำหรับเหตุการณ์ ACTION_DOWN หากทำเช่นนี้ ระบบจะไม่เรียก Listener สำหรับลำดับเหตุการณ์ ACTION_MOVE และ ACTION_UP ที่ตามมา เนื่องจาก ACTION_DOWN เป็นจุดเริ่มต้นของเหตุการณ์การสัมผัสทั้งหมด

หากสร้างมุมมองที่กำหนดเอง คุณสามารถลบล้าง onTouchEvent() ได้ตามที่อธิบายไว้ก่อนหน้านี้

ตรวจหาท่าทางสัมผัส

Android มีคลาส GestureDetector สำหรับตรวจหาท่าทางสัมผัสทั่วไป ท่าทางสัมผัสบางอย่างที่รองรับ ได้แก่ onDown(), onLongPress() และ onFling() คุณสามารถใช้ GestureDetector ร่วมกับเมธอด onTouchEvent() ที่อธิบายไว้ก่อนหน้านี้

ตรวจหาท่าทางสัมผัสที่รองรับทั้งหมด

เมื่อสร้างอินสแตนซ์ออบเจ็กต์ GestureDetectorCompat พารามิเตอร์หนึ่งที่ออบเจ็กต์นี้ใช้คือคลาสที่ใช้อินเทอร์เฟซ GestureDetector.OnGestureListener GestureDetector.OnGestureListener จะแจ้งให้ผู้ใช้ทราบเมื่อมีการโต้ตอบแบบสัมผัสที่เฉพาะเจาะจงเกิดขึ้น หากต้องการให้ออบเจ็กต์ GestureDetector รับเหตุการณ์ได้ ให้ลบล้างเมธอด onTouchEvent() ของมุมมองหรือกิจกรรม แล้วส่งเหตุการณ์ที่สังเกตได้ทั้งหมดไปยังอินสแตนซ์ของตัวตรวจหา

ในข้อมูลโค้ดต่อไปนี้ ค่าแสดงผล true จากเมธอด on<TouchEvent> แต่ละรายการบ่งชี้ว่าระบบจัดการการโต้ตอบแบบสัมผัสแล้ว ค่าแสดงผล false จะส่งเหตุการณ์ลงไปในสแต็กมุมมองจนกว่าระบบจะจัดการการสัมผัสได้สำเร็จ

หากคุณเรียกใช้ข้อมูลโค้ดต่อไปนี้ในแอปทดสอบ คุณจะเข้าใจว่าการดำเนินการจะทริกเกอร์อย่างไรเมื่อคุณโต้ตอบกับหน้าจอสัมผัส และเนื้อหาของ MotionEvent สำหรับการโต้ตอบแบบสัมผัสแต่ละรายการคืออะไร จากนั้นคุณจะเห็นปริมาณข้อมูลที่สร้างขึ้นสำหรับการโต้ตอบอย่างง่าย

Kotlin

private const val DEBUG_TAG = "Gestures"

class MainActivity :
        Activity(),
        GestureDetector.OnGestureListener,
        GestureDetector.OnDoubleTapListener {

    private lateinit var mDetector: GestureDetectorCompat

    // Called when the activity is first created.
    public override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // Instantiate the gesture detector with the
        // application context and an implementation of
        // GestureDetector.OnGestureListener.
        mDetector = GestureDetectorCompat(this, this)
        // Set the gesture detector as the double-tap
        // listener.
        mDetector.setOnDoubleTapListener(this)
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        return if (mDetector.onTouchEvent(event)) {
            true
        } else {
            super.onTouchEvent(event)
        }
    }

    override fun onDown(event: MotionEvent): Boolean {
        Log.d(DEBUG_TAG, "onDown: $event")
        return true
    }

    override fun onFling(
            event1: MotionEvent,
            event2: MotionEvent,
            velocityX: Float,
            velocityY: Float
    ): Boolean {
        Log.d(DEBUG_TAG, "onFling: $event1 $event2")
        return true
    }

    override fun onLongPress(event: MotionEvent) {
        Log.d(DEBUG_TAG, "onLongPress: $event")
    }

    override fun onScroll(
            event1: MotionEvent,
            event2: MotionEvent,
            distanceX: Float,
            distanceY: Float
    ): Boolean {
        Log.d(DEBUG_TAG, "onScroll: $event1 $event2")
        return true
    }

    override fun onShowPress(event: MotionEvent) {
        Log.d(DEBUG_TAG, "onShowPress: $event")
    }

    override fun onSingleTapUp(event: MotionEvent): Boolean {
        Log.d(DEBUG_TAG, "onSingleTapUp: $event")
        return true
    }

    override fun onDoubleTap(event: MotionEvent): Boolean {
        Log.d(DEBUG_TAG, "onDoubleTap: $event")
        return true
    }

    override fun onDoubleTapEvent(event: MotionEvent): Boolean {
        Log.d(DEBUG_TAG, "onDoubleTapEvent: $event")
        return true
    }

    override fun onSingleTapConfirmed(event: MotionEvent): Boolean {
        Log.d(DEBUG_TAG, "onSingleTapConfirmed: $event")
        return true
    }

}

Java

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){
        if (this.mDetector.onTouchEvent(event)) {
            return true;
        }
        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 event1, MotionEvent event2, float distanceX,
            float distanceY) {
        Log.d(DEBUG_TAG, "onScroll: " + event1.toString() + event2.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;
    }
}

ตรวจหาท่าทางสัมผัสที่รองรับบางส่วน

หากต้องการประมวลผลท่าทางสัมผัสเพียงไม่กี่รายการ คุณสามารถขยาย GestureDetector.SimpleOnGestureListener แทนการใช้ อินเทอร์เฟซ GestureDetector.OnGestureListener

GestureDetector.SimpleOnGestureListener มีการใช้งานสำหรับเมธอด ทั้งหมดโดยแสดงผล false สำหรับเมธอดทั้งหมดon<TouchEvent> ซึ่งจะช่วยให้คุณลบล้างเฉพาะเมธอดที่สนใจได้ ตัวอย่างเช่น ข้อมูลโค้ดต่อไปนี้สร้างคลาสที่ขยาย GestureDetector.SimpleOnGestureListener และลบล้าง onFling() และ onDown()

ไม่ว่าคุณจะใช้ GestureDetector.OnGestureListener หรือ GestureDetector.SimpleOnGestureListener แนวทางปฏิบัติแนะนำคือการใช้เมธอด onDown() ที่แสดงผล true เนื่องจากท่าทางสัมผัสทั้งหมดเริ่มต้นด้วยข้อความ onDown() หากคุณแสดงผล false จาก onDown() ตามที่ GestureDetector.SimpleOnGestureListener ทำโดยค่าเริ่มต้น ระบบจะถือว่าคุณต้องการละเว้นท่าทางสัมผัสที่เหลือ และจะไม่เรียกเมธอดอื่นๆ ของ GestureDetector.OnGestureListener ซึ่งอาจทำให้เกิดปัญหาที่ไม่คาดคิดในแอปของคุณ ให้แสดงผล false จาก onDown() ก็ต่อเมื่อคุณต้องการละเว้นท่าทางสัมผัสทั้งหมดจริงๆ

Kotlin

private const val DEBUG_TAG = "Gestures"

class MainActivity : Activity() {

    private lateinit var mDetector: GestureDetectorCompat

    public override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        mDetector = GestureDetectorCompat(this, MyGestureListener())
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        mDetector.onTouchEvent(event)
        return super.onTouchEvent(event)
    }

    private class MyGestureListener : GestureDetector.SimpleOnGestureListener() {

        override fun onDown(event: MotionEvent): Boolean {
            Log.d(DEBUG_TAG, "onDown: $event")
            return true
        }

        override fun onFling(
                event1: MotionEvent,
                event2: MotionEvent,
                velocityX: Float,
                velocityY: Float
        ): Boolean {
            Log.d(DEBUG_TAG, "onFling: $event1 $event2")
            return true
        }
    }
}

Java

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){
        if (this.mDetector.onTouchEvent(event)) {
              return true;
        }
        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;
        }
    }
}

แหล่งข้อมูลเพิ่มเติม