Prinsip-prinsip untuk meningkatkan aksesibilitas aplikasi (Tampilan)

Konsep dan penerapan Jetpack Compose

Untuk membantu pengguna yang membutuhkan aksesibilitas, framework Android memungkinkan Anda membuat layanan aksesibilitas yang dapat menyajikan konten dari aplikasi kepada pengguna dan juga mengoperasikan aplikasi atas nama mereka.

Android menyediakan beberapa layanan aksesibilitas sistem, termasuk:

  • TalkBack: membantu orang-orang yang memiliki gangguan penglihatan atau tunanetra. Aplikasi ini mengumumkan konten melalui suara yang disintesis dan melakukan tindakan pada aplikasi sebagai respons atas gestur pengguna.
  • Tombol Akses: membantu orang-orang yang memiliki gangguan motorik. Fitur ini menandai elemen interaktif dan melakukan tindakan sebagai respons terhadap pengguna yang menekan tombol. Fitur ini memungkinkan pengontrolan perangkat hanya dengan satu atau dua tombol.

Untuk membantu pengguna yang membutuhkan fitur aksesibilitas agar dapat menggunakan aplikasi Anda, aplikasi Anda harus mengikuti praktik terbaik yang dijelaskan di halaman ini, yang disusun berdasarkan panduan sebagaimana tercantum dalam Menjadikan aplikasi lebih mudah diakses.

Elemen label

Penting untuk menyediakan label yang deskriptif dan berguna kepada pengguna di setiap elemen UI interaktif aplikasi Anda. Setiap label harus menjelaskan arti dan tujuan elemen tertentu. Pembaca layar seperti TalkBack dapat membacakan label ini kepada pengguna.

Umumnya, Anda menentukan deskripsi elemen UI dalam file resource tata letak yang berisi elemen tersebut. Biasanya, Anda menambahkan label menggunakan atribut contentDescription, seperti yang dijelaskan dalam panduan membuat aplikasi lebih mudah diakses. Ada beberapa teknik pelabelan lain yang dijelaskan di bagian berikut.

Elemen yang dapat diedit

Saat melabeli elemen yang dapat diedit, seperti objek EditText, sebaiknya tampilkan teks yang memberikan contoh input yang valid dalam elemen itu sendiri, selain menjadikan teks contoh ini tersedia bagi pembaca layar. Dalam situasi ini, Anda dapat menggunakan atribut android:hint, seperti ditunjukkan dalam cuplikan berikut:

<!-- The hint text for en-US locale would be
     "Apartment, suite, or building". -->
<EditText
   android:id="@+id/addressLine2"
   android:hint="@string/aptSuiteBuilding" ... />

Dalam situasi ini, objek View harus memiliki atribut android:labelFor yang disetel ke ID elemen EditText. Untuk mengetahui detail selengkapnya, lihat bagian berikut.

Menyambungkan elemen yang salah satunya mendeskripsikan elemen lainnya

Elemen EditText biasanya memiliki objek View yang sesuai yang menjelaskan apa yang harus dimasukkan pengguna dalam elemen EditText. Anda dapat menunjukkan hubungan ini dengan menyetel atribut android:labelFor objek View.

Contoh pelabelan untuk pasangan elemen tersebut ditunjukkan pada cuplikan berikut:

<!-- Label text for en-US locale would be "Username:" -->
<TextView
   android:id="@+id/usernameLabel" ...
   android:text="@string/username"
   android:labelFor="@+id/usernameEntry" />

<EditText
   android:id="@+id/usernameEntry" ... />

<!-- Label text for en-US locale would be "Password:" -->
<TextView
   android:id="@+id/passwordLabel" ...
   android:text="@string/password
   android:labelFor="@+id/passwordEntry" />

<EditText
   android:id="@+id/passwordEntry"
   android:inputType="textPassword" ... />

Elemen dalam koleksi

Saat menambahkan label ke elemen koleksi, setiap label harus unik. Dengan begitu, layanan aksesibilitas sistem dapat merujuk dengan tepat ke satu elemen di layar saat membacakan label. Hubungan ini memberi tahu pengguna kapan mereka menjelajahi UI atau kapan mereka memindahkan fokus ke elemen yang sudah mereka temukan.

Secara khusus, sertakan teks tambahan atau informasi kontekstual di elemen dalam tata letak yang digunakan kembali—seperti objek RecyclerView—sehingga setiap elemen turunan diidentifikasi secara unik.

Untuk melakukannya, setel deskripsi konten sebagai bagian dari implementasi adaptor Anda, seperti ditunjukkan dalam cuplikan kode berikut:

Kotlin

data class MovieRating(val title: String, val starRating: Integer)

class MyMovieRatingsAdapter(private val myData: Array<MovieRating>):
        RecyclerView.Adapter<MyMovieRatingsAdapter.MyRatingViewHolder>() {

    class MyRatingViewHolder(val ratingView: ImageView) :
            RecyclerView.ViewHolder(ratingView)

    override fun onBindViewHolder(holder: MyRatingViewHolder, position: Int) {
        val ratingData = myData[position]
        holder.ratingView.contentDescription = "Movie ${position}: " +
                "${ratingData.title}, ${ratingData.starRating} stars"
    }
}

Java

public class MovieRating {
    private String title;
    private int starRating;
    // ...
    public String getTitle() { return title; }
    public int getStarRating() { return starRating; }
}

public class MyMovieRatingsAdapter
        extends RecyclerView.Adapter<MyAdapter.MyRatingViewHolder> {
    private MovieRating[] myData;


    public static class MyRatingViewHolder extends RecyclerView.ViewHolder {
        public ImageView ratingView;
        public MyRatingViewHolder(ImageView iv) {
            super(iv);
            ratingView = iv;
        }
    }

    @Override
    public void onBindViewHolder(MyRatingViewHolder holder, int position) {
        MovieRating ratingData = myData[position];
        holder.ratingView.setContentDescription("Movie " + position + ": " +
                ratingData.getTitle() + ", " + ratingData.getStarRating() +
                " stars")
    }
}

Grup konten terkait

Jika aplikasi Anda menampilkan beberapa elemen UI yang membentuk grup secara otomatis, seperti detail lagu atau atribut pesan, atur elemen tersebut dalam penampung, yang umumnya merupakan subclass ViewGroup. Setel atribut android:screenReaderFocusable objek container ke true, dan setiap atribut android:focusable objek inti ke false. Dengan demikian, layanan aksesibilitas dapat menyajikan deskripsi konten elemen inti, satu per satu, dalam satu pembacaan. Dengan menggabungkan elemen terkait ini, pengguna teknologi pendukung dapat menemukan informasi di layar dengan lebih efisien.

Cuplikan berikut berisi bagian konten yang terkait satu sama lain, sehingga elemen penampung, instance ConstraintLayout, memiliki atribut android:screenReaderFocusable yang disetel ke true dan setiap elemen TextView inti memiliki atribut android:focusable yang disetel ke false:

<!-- In response to a single user interaction, accessibility services announce
     both the title and the artist of the song. -->
<ConstraintLayout
    android:id="@+id/song_data_container" ...
    android:screenReaderFocusable="true">

    <TextView
        android:id="@+id/song_title" ...
        android:focusable="false"
        android:text="@string/my_song_title" />
    <TextView
        android:id="@+id/song_artist"
        android:focusable="false"
        android:text="@string/my_songwriter" />
</ConstraintLayout>

Karena layanan aksesibilitas membacakan deskripsi elemen inti dalam satu kalimat, penting untuk membuat setiap deskripsi sesingkat mungkin tetapi tetap menyampaikan maksud elemen tersebut.

Catatan: Secara umum, Anda harus menghindari pembuatan deskripsi konten untuk grup dengan menggabungkan teks elemen turunannya. Tindakan ini membuat deskripsi grup menjadi tidak fleksibel, dan saat teks turunan berubah, deskripsi grup mungkin tidak lagi cocok dengan teks yang terlihat.

Dalam konteks daftar atau petak, pembaca layar dapat menggabungkan teks dari node teks turunan elemen daftar atau petak. Sebaiknya hindari mengubah pengumuman ini.

Grup bertingkat

Jika antarmuka aplikasi Anda menyajikan informasi multidimensi, seperti daftar acara festival harian, gunakan atribut android:screenReaderFocusable pada penampung dalam grup. Skema pelabelan ini memberikan keseimbangan yang baik antara jumlah pembacaan yang diperlukan untuk menemukan konten layar dan durasi setiap pembacaan.

Cuplikan kode berikut menunjukkan satu metode untuk memberi label pada grup di dalam grup yang lebih besar:

<!-- In response to a single user interaction, accessibility services
     announce the events for a single stage only. -->
<ConstraintLayout
    android:id="@+id/festival_event_table" ... >
    <ConstraintLayout
        android:id="@+id/stage_a_event_column"
        android:screenReaderFocusable="true">

        <!-- UI elements that describe the events on Stage A. -->

    </ConstraintLayout>
    <ConstraintLayout
        android:id="@+id/stage_b_event_column"
        android:screenReaderFocusable="true">

        <!-- UI elements that describe the events on Stage B. -->

    </ConstraintLayout>
</ConstraintLayout>

Judul dalam teks

Beberapa aplikasi menggunakan judul untuk meringkas grup teks yang muncul di layar. Jika elemen View tertentu merepresentasikan judul, Anda dapat menunjukkan tujuan judul tersebut untuk layanan aksesibilitas dengan menyetel atribut android:accessibilityHeading elemen ke true.

Pengguna layanan aksesibilitas dapat memilih untuk memilih opsi navigasi antar-judul, bukan antar-paragraf atau antar-kata. Fleksibilitas ini akan meningkatkan pengalaman navigasi teks.

Judul panel aksesibilitas

Di Android 9 (API level 28) dan yang lebih tinggi, Anda dapat memberikan judul yang mudah diakses untuk panel layar. Untuk tujuan aksesibilitas, panel adalah bagian jendela dengan tampilan visual yang mencolok, seperti konten suatu fragmen. Agar layanan aksesibilitas dapat memahami perilaku seperti jendela di panel, berikan judul yang deskriptif pada panel aplikasi Anda. Selanjutnya, layanan aksesibilitas dapat memberikan informasi yang lebih terperinci kepada pengguna saat tampilan atau konten panel berubah.

Untuk menentukan judul panel, gunakan atribut android:accessibilityPaneTitle, seperti ditampilkan dalam cuplikan berikut:

<!-- Accessibility services receive announcements about content changes
     that are scoped to either the "shopping cart view" section (top) or
     "browse items" section (bottom) -->
<MyShoppingCartView
     android:id="@+id/shoppingCartContainer"
     android:accessibilityPaneTitle="@string/shoppingCart" ... />

<MyShoppingBrowseView
     android:id="@+id/browseItemsContainer"
     android:accessibilityPaneTitle="@string/browseProducts" ... />

Elemen dekoratif

Jika elemen di UI Anda hanya ada untuk spacing visual atau tujuan tampilan visual, setel atribut android:importantForAccessibility ke "no".

Menambahkan tindakan aksesibilitas

Penting untuk mengizinkan pengguna layanan aksesibilitas melakukan semua alur pengguna dengan mudah dalam aplikasi Anda. Misalnya, jika pengguna dapat menggeser item dalam daftar, tindakan ini juga dapat diekspos ke layanan aksesibilitas sehingga pengguna memiliki cara alternatif untuk menyelesaikan alur pengguna yang sama.

Membuat semua tindakan dapat diakses

Pengguna TalkBack, Voice Access, atau Switch Access mungkin memerlukan cara alternatif untuk menyelesaikan alur pengguna tertentu dalam aplikasi. Untuk tindakan yang terkait dengan gestur seperti tarik lalu lepas atau geser, aplikasi Anda dapat mengekspos tindakan dengan cara yang dapat diakses oleh pengguna layanan aksesibilitas.

Dengan menggunakan tindakan aksesibilitas, aplikasi dapat memberikan cara alternatif bagi pengguna untuk menyelesaikan tindakan.

Misalnya, jika aplikasi Anda memungkinkan pengguna menggeser item, Anda juga dapat mengekspos fungsi melalui tindakan aksesibilitas kustom, seperti ini:

Kotlin

ViewCompat.addAccessibilityAction(
    // View to add accessibility action
    itemView,
    // Label surfaced to user by an accessibility service
    getText(R.id.archive)
) { _, _ ->
    // Same method executed when swiping on itemView
    archiveItem()
    true
}

Java

ViewCompat.addAccessibilityAction(
    // View to add accessibility action
    itemView,
    // Label surfaced to user by an accessibility service
    getText(R.id.archive),
    (view, arguments) -> {
        // Same method executed when swiping on itemView
        archiveItem();
        return true;
    }
);

With the custom accessibility action implemented, users can access the action through the actions menu.

Make available actions understandable

When a view supports actions such as touch & hold, an accessibility service such as TalkBack announces it as "Double tap and hold to long press."

This generic announcement doesn't give the user any context about what a touch & hold action does.

To make this announcement more descriptive, you can replace the accessibility actions announcement like so:

Kotlin

ViewCompat.replaceAccessibilityAction(
    // View that contains touch & hold action
    itemView,
    AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_LONG_CLICK,
    // Announcement read by TalkBack to surface this action
    getText(R.string.favorite),
    null
)

Java

ViewCompat.replaceAccessibilityAction(
    // View that contains touch & hold action
    itemView,
    AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_LONG_CLICK,
    // Announcement read by TalkBack to surface this action
    getText(R.string.favorite),
    null
);

This results in TalkBack announcing "Double tap and hold to favorite," helping users understand the purpose of the action.

Extend system widgets

Note: When you design your app's UI, use or extend system-provided widgets that are as far down Android's class hierarchy as possible. System-provided widgets that are far down the hierarchy already have most of the accessibility capabilities your app needs. It's easier to extend these system-provided widgets than to create your own from the more generic View, ViewCompat, Canvas, and CanvasCompat classes.

If you must extend View or Canvas directly, which might be necessary for a highly customized experience or a game level, see Make custom views more accessible.

This section uses the example of implementing a special type of Switch called TriSwitch while following best practices around extending system widgets. A TriSwitch object works similarly to a Switch object, except that each instance of TriSwitch allows the user to toggle among three possible states.

Extend from far down the class hierarchy

The Switch object inherits from several framework UI classes in its hierarchy:

View
 TextView
   Button
     CompoundButton
       Switch

Sebaiknya class TriSwitch yang baru diperluas secara langsung dari class Switch. Dengan demikian, framework aksesibilitas Android menyediakan sebagian besar kemampuan aksesibilitas yang diperlukan class TriSwitch:

  • Tindakan aksesibilitas: informasi untuk sistem tentang cara layanan aksesibilitas dapat mengemulasi setiap kemungkinan input pengguna yang dilakukan pada objek TriSwitch. (Diwarisi dari View.)
  • Peristiwa aksesibilitas: informasi untuk layanan aksesibilitas tentang setiap kemungkinan cara agar tampilan objek TriSwitch dapat berubah saat layar dimuat ulang atau diperbarui. (Diwarisi dari View.)
  • Karakteristik: detail setiap objek TriSwitch, seperti konten teks yang ditampilkannya. (Diwarisi dari TextView.)
  • Informasi status: deskripsi tentang status objek TriSwitch saat ini, misalnya "dicentang" atau "tidak dicentang". (Diwarisi dari CompoundButton.)
  • Deskripsi teks tentang status: penjelasan berbasis teks tentang apa yang direpresentasikan oleh setiap status. (Diwarisi dari Switch.)

Perilaku dari Switch dan superclass-nya ini hampir sama dengan perilaku objek TriSwitch. Oleh karena itu, implementasi Anda dapat berfokus pada perluasan jumlah kemungkinan status dari dua menjadi tiga.

Menentukan peristiwa kustom

Saat Anda memperluas widget sistem, Anda mungkin mengubah aspek tentang cara pengguna berinteraksi dengan widget tersebut. Penting untuk menentukan perubahan interaksi ini agar layanan aksesibilitas dapat memperbarui widget aplikasi Anda seolah-olah pengguna berinteraksi dengan widget secara langsung.

Pedoman umumnya adalah, untuk setiap callback berbasis tampilan yang Anda ganti, Anda juga perlu menentukan ulang tindakan aksesibilitas yang sesuai dengan mengganti ViewCompat.replaceAccessibilityAction(). Dalam pengujian aplikasi, Anda dapat memvalidasi perilaku tindakan yang dtentukan ulang tersebut dengan memanggil ViewCompat.performAccessibilityAction().

Cara kerja prinsip ini untuk objek TriSwitch

Tidak seperti objek Switch biasa, mengetuk siklus objek TriSwitch akan mengalami siklus lewat tiga kemungkinan status. Oleh karena itu, tindakan aksesibilitas ACTION_CLICK yang terkait perlu diperbarui:

Kotlin

class TriSwitch(context: Context) : Switch(context) {
    // 0, 1, or 2
    var currentState: Int = 0
        private set

    init {
        updateAccessibilityActions()
    }

    private fun updateAccessibilityActions() {
        ViewCompat.replaceAccessibilityAction(this, ACTION_CLICK,
            action-label) {
            view, args -> moveToNextState()
        })
    }

    private fun moveToNextState() {
        currentState = (currentState + 1) % 3
    }
}

Java

public class TriSwitch extends Switch {
    // 0, 1, or 2
    private int currentState;

    public int getCurrentState() {
        return currentState;
    }

    public TriSwitch() {
        updateAccessibilityActions();
    }

    private void updateAccessibilityActions() {
        ViewCompat.replaceAccessibilityAction(this, ACTION_CLICK,
            action-label, (view, args) -> moveToNextState());
    }

    private void moveToNextState() {
        currentState = (currentState + 1) % 3;
    }
}

Referensi lainnya

Untuk mempelajari lebih lanjut cara menjadikan aplikasi lebih mudah diakses, lihat referensi tambahan berikut:

Codelab

Postingan blog