ใน Android มีวิธีมากกว่า 1 วิธีในการสกัดกั้นเหตุการณ์จากการโต้ตอบของผู้ใช้กับแอปพลิเคชันของคุณ เมื่อพิจารณาเหตุการณ์ภายในอินเทอร์เฟซผู้ใช้ แนวทางคือการบันทึกเหตุการณ์จากออบเจ็กต์ View ที่เฉพาะเจาะจงซึ่งผู้ใช้โต้ตอบด้วย ซึ่งคลาส View จะเป็นตัวช่วยในการดำเนินการดังกล่าว
ในคลาส View ต่างๆ ที่คุณจะใช้เพื่อสร้างเลย์เอาต์ คุณอาจเห็นเมธอดเรียกกลับสาธารณะหลายรายการ
ซึ่งดูเหมือนว่าจะมีประโยชน์สำหรับเหตุการณ์ UI เฟรมเวิร์ก Android จะเรียกใช้เมธอดเหล่านี้เมื่อมีการดำเนินการที่เกี่ยวข้องกับออบเจ็กต์นั้น เช่น เมื่อมีการแตะ View (เช่น ปุ่ม) ระบบจะเรียกใช้เมธอด onTouchEvent() ในออบเจ็กต์นั้น อย่างไรก็ตาม หากต้องการสกัดกั้นการดำเนินการนี้ คุณต้องขยาย
คลาสและลบล้างเมธอด อย่างไรก็ตาม การขยายออบเจ็กต์ View ทุกรายการ
เพื่อจัดการเหตุการณ์ดังกล่าวคงไม่สะดวก ด้วยเหตุนี้ คลาส View จึงมี
คอลเล็กชันของอินเทอร์เฟซที่ซ้อนกันพร้อมด้วยการเรียกกลับที่คุณกำหนดได้ง่ายขึ้นมาก อินเทอร์เฟซเหล่านี้
ซึ่งเรียกว่าเครื่องมือฟังเหตุการณ์คือตั๋วของคุณในการบันทึกการโต้ตอบของผู้ใช้กับ UI
แม้ว่าคุณจะใช้เครื่องมือฟังเหตุการณ์เพื่อฟังการโต้ตอบของผู้ใช้บ่อยกว่า แต่ก็อาจมีบางครั้งที่คุณต้องการขยายคลาส View เพื่อสร้างคอมโพเนนต์ที่กำหนดเอง
คุณอาจต้องการขยายButton
คลาสเพื่อสร้างสิ่งที่ดูดีขึ้น ในกรณีนี้ คุณจะกำหนดลักษณะการทำงานของเหตุการณ์เริ่มต้นสำหรับคลาสได้โดยใช้ตัวแฮนเดิลเหตุการณ์ของคลาส
Listener เหตุการณ์
Event Listener คืออินเทอร์เฟซในคลาส View ที่มีเมธอดเรียกกลับเดียว
เฟรมเวิร์ก Android จะเรียกใช้เมธอดเหล่านี้เมื่อมีการโต้ตอบของผู้ใช้กับรายการใน UI ซึ่งจะทริกเกอร์ View ที่ลงทะเบียน Listener ไว้
อินเทอร์เฟซของ Listener เหตุการณ์มีเมธอด Callback ต่อไปนี้
onClick()- จาก
View.OnClickListenerระบบจะเรียกใช้ฟังก์ชันนี้เมื่อผู้ใช้แตะรายการ (เมื่ออยู่ในโหมดแตะ) หรือโฟกัสที่รายการด้วยปุ่มนำทางหรือแทร็กบอล และ กดปุ่ม "Enter" ที่เหมาะสมหรือกดแทร็กบอลลง onLongClick()- จาก
View.OnLongClickListenerระบบจะเรียกใช้ฟังก์ชันนี้เมื่อผู้ใช้แตะและกดรายการค้างไว้ (เมื่ออยู่ในโหมดสัมผัส) หรือ โฟกัสที่รายการด้วยปุ่มนำทางหรือแทร็กบอล และ กดปุ่ม "Enter" ที่เหมาะสมค้างไว้ หรือกดแทร็กบอลค้างไว้ (เป็นเวลา 1 วินาที) onFocusChange()- จาก
View.OnFocusChangeListenerระบบจะเรียกใช้ฟังก์ชันนี้เมื่อผู้ใช้ไปยังหรือออกจากรายการโดยใช้ปุ่มนำทางหรือแทร็กบอล onKey()- จาก
View.OnKeyListenerเรียกใช้เมื่อผู้ใช้โฟกัสที่รายการและกดหรือปล่อยคีย์ฮาร์ดแวร์ในอุปกรณ์ onTouch()- จาก
View.OnTouchListenerระบบจะเรียกใช้ฟังก์ชันนี้เมื่อผู้ใช้ดำเนินการที่มีคุณสมบัติเป็นการโต้ตอบแบบสัมผัส ซึ่งรวมถึงการกด การปล่อย หรือท่าทางสัมผัสการเคลื่อนไหวใดๆ บนหน้าจอ (ภายในขอบเขตของรายการ) onCreateContextMenu()- จาก
View.OnCreateContextMenuListenerเรียกใช้เมื่อสร้างเมนูบริบท (เป็นผลมาจากการ "คลิกแบบยาว" อย่างต่อเนื่อง) ดูการสนทนา เกี่ยวกับเมนูตามบริบทในคู่มือนักพัฒนาซอฟต์แวร์เมนู
โดยเมธอดเหล่านี้จะอยู่ในอินเทอร์เฟซของตนเองเท่านั้น หากต้องการกำหนดวิธีใดวิธีหนึ่งต่อไปนี้
และจัดการเหตุการณ์ ให้ใช้การติดตั้งอินเทอร์เฟซที่ซ้อนกันในกิจกรรม หรือกำหนดเป็นคลาสที่ไม่ระบุชื่อ
จากนั้นส่งอินสแตนซ์ของการติดตั้งใช้งาน
ไปยังเมธอด View.set...Listener() ที่เกี่ยวข้อง (เช่น เรียกใช้
และส่งการติดตั้งใช้งาน setOnClickListener()OnClickListener ไปให้)
ตัวอย่างด้านล่างแสดงวิธีลงทะเบียนเครื่องมือฟังการคลิกสำหรับปุ่ม
Kotlin
protected void onCreate(savedValues: Bundle) { ... val button: Button = findViewById(R.id.corky) // Register the onClick listener with the implementation above button.setOnClickListener { view -> // do something when the button is clicked } ... }
Java
// Create an anonymous implementation of OnClickListener private OnClickListener corkyListener = new OnClickListener() { public void onClick(View v) { // do something when the button is clicked } }; protected void onCreate(Bundle savedValues) { ... // Capture our button from layout Button button = (Button)findViewById(R.id.corky); // Register the onClick listener with the implementation above button.setOnClickListener(corkyListener); ... }
นอกจากนี้ คุณยังอาจพบว่าการใช้ OnClickListener เป็นส่วนหนึ่งของกิจกรรมนั้นสะดวกกว่าด้วย ซึ่งจะช่วยหลีกเลี่ยงการโหลดคลาสและการจัดสรรออบเจ็กต์เพิ่มเติม เช่น
Kotlin
class ExampleActivity : Activity(), OnClickListener { protected fun onCreate(savedValues: Bundle) { val button: Button = findViewById(R.id.corky) button.setOnClickListener(this) } // Implement the OnClickListener callback fun onClick(v: View) { // do something when the button is clicked } }
Java
public class ExampleActivity extends Activity implements OnClickListener { protected void onCreate(Bundle savedValues) { ... Button button = (Button)findViewById(R.id.corky); button.setOnClickListener(this); } // Implement the OnClickListener callback public void onClick(View v) { // do something when the button is clicked } ... }
โปรดทราบว่า Callback onClick() ในตัวอย่างข้างต้นไม่มีค่าที่ส่งคืน แต่เมธอด Listener เหตุการณ์อื่นๆ บางรายการต้องส่งคืนบูลีน เหตุผล
ขึ้นอยู่กับเหตุการณ์ สำหรับบางกรณีที่อาจเกิดขึ้นได้ มีสาเหตุต่อไปนี้
- ฟังก์ชันนี้จะแสดงผลบูลีนเพื่อระบุว่าคุณใช้เหตุการณ์แล้วหรือไม่ และไม่ควรส่งต่อ กล่าวคือ ให้ส่งคืน true เพื่อระบุว่าคุณได้จัดการเหตุการณ์แล้วและควรหยุดที่นี่ ส่งคืน false หากคุณยังไม่ได้จัดการและ/หรือเหตุการณ์ควรดำเนินการต่อไปยัง เครื่องมือฟังการคลิกอื่นๆonLongClick()- ฟังก์ชันนี้จะแสดงผลบูลีนเพื่อระบุว่าคุณใช้เหตุการณ์แล้วหรือไม่ และไม่ควรส่งต่อ กล่าวคือ ให้แสดงผล true เพื่อระบุว่าคุณได้จัดการเหตุการณ์แล้วและควรหยุดที่นี่ แสดงผล false หากคุณยังไม่ได้จัดการและ/หรือเหตุการณ์ควรดำเนินการต่อไปยัง ผู้ฟังที่ใช้คีย์อื่นๆonKey()- ฟังก์ชันนี้จะแสดงผลบูลีนเพื่อระบุว่า Listener ใช้เหตุการณ์นี้หรือไม่ สิ่งสำคัญคือ เหตุการณ์นี้สามารถมีการดำเนินการหลายอย่างที่เกิดขึ้นตามกัน ดังนั้น หากคุณส่งคืน false เมื่อได้รับเหตุการณ์การดำเนินการลง คุณจะระบุว่าคุณไม่ได้ใช้เหตุการณ์และไม่สนใจการดำเนินการที่ตามมาในเหตุการณ์นี้ด้วย ดังนั้น ระบบจะไม่เรียกใช้การดำเนินการอื่นๆ ภายในเหตุการณ์ เช่น ท่าทางสัมผัสด้วยนิ้ว หรือเหตุการณ์การดำเนินการขึ้นในท้ายที่สุดonTouch()
โปรดทราบว่าระบบจะส่งเหตุการณ์คีย์ฮาร์ดแวร์ไปยังมุมมองที่โฟกัสอยู่เสมอ โดยจะส่งจากด้านบนสุด
ของลำดับชั้นการแสดงผล แล้วลงไปเรื่อยๆ จนกว่าจะถึงปลายทางที่เหมาะสม หาก View (หรือ View ย่อย) ของคุณ
มีโฟกัสอยู่ในขณะนี้ คุณจะเห็นเหตุการณ์ที่ส่งผ่านได้ คุณยังรับเหตุการณ์ทั้งหมดภายในกิจกรรมด้วย dispatchKeyEvent()
และ onKeyDown() ได้ด้วย ซึ่งเป็นอีกทางเลือกหนึ่งนอกเหนือจากการบันทึกเหตุการณ์สําคัญผ่านมุมมองonKeyUp()
นอกจากนี้ เมื่อคิดถึงการป้อนข้อความสำหรับแอปพลิเคชัน โปรดทราบว่าอุปกรณ์หลายเครื่องมีเฉพาะวิธีการป้อนข้อมูลด้วยซอฟต์แวร์
วิธีการดังกล่าวไม่จำเป็นต้องใช้คีย์ บางวิธีอาจใช้การป้อนข้อมูลด้วยเสียง การเขียนด้วยลายมือ และอื่นๆ แม้ว่าวิธีการป้อนข้อมูลจะมีอินเทอร์เฟซที่คล้ายกับแป้นพิมพ์ แต่โดยทั่วไปแล้วจะไม่ทริกเกอร์เหตุการณ์ในตระกูล คุณไม่ควรสร้าง UI ที่ต้องกดปุ่มที่เฉพาะเจาะจงเพื่อควบคุม เว้นแต่คุณต้องการจำกัดแอปพลิเคชันให้ใช้ได้กับอุปกรณ์ที่มีคีย์บอร์ดฮาร์ดแวร์เท่านั้น โดยเฉพาะอย่างยิ่ง อย่าใช้วิธีการเหล่านี้เพื่อตรวจสอบความถูกต้องของอินพุตเมื่อผู้ใช้กดปุ่ม
Return แต่ให้ใช้การดำเนินการ เช่น onKeyDown()IME_ACTION_DONE เพื่อส่งสัญญาณไปยัง
วิธีการป้อนข้อมูลว่าแอปพลิเคชันของคุณคาดหวังให้ตอบสนองอย่างไร เพื่อให้วิธีการป้อนข้อมูลเปลี่ยน UI ในลักษณะที่มีความหมายได้ อย่าคาดเดา
เกี่ยวกับวิธีการป้อนข้อมูลของซอฟต์แวร์ควรทำงานอย่างไร และเพียงแค่เชื่อมั่นว่าซอฟต์แวร์จะจัดหาข้อความที่จัดรูปแบบแล้วให้กับแอปพลิเคชันของคุณ
หมายเหตุ: Android จะเรียกตัวแฮนเดิลเหตุการณ์ก่อน แล้วจึงเรียกตัวแฮนเดิลเริ่มต้นที่เหมาะสม จากคำจำกัดความของคลาสเป็นอันดับที่ 2 ดังนั้น การส่งคืนค่า true จาก Listener เหตุการณ์เหล่านี้จะหยุด การเผยแพร่เหตุการณ์ไปยัง Listener เหตุการณ์อื่นๆ และจะบล็อกการเรียกกลับไปยัง ตัวแฮนเดิลเหตุการณ์เริ่มต้นใน View ด้วย ดังนั้นโปรดตรวจสอบว่าคุณต้องการสิ้นสุดเหตุการณ์เมื่อส่งคืน true
เครื่องจัดการเหตุการณ์
หากสร้างคอมโพเนนต์ที่กำหนดเองจาก View คุณจะกำหนดเมธอดเรียกกลับหลายรายการ ที่ใช้เป็นตัวแฮนเดิลเหตุการณ์เริ่มต้นได้ ในเอกสารเกี่ยวกับคอมโพเนนต์ View ที่กำหนดเอง คุณจะได้เรียนรู้การเรียกกลับที่ใช้กันโดยทั่วไปสำหรับการจัดการเหตุการณ์ ซึ่งรวมถึง
- เรียกใช้เมื่อเกิดเหตุการณ์สำคัญใหม่onKeyDown(int, KeyEvent)- เรียกใช้เมื่อเกิดเหตุการณ์กดปุ่มonKeyUp(int, KeyEvent)- เรียกใช้เมื่อเกิดเหตุการณ์การเคลื่อนที่ของแทร็กบอลonTrackballEvent(MotionEvent)- เรียกใช้เมื่อเกิดเหตุการณ์การเคลื่อนไหวบนหน้าจอสัมผัสonTouchEvent(MotionEvent)- เรียกใช้เมื่อมุมมองได้รับหรือสูญเสียโฟกัสonFocusChanged(boolean, int, Rect)
นอกจากนี้ ยังมีอีก 2 วิธีที่คุณควรทราบ ซึ่งไม่ได้เป็นส่วนหนึ่งของคลาส View แต่สามารถส่งผลโดยตรงต่อวิธีที่คุณใช้จัดการเหตุการณ์ได้ ดังนั้น เมื่อจัดการเหตุการณ์ที่ซับซ้อนมากขึ้นภายในเลย์เอาต์ ให้พิจารณาวิธีการอื่นๆ ดังนี้
- ช่วยให้Activity.dispatchTouchEvent(MotionEvent)Activityสกัดกั้นเหตุการณ์สัมผัสทั้งหมดก่อนที่จะส่งไปยังหน้าต่าง- ช่วยให้ViewGroup.onInterceptTouchEvent(MotionEvent)ViewGroupดูเหตุการณ์ได้เมื่อมีการส่งไปยัง View ย่อย- Call this upon a parent View to indicate that it should not intercept touch events withViewParent.requestDisallowInterceptTouchEvent(boolean).onInterceptTouchEvent(MotionEvent)
โหมดสัมผัส
เมื่อผู้ใช้ไปยังส่วนต่างๆ ของอินเทอร์เฟซผู้ใช้ด้วยปุ่มทิศทางหรือแทร็กบอล คุณจะต้อง ให้ความสำคัญกับรายการที่ดำเนินการได้ (เช่น ปุ่ม) เพื่อให้ผู้ใช้เห็น ว่ารายการใดจะรับอินพุต อย่างไรก็ตาม หากอุปกรณ์มีความสามารถในการสัมผัสและผู้ใช้เริ่มโต้ตอบกับอินเทอร์เฟซด้วยการสัมผัส ก็ไม่จำเป็นต้องไฮไลต์รายการหรือโฟกัสที่ View ใด View หนึ่งอีกต่อไป จึงมีโหมด สำหรับการโต้ตอบที่ชื่อว่า "โหมดแตะ"
สำหรับอุปกรณ์ที่รองรับการสัมผัส เมื่อผู้ใช้สัมผัสหน้าจอ อุปกรณ์
จะเข้าสู่โหมดสัมผัส นับจากนี้ไป เฉพาะมุมมองที่
isFocusableInTouchMode()เป็นจริงเท่านั้นที่จะโฟกัสได้ เช่น วิดเจ็ตการแก้ไขข้อความ
มุมมองอื่นๆ ที่แตะได้ เช่น ปุ่ม จะไม่โฟกัสเมื่อแตะ แต่จะเรียกใช้เครื่องมือฟังการคลิกเมื่อกด
ทุกครั้งที่ผู้ใช้กดปุ่มลูกศรหรือเลื่อนด้วยแทร็กบอล อุปกรณ์จะ ออกจากโหมดสัมผัสและค้นหามุมมองที่จะโฟกัส ตอนนี้ผู้ใช้สามารถกลับมาโต้ตอบ กับอินเทอร์เฟซผู้ใช้ได้โดยไม่ต้องสัมผัสหน้าจอ
ระบบจะคงสถานะโหมดสัมผัสไว้ตลอดทั้งระบบ (หน้าต่างและกิจกรรมทั้งหมด)
หากต้องการค้นหาสถานะปัจจุบัน คุณสามารถเรียกใช้
isInTouchMode() เพื่อดูว่าอุปกรณ์อยู่ในโหมดสัมผัสหรือไม่
การจัดการโฟกัส
เฟรมเวิร์กจะจัดการการเคลื่อนไหวโฟกัสตามปกติเพื่อตอบสนองต่อข้อมูลจากผู้ใช้
ซึ่งรวมถึงการเปลี่ยนโฟกัสเมื่อมีการนำมุมมองออกหรือซ่อนมุมมอง หรือเมื่อมีมุมมองใหม่
พร้อมใช้งาน ยอดดูแสดงให้เห็นถึงความเต็มใจที่จะมุ่งเน้น
ผ่านวิธีนี้ หากต้องการเปลี่ยนว่า View จะรับโฟกัสได้หรือไม่ ให้เรียกใช้ isFocusable() เมื่ออยู่ในโหมดสัมผัส
คุณอาจสอบถามว่า View อนุญาตให้โฟกัสด้วย setFocusable() หรือไม่
คุณเปลี่ยนการตั้งค่านี้ได้ด้วย isFocusableInTouchMode()setFocusableInTouchMode()
ในอุปกรณ์ที่ใช้ Android 9 (ระดับ API 28) ขึ้นไป กิจกรรมจะไม่กำหนดโฟกัสเริ่มต้น แต่คุณต้องขอโฟกัสเริ่มต้นอย่างชัดเจนหากต้องการ
การเคลื่อนไหวของโฟกัสจะอิงตามอัลกอริทึมที่ค้นหาองค์ประกอบที่ใกล้ที่สุดใน ทิศทางที่กำหนด ในบางกรณีซึ่งพบได้ไม่บ่อยนัก อัลกอริทึมเริ่มต้นอาจไม่ตรงกับลักษณะการทำงานที่นักพัฒนาแอปต้องการ ในกรณีเหล่านี้ คุณสามารถระบุการลบล้างที่ชัดเจนด้วยแอตทริบิวต์ XML ต่อไปนี้ในไฟล์เลย์เอาต์ nextFocusDown, nextFocusLeft, nextFocusRight และ nextFocusUp เพิ่มแอตทริบิวต์ต่อไปนี้อย่างใดอย่างหนึ่งไปยัง View from ซึ่ง โฟกัสกำลังออกจาก กำหนดค่าของแอตทริบิวต์ให้เป็นรหัสของ View to ซึ่งควรให้โฟกัส เช่น
<LinearLayout android:orientation="vertical" ... > <Button android:id="@+id/top" android:nextFocusUp="@+id/bottom" ... /> <Button android:id="@+id/bottom" android:nextFocusDown="@+id/top" ... /> </LinearLayout>
โดยปกติแล้ว ในเลย์เอาต์แนวตั้งนี้ การไปยังปุ่มแรกจากด้านบนจะไม่ไปไหน และการไปยังปุ่มที่ 2 จากด้านล่างก็เช่นกัน ตอนนี้ปุ่มด้านบนได้กำหนดปุ่มด้านล่างเป็น nextFocusUp (และในทางกลับกัน) โฟกัสการไปยังส่วนต่างๆ จะวนจากบนลงล่างและจากล่างขึ้นบน
หากต้องการประกาศ View เป็นโฟกัสได้ใน UI (เมื่อปกติแล้วจะโฟกัสไม่ได้)
ให้เพิ่มแอตทริบิวต์ XML android:focusable ลงใน View ในการประกาศเลย์เอาต์
ตั้งค่า true นอกจากนี้ คุณยังประกาศ View
เป็นโฟกัสได้ขณะอยู่ในโหมดสัมผัสด้วย android:focusableInTouchMode
หากต้องการขอให้มุมมองใดมุมมองหนึ่งเป็นมุมมองหลัก ให้เรียกใช้ requestFocus()
หากต้องการฟังเหตุการณ์โฟกัส (รับการแจ้งเตือนเมื่อ View ได้รับหรือสูญเสียโฟกัส) ให้ใช้
ตามที่อธิบายไว้ในส่วนเครื่องมือฟังเหตุการณ์onFocusChange()