บทเรียนนี้อธิบายวิธีติดตามการเคลื่อนไหวในเหตุการณ์การแตะ
ระบบจะทริกเกอร์
onTouchEvent()
ใหม่ด้วยเหตุการณ์
ACTION_MOVE
เมื่อใดก็ตามที่ตำแหน่ง แรงกด หรือขนาดของการสัมผัสปัจจุบันเปลี่ยนแปลง ตามที่อธิบายไว้ในตรวจหาท่าทางสัมผัสทั่วไป ระบบจะบันทึกเหตุการณ์ทั้งหมดนี้ในพารามิเตอร์ MotionEvent ของ onTouchEvent()
เนื่องจากการสัมผัสด้วยนิ้วอาจไม่ใช่รูปแบบการโต้ตอบที่แม่นยำที่สุดเสมอไป การตรวจหาเหตุการณ์การสัมผัสจึงมักอิงตามการเคลื่อนไหวมากกว่าการสัมผัสแบบง่ายๆ Android มีแนวคิดเกี่ยวกับระยะสัมผัสเพื่อช่วยให้แอปแยกความแตกต่างระหว่างท่าทางสัมผัสที่อิงตามการเคลื่อนไหว (เช่น การปัด) กับท่าทางสัมผัสที่ไม่อิงตามการเคลื่อนไหว (เช่น การแตะครั้งเดียว) ได้ ความคลาดเคลื่อนในการสัมผัสหมายถึงระยะทางในหน่วยพิกเซลที่การสัมผัสของผู้ใช้สามารถ เคลื่อนที่ได้ก่อนที่ระบบจะตีความท่าทางสัมผัสนั้นเป็นการสัมผัสที่อิงตามการเคลื่อนไหว ดูข้อมูลเพิ่มเติมเกี่ยวกับหัวข้อนี้ได้ที่จัดการเหตุการณ์การแตะใน ViewGroup
การติดตามการเคลื่อนไหวในท่าทางสัมผัสทำได้หลายวิธี ขึ้นอยู่กับ ความต้องการของแอปพลิเคชัน ตัวอย่างมีดังนี้
- ตำแหน่งเริ่มต้นและสิ้นสุดของเคอร์เซอร์ เช่น การย้ายออบเจ็กต์บนหน้าจอจากจุด ก ไปยังจุด ข
- ทิศทางที่เคอร์เซอร์เคลื่อนที่ตามที่กำหนดโดยพิกัด X และ Y
- ประวัติ คุณดูขนาดของประวัติท่าทางสัมผัสได้โดยเรียกใช้เมธอด
MotionEventgetHistorySize()จากนั้นคุณจะได้รับตำแหน่ง ขนาด เวลา และแรงกดของแต่ละ เหตุการณ์ในอดีตโดยใช้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) - หรือลงทะเบียน
OnCapturedPointerListener
ตัวอย่างโค้ดต่อไปนี้แสดงวิธีใช้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() อย่างชัดเจน ซึ่งมักเป็นเพราะลำดับชั้นการแสดงผลของมุมมองที่มีมุมมองที่ขอการจับภาพสูญเสียโฟกัส