ติดตามการแตะและการเคลื่อนไหวของตัวชี้

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

บทเรียนนี้อธิบายวิธีติดตามการเคลื่อนไหวในเหตุการณ์การแตะ

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

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

การติดตามการเคลื่อนไหวในท่าทางสัมผัสทำได้หลายวิธี ขึ้นอยู่กับ ความต้องการของแอปพลิเคชัน ตัวอย่างมีดังนี้

  • ตำแหน่งเริ่มต้นและสิ้นสุดของเคอร์เซอร์ เช่น การย้ายออบเจ็กต์บนหน้าจอจากจุด ก ไปยังจุด ข
  • ทิศทางที่เคอร์เซอร์เคลื่อนที่ตามที่กำหนดโดยพิกัด X และ Y
  • ประวัติ คุณดูขนาดของประวัติท่าทางสัมผัสได้โดยเรียกใช้เมธอด MotionEvent getHistorySize() จากนั้นคุณจะได้รับตำแหน่ง ขนาด เวลา และแรงกดของแต่ละ เหตุการณ์ในอดีตโดยใช้ getHistorical<Value> เมธอดของเหตุการณ์การเคลื่อนไหว ประวัติมีประโยชน์เมื่อแสดงเส้นทางของนิ้วผู้ใช้ เช่น สำหรับการวาดด้วยการสัมผัส ดูรายละเอียดได้ที่ข้อมูลอ้างอิง MotionEvent
  • ความเร็วของเคอร์เซอร์ขณะเคลื่อนที่บนหน้าจอสัมผัส

โปรดดูแหล่งข้อมูลที่เกี่ยวข้องต่อไปนี้

ความเร็วของแทร็ก

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

ตัวอย่างต่อไปนี้แสดงให้เห็นวัตถุประสงค์ของเมธอดใน VelocityTracker API

Kotlin

private const val DEBUG_TAG = "Velocity"

class MainActivity : Activity() {
    private var mVelocityTracker: VelocityTracker? = null

    override fun onTouchEvent(event: MotionEvent): Boolean {

        when (event.actionMasked) {
            MotionEvent.ACTION_DOWN -> {
                // Reset the velocity tracker back to its initial state.
                mVelocityTracker?.clear()
                // If necessary, retrieve a new VelocityTracker object to watch
                // the velocity of a motion.
                mVelocityTracker = mVelocityTracker ?: VelocityTracker.obtain()
                // Add a user's movement to the tracker.
                mVelocityTracker?.addMovement(event)
            }
            MotionEvent.ACTION_MOVE -> {
                mVelocityTracker?.apply {
                    val pointerId: Int = event.getPointerId(event.actionIndex)
                    addMovement(event)
                    // When you want to determine the velocity, call
                    // computeCurrentVelocity(). Then, call getXVelocity() and
                    // getYVelocity() to retrieve the velocity for each pointer
                    // ID.
                    computeCurrentVelocity(1000)
                    // Log velocity of pixels per second. It's best practice to
                    // use VelocityTrackerCompat where possible.
                    Log.d("", "X velocity: ${getXVelocity(pointerId)}")
                    Log.d("", "Y velocity: ${getYVelocity(pointerId)}")
                }
            }
            MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
                // Return a VelocityTracker object back to be re-used by others.
                mVelocityTracker?.recycle()
                mVelocityTracker = null
            }
        }
        return true
    }
}

Java

public class MainActivity extends Activity {
    private static final String DEBUG_TAG = "Velocity";
        ...
    private VelocityTracker mVelocityTracker = null;
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int index = event.getActionIndex();
        int action = event.getActionMasked();
        int pointerId = event.getPointerId(index);

        switch(action) {
            case MotionEvent.ACTION_DOWN:
                if(mVelocityTracker == null) {
                    // Retrieve a new VelocityTracker object to watch the
                    // velocity of a motion.
                    mVelocityTracker = VelocityTracker.obtain();
                }
                else {
                    // Reset the velocity tracker back to its initial state.
                    mVelocityTracker.clear();
                }
                // Add a user's movement to the tracker.
                mVelocityTracker.addMovement(event);
                break;
            case MotionEvent.ACTION_MOVE:
                mVelocityTracker.addMovement(event);
                // When you want to determine the velocity, call
                // computeCurrentVelocity(). Then call getXVelocity() and
                // getYVelocity() to retrieve the velocity for each pointer ID.
                mVelocityTracker.computeCurrentVelocity(1000);
                // Log velocity of pixels per second. It's best practice to use
                // VelocityTrackerCompat where possible.
                Log.d("", "X velocity: " + mVelocityTracker.getXVelocity(pointerId));
                Log.d("", "Y velocity: " + mVelocityTracker.getYVelocity(pointerId));
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                // Return a VelocityTracker object back to be re-used by others.
                mVelocityTracker.recycle();
                break;
        }
        return true;
    }
}

ใช้การจับภาพเคอร์เซอร์

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

ขอการจับพอยน์เตอร์

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

หากต้องการขอการจับพอยน์เตอร์ ให้เรียกใช้เมธอด requestPointerCapture() ในมุมมอง ตัวอย่างโค้ดต่อไปนี้แสดงวิธีขอการจับภาพเคอร์เซอร์ เมื่อผู้ใช้คลิกมุมมอง

Kotlin

fun onClick(view: View) {
    view.requestPointerCapture()
}

Java

@Override
public void onClick(View view) {
    view.requestPointerCapture();
}

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

จัดการเหตุการณ์ของเคอร์เซอร์ที่จับภาพ

เมื่อ View ได้รับการจับภาพเคอร์เซอร์สำเร็จแล้ว Android จะส่ง เหตุการณ์ของเมาส์ มุมมองที่โฟกัสจะจัดการเหตุการณ์ได้โดยการทำงานอย่างใดอย่างหนึ่งต่อไปนี้

ตัวอย่างโค้ดต่อไปนี้แสดงวิธีใช้onCapturedPointerEvent(MotionEvent)

Kotlin

override fun onCapturedPointerEvent(motionEvent: MotionEvent): Boolean {
    // Get the coordinates required by your app.
    val verticalOffset: Float = motionEvent.y
    // Use the coordinates to update your view and return true if the event is
    // successfully processed.
    return true
}

Java

@Override
public boolean onCapturedPointerEvent(MotionEvent motionEvent) {
  // Get the coordinates required by your app.
  float verticalOffset = motionEvent.getY();
  // Use the coordinates to update your view and return true if the event is
  // successfully processed.
  return true;
}

ตัวอย่างโค้ดต่อไปนี้แสดงวิธีลงทะเบียน OnCapturedPointerListener

Kotlin

myView.setOnCapturedPointerListener { view, motionEvent ->
    // Get the coordinates required by your app.
    val horizontalOffset: Float = motionEvent.x
    // Use the coordinates to update your view and return true if the event is
    // successfully processed.
    true
}

Java

myView.setOnCapturedPointerListener(new View.OnCapturedPointerListener() {
  @Override
  public boolean onCapturedPointer (View view, MotionEvent motionEvent) {
    // Get the coordinates required by your app.
    float horizontalOffset = motionEvent.getX();
    // Use the coordinates to update your view and return true if the event is
    // successfully processed.
    return true;
  }
});

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

ปล่อยการจับเคอร์เซอร์

มุมมองในแอปสามารถปล่อยการจับพอยน์เตอร์ได้โดยการเรียกใช้ releasePointerCapture() ดังที่แสดงในตัวอย่างโค้ดต่อไปนี้

Kotlin

override fun onClick(view: View) {
    view.releasePointerCapture()
}

Java

@Override
public void onClick(View view) {
    view.releasePointerCapture();
}

ระบบสามารถนำการจับภาพออกจากมุมมองได้โดยที่คุณไม่ต้องเรียกใช้ releasePointerCapture() อย่างชัดเจน ซึ่งมักเป็นเพราะลำดับชั้นการแสดงผลของมุมมองที่มีมุมมองที่ขอการจับภาพสูญเสียโฟกัส