หัวข้อสำคัญ

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

ส่วนต่อไปนี้จะอธิบายแนวคิดสําคัญ 2-3 ข้อสําหรับกระบวนการลากและวาง

กระบวนการลากและวาง

กระบวนการลากและวางมี 4 ขั้นตอนหรือสถานะ ได้แก่ เริ่ม ดำเนินการต่อ วาง และสิ้นสุด

เริ่มต้นแล้ว

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

  • ข้อมูลที่จะลาก
  • โค้ดเรียกกลับสำหรับการวาดเงาการลาก
  • ข้อมูลเมตาที่อธิบายข้อมูลที่ลาก
  • ระบบจะตอบสนองด้วยการเรียกกลับไปยังแอปพลิเคชันของคุณเพื่อรับการลากเงา จากนั้นระบบจะแสดงเงาการลากบนอุปกรณ์
  • จากนั้นระบบจะส่งเหตุการณ์การลากที่มีประเภทการดําเนินการ ACTION_DRAG_STARTED ไปยัง ฟังก์ชันการฟังเหตุการณ์การลาก ของออบเจ็กต์ View ทั้งหมดในเลย์เอาต์ปัจจุบัน หากต้องการรับเหตุการณ์การลากต่อไป ซึ่งรวมถึงเหตุการณ์การวางที่เป็นไปได้ Listener เหตุการณ์การลากต้องแสดงผล true ซึ่งจะลงทะเบียน Listener กับระบบ เฉพาะผู้ฟังที่ลงทะเบียนไว้เท่านั้นที่จะยังคงได้รับเหตุการณ์การลาก เมื่อถึงจุดนี้ โปรแกรมฟังยังเปลี่ยนลักษณะที่ปรากฏของออบเจ็กต์เป้าหมายการวาง View เพื่อแสดงให้เห็นว่ามุมมองยอมรับเหตุการณ์การวางได้ด้วย
  • หากตัวรับฟังเหตุการณ์การลากแสดงผลเป็น false แสดงว่าไม่ได้รับการลากเหตุการณ์สําหรับการดำเนินการปัจจุบันจนกว่าระบบจะส่งเหตุการณ์การลากที่มีประเภทการดําเนินการ ACTION_DRAG_ENDED การคืนค่า false เป็นการบอกให้ระบบทราบว่าไม่สนใจการดำเนินการลากและวาง และไม่ต้องการยอมรับข้อมูลที่ลากมา
ต่อไป
ผู้ใช้ลากต่อ เมื่อเงาการลากตัดกับกล่องขอบเขตของเป้าหมายการวาง ระบบจะส่งเหตุการณ์การลากอย่างน้อย 1 รายการไปยัง Listener เหตุการณ์การลากของเป้าหมาย โปรแกรมฟังอาจเปลี่ยนลักษณะที่ปรากฏของเป้าหมายการลากวาง View เพื่อตอบสนองต่อเหตุการณ์ ตัวอย่างเช่น หากเหตุการณ์บ่งชี้ว่าเงาการลากเข้าสู่กล่องขอบเขตของเป้าหมายการปล่อยประเภทการดำเนินการ ACTION_DRAG_ENTERED โปรแกรมฟังเหตุการณ์จะตอบสนองด้วยการไฮไลต์ View
ยกเลิก
ผู้ใช้ปล่อยเงาการลากภายในขอบเขตของเป้าหมายการวาง ระบบจะส่งเหตุการณ์การลากที่มีประเภทการดำเนินการ ACTION_DROP ไปยัง Listener ของเป้าหมายการวาง ออบเจ็กต์เหตุการณ์การลากมีข้อมูลที่ส่งไปยังระบบในการเรียกใช้ startDragAndDrop() ที่เริ่มการดำเนินการ คาดว่า Listener จะแสดงผลบูลีน true ไปยังระบบหาก Listener ประมวลผลข้อมูลที่ทิ้งได้สําเร็จ : ขั้นตอนนี้จะเริ่มต้นขึ้นก็ต่อเมื่อผู้ใช้วางเงาการลากภายในกล่องขอบเขตของ View ที่ลงทะเบียน Listener เพื่อรับเหตุการณ์การลาก (เป้าหมายการวาง) หากผู้ใช้ปล่อยเงาการลากในสถานการณ์อื่นๆ ระบบจะไม่ACTION_DROPส่งเหตุการณ์การลาก
สิ้นสุดแล้ว

หลังจากผู้ใช้ปล่อยเงาการลาก และหลังจากระบบส่ง

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

อ่านคำอธิบายขั้นตอนเหล่านี้แต่ละขั้นตอนอย่างละเอียดได้ที่ส่วนที่เรียกว่าการดำเนินการแบบลากและวาง

เหตุการณ์การลาก

ระบบจะส่งเหตุการณ์การลากในรูปแบบออบเจ็กต์ DragEvent ซึ่งมีประเภทการดำเนินการที่อธิบายสิ่งที่เกิดขึ้นในกระบวนการลากและวาง ออบเจ็กต์อาจมีข้อมูลอื่นๆ ด้วย ทั้งนี้ขึ้นอยู่กับประเภทการดำเนินการ

Listener เหตุการณ์การลากจะได้รับออบเจ็กต์ DragEvent หากต้องการดูประเภทการดำเนินการ ผู้ฟังจะโทรไปที่ DragEvent.getAction() ค่าที่เป็นไปได้มี 6 ค่าซึ่งกำหนดโดยค่าคงที่ในคลาส DragEvent ซึ่งอธิบายไว้ในตารางที่ 1

ตารางที่ 1 ประเภทการดำเนินการของ DragEvent

ประเภทการดําเนินการ ความหมาย
ACTION_DRAG_STARTED แอปพลิเคชันเรียก startDragAndDrop() และรับเงาการลาก หากตัวฟังต้องการรับเหตุการณ์การลากสําหรับการดำเนินการนี้ต่อไป จะต้องแสดงผลบูลีน true ไปยังระบบ
ACTION_DRAG_ENTERED เงาการลากเข้าสู่ขอบเขตของ View ของ Listener เหตุการณ์การลาก นี่คือประเภทการดำเนินการของเหตุการณ์แรกๆ ที่ Listener ได้รับเมื่อเงาการลากเข้าสู่ขอบเขต
ACTION_DRAG_LOCATION หลังจากเหตุการณ์ ACTION_DRAG_ENTERED เกิด เงาบดยังคงอยู่ภายในกล่องขอบเขตของ View ของ Listener เหตุการณ์การลาก
ACTION_DRAG_EXITED หลังจากเหตุการณ์ ACTION_DRAG_ENTERED และเหตุการณ์ ACTION_DRAG_LOCATION อย่างน้อย 1 รายการ เงาการลากจะย้ายออกนอกขอบเขตของ View ของ Listener เหตุการณ์การลาก
ACTION_DROP เงาการลากจะปล่อยเหนือ View ของ Listener เหตุการณ์การลาก ระบบจะส่งประเภทการดําเนินการนี้ไปยัง Listener ของView ออบเจ็กต์ก็ต่อเมื่อ Listener แสดงผลบูลีน true เพื่อตอบสนองACTION_DRAG_STARTEDเหตุการณ์การลาก ระบบจะไม่ส่งการดําเนินการประเภทนี้หากผู้ใช้ปล่อยเงาการลากเหนือ View ที่ยังไม่ได้ลงทะเบียน Listener หรือหากผู้ใช้ปล่อยเงาการลากเหนือสิ่งที่ไม่ได้เป็นส่วนหนึ่งของเลย์เอาต์ปัจจุบัน

โปรแกรมฟังจะแสดงผลบูลีน true หากประมวลผลการปล่อยสำเร็จ ไม่เช่นนั้นจะต้องแสดงผลเป็น false

ACTION_DRAG_ENDED ระบบกำลังสิ้นสุดการดำเนินการลากและวาง ประเภทการดําเนินการนี้ไม่จำเป็นต้องมีเหตุการณ์ ACTION_DROP มาก่อน หากระบบส่ง ACTION_DROP การได้รับประเภทการดําเนินการ ACTION_DRAG_ENDED ไม่ได้หมายความว่าการปล่อยสําเร็จ ผู้ฟังต้องเรียกใช้ getResult() ตามที่แสดงในตารางที่ 2 เพื่อรับค่าที่แสดงผลเพื่อตอบสนองต่อ ACTION_DROP หากไม่มีการส่งเหตุการณ์ ACTION_DROP getResult() จะแสดงผลเป็น false

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

ตารางที่ 2 ข้อมูล DragEvent ที่ถูกต้องตามประเภทการดําเนินการ

getAction()
value
getClipDescription()
value
getLocalState()
value
getX()
value
getY()
value
getClipData()
value
getResult()
value
ACTION_DRAG_STARTED ✓ ✓        
ACTION_DRAG_ENTERED ✓ ✓        
ACTION_DRAG_LOCATION ✓ ✓ ✓ ✓    
ACTION_DRAG_EXITED ✓ ✓        
ACTION_DROP ✓ ✓ ✓ ✓ ✓  
ACTION_DRAG_ENDED   ✓       ✓

เมธอด DragEvent getAction(), describeContents(), writeToParcel() และ toString() จะแสดงผลข้อมูลที่ถูกต้องเสมอ

หากเมธอดไม่มีข้อมูลที่ถูกต้องสําหรับประเภทการดําเนินการหนึ่งๆ ก็จะแสดงผลเป็น null หรือ 0 ทั้งนี้ขึ้นอยู่กับประเภทผลลัพธ์

เงาการลาก

ในระหว่างการลากและวาง ระบบจะแสดงรูปภาพที่ผู้ใช้ลาก รูปภาพนี้แสดงข้อมูลที่กำลังลากอยู่สำหรับการย้ายข้อมูล สำหรับการดำเนินการอื่นๆ รูปภาพจะแสดงแง่มุมบางอย่างของการดำเนินการลาก

รูปภาพนี้เรียกว่าเงาการลาก คุณสร้างออบเจ็กต์ด้วยเมธอดที่คุณประกาศสำหรับออบเจ็กต์ View.DragShadowBuilder คุณส่ง Builder ไปยังระบบเมื่อเริ่มการดําเนินการลากและวางโดยใช้ startDragAndDrop() ในการตอบสนองต่อ startDragAndDrop() ระบบจะเรียกใช้เมธอดการเรียกกลับที่คุณกำหนดไว้ใน View.DragShadowBuilder เพื่อรับเงาการลาก

คลาส View.DragShadowBuilder มีคอนสตรัคเตอร์ 2 รายการ ดังนี้

View.DragShadowBuilder(View)

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

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

View.DragShadowBuilder()

หากคุณใช้คอนสตรัคเตอร์นี้ ออบเจ็กต์ View จะไม่พร้อมใช้งานในออบเจ็กต์ View.DragShadowBuilder ช่องนี้ตั้งค่าเป็น null คุณต้องขยาย View.DragShadowBuilder และลบล้างเมธอด มิเช่นนั้นคุณจะเห็นเงาการลากที่มองไม่เห็น ระบบไม่แสดงข้อผิดพลาด

คลาส View.DragShadowBuilder มีเมธอด 2 รายการที่ร่วมกันสร้างเงาลาก

onProvideShadowMetrics()

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

outShadowSize: Point ออบเจ็กต์ ความกว้างของเงาการลากจะอยู่ใน x และความสูงจะอยู่ใน y

outShadowTouchPoint: ออบเจ็กต์ Point จุดสัมผัสคือตําแหน่งภายในเงาการลากซึ่งต้องอยู่ใต้นิ้วของผู้ใช้ขณะลาก ตำแหน่ง X ขององค์ประกอบจะอยู่ใน x และตำแหน่ง Y ขององค์ประกอบจะอยู่ใน y

onDrawShadow()

ทันทีหลังจากการเรียก onProvideShadowMetrics() ระบบจะเรียก onDrawShadow() เพื่อสร้างเงาการลาก เมธอดนี้มีอาร์กิวเมนต์เดียว ซึ่งเป็นออบเจ็กต์ Canvas ที่ระบบสร้างขึ้นจากพารามิเตอร์ที่คุณระบุใน onProvideShadowMetrics() เมธอดจะวาดเงาการลากใน Canvas ที่ระบุ

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

ลาก Listener เหตุการณ์และเมธอดการเรียกกลับ

View จะได้รับเหตุการณ์การลากด้วย Listener เหตุการณ์การลากที่ใช้ View.OnDragListener หรือด้วยเมธอด Callback onDragEvent() ของมุมมอง เมื่อระบบเรียกใช้เมธอดหรือ Listener ระบบจะระบุอาร์กิวเมนต์ DragEvent

ในกรณีส่วนใหญ่ การใช้ Listener นั้นดีกว่าการใช้เมธอดการเรียกกลับ เมื่อออกแบบ UI คุณมักจะไม่ได้สร้างคลาสย่อยของ View แต่การใช้เมธอด callback จะทำให้คุณต้องสร้างคลาสย่อยเพื่อลบล้างเมธอด ในทางกลับกัน คุณสามารถใช้คลาส Listener 1 คลาสกับออบเจ็กต์ View หลายรายการที่แตกต่างกันได้ นอกจากนี้ คุณยังใช้ Lambda นี้เป็นคลาสอินไลน์นิรนามหรือนิพจน์ Lambda ได้ด้วย หากต้องการตั้งค่าตัวรับฟังสําหรับออบเจ็กต์ View ให้เรียกใช้ setOnDragListener()

หรือจะเปลี่ยนการใช้งานเริ่มต้นของ onDragEvent() โดยไม่ต้องลบล้างเมธอดก็ได้ ตั้งค่า OnReceiveContentListener ในมุมมอง ดูรายละเอียดเพิ่มเติมได้ที่ setOnReceiveContentListener() จากนั้นเมธอด onDragEvent() จะทําสิ่งต่อไปนี้โดยค่าเริ่มต้น

  • แสดงผลเป็น "จริง" เพื่อตอบสนองการเรียกใช้ startDragAndDrop()
  • การโทร performReceiveContent() หากมีการวางข้อมูลการลากและวางในมุมมอง ระบบจะส่งข้อมูลไปยังเมธอดเป็นออบเจ็กต์ ContentInfo วิธีการเรียกใช้ OnReceiveContentListener

  • แสดงผลเป็น "จริง" หากมีการวางข้อมูลการลากและวางในมุมมอง และOnReceiveContentListenerใช้เนื้อหาใดก็ตาม

กำหนด OnReceiveContentListener เพื่อจัดการข้อมูลสำหรับแอปของคุณโดยเฉพาะ หากต้องการความเข้ากันได้แบบย้อนหลังจนถึง API ระดับ 24 ให้ใช้ OnReceiveContentListener เวอร์ชัน Jetpack

คุณมี Listener เหตุการณ์การลากและเมธอด Callback สําหรับออบเจ็กต์ View ได้ ซึ่งในกรณีนี้ระบบจะเรียก Listener ก่อน ระบบจะไม่เรียกใช้เมธอด callback เว้นแต่ตัวฟังจะแสดงผล false

การรวมวิธีการ onDragEvent() กับ View.OnDragListener นั้นคล้ายกับการรวม onTouchEvent() กับ View.OnTouchListener ที่ใช้กับเหตุการณ์การสัมผัส