Membuat tampilan kustom lebih mudah diakses

Jika aplikasi Anda memerlukan komponen tampilan kustom, Anda harus membuat tampilan lebih mudah diakses. Langkah-langkah berikut dapat meningkatkan aksesibilitas tampilan kustom Anda, seperti yang dijelaskan di halaman ini:

  • Menangani klik pengontrol arah.
  • Mengimplementasikan metode API aksesibilitas.
  • Mengirim objek AccessibilityEvent khusus untuk tampilan kustom Anda
  • Mengisi AccessibilityEvent dan AccessibilityNodeInfo untuk tampilan Anda

Menangani klik pengontrol arah

Pada sebagian besar perangkat, mengklik tampilan menggunakan pengontrol arah akan mengirimkan KeyEvent dengan KEYCODE_DPAD_CENTER ke tampilan yang saat ini menjadi fokus. Semua tampilan Android standar sudah menangani KEYCODE_DPAD_CENTER dengan tepat. Saat membuat kontrol View kustom, pastikan peristiwa ini memiliki efek yang sama dengan menyentuh tampilan di layar sentuh.

Kontrol kustom Anda harus memperlakukan peristiwa KEYCODE_ENTER sama dengan KEYCODE_DPAD_CENTER. Dengan begitu, interaksi dengan keyboard lengkap menjadi lebih mudah bagi pengguna.

Mengimplementasikan metode API aksesibilitas

Peristiwa aksesibilitas adalah pesan tentang interaksi pengguna dengan komponen antarmuka visual aplikasi Anda. Pesan ini ditangani oleh layanan aksesibilitas, yang menggunakan informasi dalam peristiwa ini untuk menghasilkan masukan dan perintah tambahan. Metode aksesibilitas adalah bagian dari class View dan View.AccessibilityDelegate . Metode tersebut adalah sebagai berikut:

dispatchPopulateAccessibilityEvent()
Sistem memanggil metode ini saat tampilan kustom Anda menghasilkan peristiwa aksesibilitas. Implementasi default dari metode ini akan memanggil onPopulateAccessibilityEvent() untuk tampilan ini, lalu metode dispatchPopulateAccessibilityEvent() untuk setiap turunan tampilan ini.
onInitializeAccessibilityEvent()
Sistem memanggil metode ini untuk mendapatkan informasi tambahan tentang status tampilan di luar konten teks. Jika tampilan kustom Anda memberikan kontrol interaktif di luar TextView atau Button yang sederhana, ganti metode ini dan tetapkan informasi tambahan tentang tampilan Anda—seperti jenis kolom sandi, jenis kotak centang, atau status yang memberikan interaksi atau masukan kepada pengguna mengenai peristiwa tersebut—menggunakan metode ini. Jika Anda mengganti metode ini, panggil implementasi supernya dan hanya ubah properti yang tidak ditetapkan oleh class super.
onInitializeAccessibilityNodeInfo()
Metode ini menyediakan layanan aksesibilitas dengan informasi tentang status tampilan. Implementasi View default memiliki kumpulan standar properti tampilan. Namun, jika tampilan kustom Anda memberikan kontrol interaktif di luar TextView atau Button yang sederhana, Anda harus mengganti metode ini dan menetapkan informasi tambahan tentang tampilan Anda ke objek AccessibilityNodeInfo yang ditangani oleh metode ini.
onPopulateAccessibilityEvent()
Metode ini menetapkan perintah teks yang diucapkan dari AccessibilityEvent untuk tampilan Anda. Ini juga dipanggil jika tampilan adalah turunan dari tampilan yang menghasilkan peristiwa aksesibilitas.
onRequestSendAccessibilityEvent()
Sistem memanggil metode ini saat turunan tampilan Anda menghasilkan AccessibilityEvent. Langkah ini memungkinkan tampilan induk mengubah peristiwa aksesibilitas dengan informasi tambahan. Terapkan metode ini hanya jika tampilan kustom Anda dapat memiliki tampilan turunan dan jika tampilan induk dapat memberikan informasi konteks ke peristiwa aksesibilitas yang berguna untuk layanan aksesibilitas.
sendAccessibilityEvent()
Sistem memanggil metode ini saat pengguna mengambil tindakan pada tampilan. Peristiwa tersebut diklasifikasikan dengan jenis tindakan pengguna seperti TYPE_VIEW_CLICKED. Secara umum, Anda harus mengirim AccessibilityEvent kapan pun konten tampilan kustom Anda berubah.
sendAccessibilityEventUnchecked()
Metode ini digunakan ketika kode panggilan harus mengontrol secara langsung pemeriksaan aksesibilitas yang diaktifkan pada perangkat (AccessibilityManager.isEnabled()). Jika Anda mengimplementasikan metode ini, jalankan panggilan seolah-olah aksesibilitas diaktifkan, terlepas dari setelan sistemnya. Anda biasanya tidak perlu mengimplementasikan metode ini untuk tampilan kustom.

Untuk mendukung aksesibilitas, ganti dan implementasikan metode aksesibilitas sebelumnya secara langsung di class tampilan kustom Anda.

Setidaknya, implementasikan metode aksesibilitas berikut untuk class tampilan kustom Anda:

  • dispatchPopulateAccessibilityEvent()
  • onInitializeAccessibilityEvent()
  • onInitializeAccessibilityNodeInfo()
  • onPopulateAccessibilityEvent()

Untuk mengetahui informasi selengkapnya tentang mengimplementasikan metode tersebut, lihat bagian tentang mengisi peristiwa aksesibilitas.

Mengirim peristiwa aksesibilitas

Tergantung pada spesifikasi tampilan kustom, Anda mungkin perlu mengirim objek AccessibilityEvent pada waktu yang berbeda atau untuk peristiwa yang tidak ditangani oleh implementasi default. Class View akan memberikan implementasi default untuk jenis peristiwa berikut:

Secara umum, Anda harus mengirim AccessibilityEvent kapan pun konten tampilan kustom Anda berubah. Misalnya, jika Anda menerapkan panel penggeser kustom yang memungkinkan pengguna memilih nilai numerik dengan menekan tombol panah kiri atau kanan, tampilan kustom Anda harus memunculkan peristiwa TYPE_VIEW_TEXT_CHANGED setiap kali perubahan nilai penggeser. Contoh kode berikut menunjukkan penggunaan metode sendAccessibilityEvent() untuk melaporkan peristiwa ini.

Kotlin

override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean {
    return when(keyCode) {
        KeyEvent.KEYCODE_DPAD_LEFT -> {
            currentValue--
            sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED)
            true
        }
        ...
    }
}

Java

@Override
public boolean onKeyUp (int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) {
        currentValue--;
        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);
        return true;
    }
    ...
}

Mengisi peristiwa aksesibilitas

Setiap AccessibilityEvent memiliki kumpulan properti yang diperlukan yang menggambarkan status tampilan saat ini. Properti tersebut mencakup hal-hal seperti nama class, deskripsi konten, dan status yang dicentang dari suatu tampilan. Properti khusus yang diperlukan untuk setiap jenis peristiwa dijelaskan dalam dokumentasi referensi AccessibilityEvent.

Implementasi View akan memberikan nilai default untuk properti tersebut. Banyak dari nilai tersebut, termasuk nama class dan stempel waktu peristiwa, disediakan secara otomatis. Jika membuat komponen tampilan kustom, Anda harus memberikan beberapa informasi tentang konten dan karakteristik tampilan. Informasi ini dapat menyederhanakan label tombol dan dapat menyertakan informasi status tambahan yang ingin Anda tambahkan ke peristiwa.

Gunakan metode onPopulateAccessibilityEvent() dan onInitializeAccessibilityEvent() untuk mengisi atau mengubah informasi dalam AccessibilityEvent. Gunakan metode onPopulateAccessibilityEvent() secara khusus untuk menambahkan atau mengubah konten teks peristiwa, yang diubah menjadi perintah suara oleh layanan aksesibilitas seperti TalkBack. Gunakan metode onInitializeAccessibilityEvent() untuk mengisi informasi tambahan tentang peristiwa, seperti status pemilihan tampilan.

Selain itu, implementasikan metode onInitializeAccessibilityNodeInfo(). Layanan aksesibilitas menggunakan objek AccessibilityNodeInfo yang diisi oleh metode ini untuk menyelidiki hierarki tampilan yang menghasilkan peristiwa aksesibilitas setelah diterima dan memberikan masukan yang sesuai kepada pengguna.

Contoh kode berikut menunjukkan cara mengganti ketiga metode ini dalam tampilan Anda:

Kotlin

override fun onPopulateAccessibilityEvent(event: AccessibilityEvent?) {
    super.onPopulateAccessibilityEvent(event)
    // Call the super implementation to populate its text for the
    // event. Then, add text not present in a super class.
    // You typically only need to add the text for the custom view.
    if (text?.isNotEmpty() == true) {
        event?.text?.add(text)
    }
}

override fun onInitializeAccessibilityEvent(event: AccessibilityEvent?) {
    super.onInitializeAccessibilityEvent(event)
    // Call the super implementation to let super classes
    // set appropriate event properties. Then, add the new checked
    // property that is not supported by a super class.
    event?.isChecked = isChecked()
}

override fun onInitializeAccessibilityNodeInfo(info: AccessibilityNodeInfo?) {
    super.onInitializeAccessibilityNodeInfo(info)
    // Call the super implementation to let super classes set
    // appropriate info properties. Then, add the checkable and checked
    // properties that are not supported by a super class.
    info?.isCheckable = true
    info?.isChecked = isChecked()
    // You typically only need to add the text for the custom view.
    if (text?.isNotEmpty() == true) {
        info?.text = text
    }
}

Java

@Override
public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
    super.onPopulateAccessibilityEvent(event);
    // Call the super implementation to populate its text for the
    // event. Then, add the text not present in a super class.
    // You typically only need to add the text for the custom view.
    CharSequence text = getText();
    if (!TextUtils.isEmpty(text)) {
        event.getText().add(text);
    }
}

@Override
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
    super.onInitializeAccessibilityEvent(event);
    // Call the super implementation to let super classes
    // set appropriate event properties. Then, add the new checked
    // property that is not supported by a super class.
    event.setChecked(isChecked());
}

@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
    super.onInitializeAccessibilityNodeInfo(info);
    // Call the super implementation to let super classes set
    // appropriate info properties. Then, add the checkable and checked
    // properties that are not supported by a super class.
    info.setCheckable(true);
    info.setChecked(isChecked());
    // You typically only need to add the text for the custom view.
    CharSequence text = getText();
    if (!TextUtils.isEmpty(text)) {
        info.setText(text);
    }
}

Anda dapat mengimplementasikan metode ini secara langsung dalam class tampilan kustom.

Memberikan konteks aksesibilitas yang disesuaikan

Layanan aksesibilitas dapat memeriksa hierarki tampilan yang berisi komponen antarmuka pengguna yang menghasilkan peristiwa aksesibilitas. Hal ini memungkinkan layanan aksesibilitas memberikan informasi kontekstual yang lebih lengkap untuk membantu pengguna.

Ada kasus saat layanan aksesibilitas tidak bisa mendapatkan informasi yang memadai dari hierarki tampilan. Contoh hal ini adalah kontrol antarmuka kustom yang memiliki dua atau beberapa area yang dapat diklik secara terpisah, seperti kontrol kalender. Dalam hal ini, layanan tidak bisa mendapatkan informasi yang memadai karena subbagian yang dapat diklik bukan bagian dari hierarki tampilan.

Gambar 1. Tampilan kalender kustom dengan elemen hari yang dapat dipilih.

Pada contoh di gambar 1, seluruh kalender diimplementasikan sebagai satu tampilan, sehingga layanan aksesibilitas tidak menerima cukup informasi tentang konten tampilan dan pilihan pengguna dalam tampilan kecuali jika developer menyediakan informasi tambahan. Misalnya, jika pengguna mengklik pada hari yang berisi 17, framework aksesibilitas hanya akan menerima informasi deskripsi untuk seluruh kontrol kalender. Dalam hal ini, layanan aksesibilitas TalkBack mengumumkan "Kalender" atau "Kalender April", dan pengguna tidak tahu hari apa yang dipilih.

Untuk memberikan informasi konteks yang memadai bagi layanan aksesibilitas dalam situasi seperti ini, framework memberikan cara guna menentukan hierarki tampilan virtual. Hierarki tampilan virtual adalah cara bagi developer aplikasi untuk memberikan hierarki tampilan tambahan ke layanan aksesibilitas yang lebih cocok dengan informasi di layar. Pendekatan ini memungkinkan layanan aksesibilitas memberikan informasi konteks yang lebih berguna kepada pengguna.

Situasi lain ketika hierarki tampilan virtual mungkin diperlukan adalah antarmuka pengguna yang berisi kumpulan kontrol View yang memiliki beberapa fungsi yang terkait erat. Di sana, tindakan pada satu kontrol akan memengaruhi konten dari satu atau beberapa elemen, seperti pemilih angka dengan tombol atas dan bawah yang terpisah. Dalam hal ini, layanan aksesibilitas tidak bisa mendapatkan informasi yang memadai karena tindakan pada satu kontrol akan mengubah konten di konten lain, dan hubungan kontrol tersebut mungkin tidak terlihat layanan.

Untuk menangani situasi ini, kelompokkan kontrol terkait dengan tampilan yang memuatnya dan berikan hierarki tampilan virtual dari penampung ini untuk menggambarkan dengan jelas informasi dan perilaku yang diberikan oleh kontrol tersebut.

Agar dapat memberikan hierarki tampilan virtual untuk suatu tampilan, ganti metode getAccessibilityNodeProvider() dalam tampilan kustom Anda atau kelompok tampilan dan tampilkan implementasi AccessibilityNodeProvider. Anda dapat mengimplementasikan hierarki tampilan virtual dengan menggunakan Support Library dengan metode ViewCompat.getAccessibilityNodeProvider() dan memberikan implementasi dengan AccessibilityNodeProviderCompat.

Untuk menyederhanakan tugas penyediaan informasi ke layanan aksesibilitas dan mengelola fokus aksesibilitas, Anda dapat mengimplementasikan ExploreByTouchHelper. Class ini menyediakan AccessibilityNodeProviderCompat dan dapat dilampirkan sebagai AccessibilityDelegateCompat tampilan dengan memanggil setAccessibilityDelegate. Untuk contoh, lihat ExploreByTouchHelperActivity. ExploreByTouchHelper juga digunakan oleh widget framework seperti CalendarView, melalui tampilan turunannya SimpleMonthView.

Menangani peristiwa sentuh kustom

Kontrol tampilan kustom mungkin memerlukan perilaku peristiwa sentuh non-standar, sebagaimana didemonstrasikan dalam contoh berikut.

Menentukan tindakan berbasis klik

Jika widget menggunakan OnClickListener atau OnLongClickListener antarmuka, sistem akan menangani tindakan ACTION_CLICK dan ACTION_LONG_CLICK untuk Anda. Jika aplikasi Anda menggunakan widget yang lebih disesuaikan yang mengandalkan antarmuka OnTouchListener, tentukan pengendali kustom untuk tindakan aksesibilitas berbasis klik. Untuk melakukannya, panggil metode replaceAccessibilityAction() untuk setiap tindakan, sebagaimana ditunjukkan dalam cuplikan kode berikut:

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    ...

    // Assumes that the widget is designed to select text when tapped, and selects
    // all text when tapped and held. In its strings.xml file, this app sets
    // "select" to "Select" and "select_all" to "Select all".
    ViewCompat.replaceAccessibilityAction(
        binding.textSelectWidget,
        ACTION_CLICK,
        getString(R.string.select)
    ) { view, commandArguments ->
        selectText()
    }

    ViewCompat.replaceAccessibilityAction(
        binding.textSelectWidget,
        ACTION_LONG_CLICK,
        getString(R.string.select_all)
    ) { view, commandArguments ->
        selectAllText()
    }
}

Java

@Override
protected void onCreate(Bundle savedInstanceState) {
    ...

    // Assumes that the widget is designed to select text when tapped, and select
    // all text when tapped and held. In its strings.xml file, this app sets
    // "select" to "Select" and "select_all" to "Select all".
    ViewCompat.replaceAccessibilityAction(
            binding.textSelectWidget,
            ACTION_CLICK,
            getString(R.string.select),
            (view, commandArguments) -> selectText());

    ViewCompat.replaceAccessibilityAction(
            binding.textSelectWidget,
            ACTION_LONG_CLICK,
            getString(R.string.select_all),
            (view, commandArguments) -> selectAllText());
}

Membuat peristiwa klik kustom

Kontrol kustom dapat menggunakan metode pemroses onTouchEvent(MotionEvent) untuk mendeteksi peristiwa ACTION_DOWN dan ACTION_UP serta memicu peristiwa klik khusus. Untuk menjaga kompatibilitas dengan layanan aksesibilitas, kode yang menangani peristiwa klik kustom ini harus melakukan hal berikut:

  1. Menghasilkan AccessibilityEvent yang sesuai untuk tindakan klik yang diinterpretasikan.
  2. Mengaktifkan layanan aksesibilitas untuk menjalankan tindakan klik kustom bagi pengguna yang tidak dapat menggunakan layar sentuh.

Untuk menangani persyaratan ini secara efisien, kode Anda harus mengganti metode performClick(), yang harus memanggil implementasi super metode ini, lalu menjalankan tindakan apa pun yang diperlukan oleh peristiwa klik. Saat tindakan klik kustom terdeteksi, kode tersebut kemudian akan memanggil metode performClick() Anda. Contoh kode berikut ini menunjukkan pola tersebut.

Kotlin

class CustomTouchView(context: Context) : View(context) {

    var downTouch = false

    override fun onTouchEvent(event: MotionEvent): Boolean {
        super.onTouchEvent(event)

        // Listening for the down and up touch events.
        return when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                downTouch = true
                true
            }

            MotionEvent.ACTION_UP -> if (downTouch) {
                downTouch = false
                performClick() // Call this method to handle the response and
                // enable accessibility services to
                // perform this action for a user who can't
                // tap the touchscreen.
                true
            } else {
                false
            }

            else -> false  // Return false for other touch events.
        }
    }

    override fun performClick(): Boolean {
        // Calls the super implementation, which generates an AccessibilityEvent
        // and calls the onClick() listener on the view, if any.
        super.performClick()

        // Handle the action for the custom click here.

        return true
    }
}

Java

class CustomTouchView extends View {

    public CustomTouchView(Context context) {
        super(context);
    }

    boolean downTouch = false;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        super.onTouchEvent(event);

        // Listening for the down and up touch events
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                downTouch = true;
                return true;

            case MotionEvent.ACTION_UP:
                if (downTouch) {
                    downTouch = false;
                    performClick(); // Call this method to handle the response and
                                    // enable accessibility services to
                                    // perform this action for a user who can't
                                    // tap the touchscreen.
                    return true;
                }
        }
        return false; // Return false for other touch events.
    }

    @Override
    public boolean performClick() {
        // Calls the super implementation, which generates an AccessibilityEvent
        // and calls the onClick() listener on the view, if any.
        super.performClick();

        // Handle the action for the custom click here.

        return true;
    }
}

Pola sebelumnya membantu memastikan bahwa peristiwa klik kustom kompatibel dengan layanan aksesibilitas menggunakan metode performClick() untuk membuat peristiwa aksesibilitas dan menyediakan titik entri bagi layanan aksesibilitas untuk bertindak atas nama pengguna yang melakukan performa untuk peristiwa klik kustom.