ภาพรวมของเหตุการณ์อินพุต

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

ใน 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 เหตุการณ์อื่นๆ บางรายการต้องส่งคืนบูลีน เหตุผล ขึ้นอยู่กับเหตุการณ์ สำหรับบางกรณีที่อาจเกิดขึ้นได้ มีสาเหตุต่อไปนี้

  • onLongClick() - ฟังก์ชันนี้จะแสดงผลบูลีนเพื่อระบุว่าคุณใช้เหตุการณ์แล้วหรือไม่ และไม่ควรส่งต่อ กล่าวคือ ให้ส่งคืน true เพื่อระบุว่าคุณได้จัดการเหตุการณ์แล้วและควรหยุดที่นี่ ส่งคืน false หากคุณยังไม่ได้จัดการและ/หรือเหตุการณ์ควรดำเนินการต่อไปยัง เครื่องมือฟังการคลิกอื่นๆ
  • onKey() - ฟังก์ชันนี้จะแสดงผลบูลีนเพื่อระบุว่าคุณใช้เหตุการณ์แล้วหรือไม่ และไม่ควรส่งต่อ กล่าวคือ ให้แสดงผล true เพื่อระบุว่าคุณได้จัดการเหตุการณ์แล้วและควรหยุดที่นี่ แสดงผล false หากคุณยังไม่ได้จัดการและ/หรือเหตุการณ์ควรดำเนินการต่อไปยัง ผู้ฟังที่ใช้คีย์อื่นๆ
  • onTouch() - ฟังก์ชันนี้จะแสดงผลบูลีนเพื่อระบุว่า Listener ใช้เหตุการณ์นี้หรือไม่ สิ่งสำคัญคือ เหตุการณ์นี้สามารถมีการดำเนินการหลายอย่างที่เกิดขึ้นตามกัน ดังนั้น หากคุณส่งคืน false เมื่อได้รับเหตุการณ์การดำเนินการลง คุณจะระบุว่าคุณไม่ได้ใช้เหตุการณ์และไม่สนใจการดำเนินการที่ตามมาในเหตุการณ์นี้ด้วย ดังนั้น ระบบจะไม่เรียกใช้การดำเนินการอื่นๆ ภายในเหตุการณ์ เช่น ท่าทางสัมผัสด้วยนิ้ว หรือเหตุการณ์การดำเนินการขึ้นในท้ายที่สุด

โปรดทราบว่าระบบจะส่งเหตุการณ์คีย์ฮาร์ดแวร์ไปยังมุมมองที่โฟกัสอยู่เสมอ โดยจะส่งจากด้านบนสุด ของลำดับชั้นการแสดงผล แล้วลงไปเรื่อยๆ จนกว่าจะถึงปลายทางที่เหมาะสม หาก View (หรือ View ย่อย) ของคุณ มีโฟกัสอยู่ในขณะนี้ คุณจะเห็นเหตุการณ์ที่ส่งผ่านdispatchKeyEvent()ได้ คุณยังรับเหตุการณ์ทั้งหมดภายในกิจกรรมด้วย onKeyDown() และ onKeyUp() ได้ด้วย ซึ่งเป็นอีกทางเลือกหนึ่งนอกเหนือจากการบันทึกเหตุการณ์สําคัญผ่านมุมมอง

นอกจากนี้ เมื่อคิดถึงการป้อนข้อความสำหรับแอปพลิเคชัน โปรดทราบว่าอุปกรณ์หลายเครื่องมีเฉพาะวิธีการป้อนข้อมูลด้วยซอฟต์แวร์ วิธีการดังกล่าวไม่จำเป็นต้องใช้คีย์ บางวิธีอาจใช้การป้อนข้อมูลด้วยเสียง การเขียนด้วยลายมือ และอื่นๆ แม้ว่าวิธีการป้อนข้อมูลจะมีอินเทอร์เฟซที่คล้ายกับแป้นพิมพ์ แต่โดยทั่วไปแล้วจะไม่ทริกเกอร์เหตุการณ์ในตระกูล onKeyDown() คุณไม่ควรสร้าง UI ที่ต้องกดปุ่มที่เฉพาะเจาะจงเพื่อควบคุม เว้นแต่คุณต้องการจำกัดแอปพลิเคชันให้ใช้ได้กับอุปกรณ์ที่มีคีย์บอร์ดฮาร์ดแวร์เท่านั้น โดยเฉพาะอย่างยิ่ง อย่าใช้วิธีการเหล่านี้เพื่อตรวจสอบความถูกต้องของอินพุตเมื่อผู้ใช้กดปุ่ม Return แต่ให้ใช้การดำเนินการ เช่น 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 แต่สามารถส่งผลโดยตรงต่อวิธีที่คุณใช้จัดการเหตุการณ์ได้ ดังนั้น เมื่อจัดการเหตุการณ์ที่ซับซ้อนมากขึ้นภายในเลย์เอาต์ ให้พิจารณาวิธีการอื่นๆ ดังนี้

โหมดสัมผัส

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

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

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

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

การจัดการโฟกัส

เฟรมเวิร์กจะจัดการการเคลื่อนไหวโฟกัสตามปกติเพื่อตอบสนองต่อข้อมูลจากผู้ใช้ ซึ่งรวมถึงการเปลี่ยนโฟกัสเมื่อมีการนำมุมมองออกหรือซ่อนมุมมอง หรือเมื่อมีมุมมองใหม่ พร้อมใช้งาน ยอดดูแสดงให้เห็นถึงความเต็มใจที่จะมุ่งเน้น ผ่านisFocusable()วิธีนี้ หากต้องการเปลี่ยนว่า View จะรับโฟกัสได้หรือไม่ ให้เรียกใช้ setFocusable() เมื่ออยู่ในโหมดสัมผัส คุณอาจสอบถามว่า View อนุญาตให้โฟกัสด้วย 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() ตามที่อธิบายไว้ในส่วนเครื่องมือฟังเหตุการณ์