مبادئ تحسين إمكانية الوصول إلى التطبيق

لمساعدة المستخدمين بخصوص تسهيل الاستخدام، يتيح لك إطار عمل Android إنشاء خدمة لتسهيل الاستخدام يمكنها عرض المحتوى من التطبيقات للمستخدمين وتشغيل التطبيقات نيابةً عنهم أيضًا.

يوفّر Android العديد من خدمات الوصول إلى النظام، بما في ذلك ما يلي:

  • TalkBack: لمساعدة المستخدمين الذين يعانون من ضعف في النظر أو المكفوفين فهو يعلن عن المحتوى من خلال صوت مُركب وينفذ إجراءات على التطبيق استجابةً لإيماءات المستخدم.
  • الوصول عبر مفتاح تحكّم: لمساعدة الأشخاص الذين يعانون من إعاقات حركية. إنه يسلط الضوء على العناصر التفاعلية وينفذ إجراءات استجابة للمستخدم الذي يضغط على زر. تسمح بالتحكم في الجهاز باستخدام زر واحد أو زرين فقط.

لمساعدة الأشخاص الذين يحتاجون إلى سهولة الوصول في استخدام تطبيقك بنجاح، يجب أن يتّبع تطبيقك أفضل الممارسات الموضحة في هذه الصفحة والتي تستند إلى الإرشادات الموضّحة في قسم تسهيل الوصول إلى التطبيقات.

يمكن أن يؤدي كلٌّ من أفضل الممارسات الموضحة في الأقسام التالية إلى تحسين إمكانية الوصول إلى تطبيقك:

عناصر التصنيف
يجب أن يتمكّن المستخدمون من فهم محتوى كل عنصر من عناصر واجهة المستخدم التفاعلية والمفيدة والغرض منه.
إضافة إجراءات تسهيل الاستخدام
من خلال إضافة إجراءات تسهيل الاستخدام، يمكنك السماح لمستخدمي خدمات تسهيل الاستخدام بإكمال الخطوات المهمة للمستخدمين داخل تطبيقك.
توسيع نطاق التطبيقات المصغّرة للنظام
استخدِم عناصر العرض التي يتضمّنها إطار العمل، بدلاً من إنشاء طرق عرض مخصّصة لك. توفّر فئات التطبيقات المصغّرة وعرض إطار العمل حاليًا معظم إمكانيات تسهيل الاستخدام التي يحتاجها تطبيقك.
استخدام إشارات أخرى غير اللون
يجب أن يتمكّن المستخدمون من التمييز بوضوح بين فئات العناصر في واجهة المستخدم. لإجراء ذلك، استخدِم الأنماط والموضع، إلى جانب اللون، للتعبير عن هذه الاختلافات.
تسهيل الوصول إلى محتوى الوسائط
أضِف أوصافًا إلى الفيديو أو المحتوى الصوتي في تطبيقك حتى لا يحتاج المستخدمون الذين يشاهدون هذا المحتوى إلى الاعتماد بالكامل على الإشارات البصرية أو السمعية.

عناصر التصنيف

من المهم تزويد المستخدمين بتصنيفات مفيدة ووصفية لكل عنصر تفاعلي في واجهة المستخدم في تطبيقك. ويجب أن يوضّح كل تصنيف معنى عنصر معيّن والغرض منه. يمكن لبرامج قراءة الشاشة مثل TalkBack أن تعلن عن هذه التصنيفات للمستخدمين.

في معظم الحالات، يمكنك تحديد وصف عنصر واجهة المستخدم في ملف مورد التخطيط الذي يحتوي على العنصر. تتم عادةً إضافة تصنيفات باستخدام السمة contentDescription، كما هو موضّح في الدليل حول تسهيل استخدام التطبيقات. هناك العديد من أساليب التصنيف الأخرى الموضّحة في الأقسام التالية.

عناصر قابلة للتعديل

عند تصنيف العناصر القابلة للتعديل، مثل كائنات EditText، من المفيد عرض نصّ يقدّم مثالاً على إدخال صالح في العنصر نفسه، بالإضافة إلى إتاحة هذا المثال على النص لقارئات الشاشة. في هذه الحالات، يمكنك استخدام السمة android:hint، كما هو موضّح في المقتطف التالي:

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

في هذه الحالة، يجب ضبط السمة android:labelFor الخاصة بالكائن View على رقم تعريف العنصر EditText. لمزيد من التفاصيل، يُرجى الاطّلاع على القسم التالي.

أزواج من العناصر حيث يصف أحدها الآخر

من الشائع أن يكون للعنصر EditText عنصر View مقابل يصف ما يجب على المستخدمين إدخاله في العنصر EditText. يمكنك الإشارة إلى هذه العلاقة عن طريق ضبط السمة android:labelFor لكائن View.

يظهر مثال على تصنيف أزواج العناصر هذه في المقتطف التالي:


<!-- 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" ... />

العناصر في مجموعة

عند إضافة تصنيفات إلى عناصر مجموعة، يجب أن يكون كل تصنيف فريدًا من نوعه. وبهذه الطريقة، يمكن أن تشير خدمات تسهيل الاستخدام الخاصة بالنظام إلى عنصر واحد فقط على الشاشة عند الإعلان عن تصنيف. تتيح هذه المراسلة للمستخدمين معرفة الوقت الذي يتنقلون فيه عبر واجهة المستخدم أو عندما ينقلون التركيز إلى عنصر اكتشفوه بالفعل.

وعلى وجه الخصوص، ضمِّن نصًا إضافيًا أو معلومات سياقية في العناصر ضمن التنسيقات المُعاد استخدامها، مثل كائنات RecyclerView، بحيث يتم تحديد كل عنصر فرعي على حدة.

لإجراء ذلك، اضبط وصف المحتوى كجزء من تنفيذ المحوِّل، كما هو موضّح في مقتطف الرمز التالي:

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")
    }
}

مجموعات ذات صلة بالمحتوى

إذا كان تطبيقك يعرض العديد من عناصر واجهة المستخدم التي تشكّل مجموعة طبيعية، مثل تفاصيل أغنية أو سمات رسالة، يمكنك ترتيب هذه العناصر داخل حاوية، وعادةً ما تكون فئة فرعية من ViewGroup. اضبط السمة android:screenReaderFocusable لكائن الحاوية على true، وضبط السمة android:focusable لكل عنصر داخلي على false. بهذه الطريقة، يمكن لخدمات إمكانية الوصول تقديم أوصاف محتوى العناصر الداخلية، الواحد تلو الآخر، في إعلان واحد. يساعد هذا الدمج للعناصر ذات الصلة مستخدمي التكنولوجيا المساعدة على اكتشاف المعلومات على الشاشة بشكل أكثر كفاءة.

يتضمّن المقتطف التالي أجزاءً من المحتوى ذات صلة ببعضها، لذا تم ضبط سمة android:screenReaderFocusable لعنصر الحاوية، وهو مثيل ConstraintLayout، على true، في حين تم ضبط سمة android:focusable الخاصة بكل عنصر من عناصر TextView الداخلية على 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>

نظرًا لأن خدمات إمكانية الوصول تعلن عن أوصاف العناصر الداخلية في جملة واحدة، فمن المهم إبقاء كل وصف قصيرًا قدر الإمكان مع الاستمرار في نقل معنى العنصر.

ملاحظة:بشكل عام، يجب تجنُّب إنشاء وصف لمحتوى مجموعة من خلال تجميع نص أنشطتها. يؤدي ذلك إلى زيادة رقة وصف المجموعة، وعندما يتغير نص العنصر الفرعي، قد لا يتطابق وصف المجموعة مع النص المرئي.

في سياق قائمة أو شبكة، قد يدمج قارئ الشاشة نص العُقد النصية الفرعية لعنصر القائمة أو الشبكة. من الأفضل تجنب تعديل هذا الإعلان.

المجموعات المدمَجة

إذا كانت واجهة تطبيقك تعرض معلومات متعددة الأبعاد، مثل قائمة يومية بفعاليات المهرجانات، يمكنك استخدام السمة android:screenReaderFocusable على حاويات المجموعة الداخلية. يوفر نظام التصنيف هذا توازنًا جيدًا بين عدد الإعلانات اللازمة لاكتشاف محتوى الشاشة وطول كل إعلان.

يعرض مقتطف الرمز التالي طريقة واحدة لتصنيف المجموعات داخل مجموعات أكبر:

<!-- 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>

العناوين ضمن النص

تستخدم بعض التطبيقات العناوين لتلخيص مجموعات من النصوص التي تظهر على الشاشة. إذا كان عنصر View معيّن يمثّل عنوانًا، يمكنك الإشارة إلى الغرض منه في خدمات تسهيل الاستخدام من خلال ضبط سمة android:accessibilityHeading للعنصر على true.

يمكن لمستخدمي خدمات إمكانية الوصول اختيار التنقل بين العناوين بدلاً من الفقرات أو بين الكلمات. تعمل هذه المرونة على تحسين تجربة التنقل بالنص.

عناوين لوحات تسهيل الاستخدام

في نظام التشغيل Android 9 (المستوى 28 من واجهة برمجة التطبيقات) والإصدارات الأحدث، يمكنك توفير عناوين سهلة الاستخدام للوحات الشاشة. لأغراض إمكانية الوصول، يعد الجزء جزءًا مميزًا مرئيًا من النافذة، مثل محتويات الجزء. لكي تفهم خدمات إمكانية الوصول السلوك الشبيه بالنافذة في اللوحة، قدم عناوين وصفية لأجزاء التطبيق. يمكن لخدمات إمكانية الوصول بعد ذلك توفير معلومات أكثر دقة للمستخدمين عندما يتغير مظهر الجزء أو المحتوى.

لتحديد عنوان لوحة، استخدِم السمة android:accessibilityPaneTitle، كما هو موضّح في المقتطف التالي:

<!-- 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" ... />

عناصر زخرفية

إذا كان هناك عنصر في واجهة المستخدم مخصّص لأغراض التباعد البصري أو المظهر المرئي فقط، اضبط سمة android:importantForAccessibility على "no".

إضافة إجراءات تسهيل الاستخدام

من المهم أن تسمح لمستخدمي خدمات تسهيل الاستخدام بتنفيذ مسارات جميع المستخدمين داخل تطبيقك بسهولة. على سبيل المثال، إذا تمكّن المستخدم من التمرير سريعًا على عنصر ضمن قائمة، يمكن أيضًا عرض هذا الإجراء لخدمات تسهيل الاستخدام لكي يحصل المستخدمون على طريقة بديلة لإكمال تدفق المستخدم نفسه.

إتاحة الوصول إلى جميع الإجراءات

قد يحتاج مستخدم تطبيق TalkBack أو Voice Access أو "الوصول عبر مفتاح تحكّم" إلى طُرق بديلة لإكمال مسارات معيّنة للمستخدمين داخل التطبيق. وبالنسبة إلى الإجراءات المرتبطة بالإيماءات مثل السحب والإفلات أو التمريرات السريعة، يمكن أن يعرض تطبيقك الإجراءات بطريقة يمكن لمستخدمي خدمات تسهيل الاستخدام الوصول إليها.

باستخدام إجراءات إمكانية الوصول، يمكن أن يوفر التطبيق طرقًا بديلة للمستخدمين لإكمال إجراء ما.

على سبيل المثال، إذا كان تطبيقك يسمح للمستخدمين بالتمرير السريع على عنصر ما، يمكنك أيضًا عرض الوظيفة من خلال إجراء إمكانية وصول مخصص، مثل هذا:

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 action’s 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

من الأفضل لفئة TriSwitch الجديدة أن تتم إتاحتها مباشرةً من الصف Switch. بهذه الطريقة، يوفّر إطار عمل تسهيل الاستخدام على Android معظم إمكانيات تسهيل الاستخدام التي يحتاجها صف TriSwitch:

  • إجراءات تسهيل الاستخدام: معلومات للنظام حول كيفية محاكاة خدمات تسهيل الاستخدام لكل إدخال محتمَل من قِبل المستخدم يتم تنفيذه على عنصر TriSwitch. (مُكتسَب من View.)
  • أحداث إمكانية الوصول: معلومات لخدمات تسهيل الاستخدام حول كل طريقة يمكن أن يتغيّر بها مظهر عنصر TriSwitch عند إعادة تحميل الشاشة أو تحديثها. (مُكتسَب من View.)
  • الخصائص: تفاصيل حول كل عنصر TriSwitch، مثل محتوى أي نص يعرضه. (مُكتسَب من TextView.)
  • معلومات الحالة: وصف للحالة الراهنة للعنصر TriSwitch، مثل "تم وضع علامة" أو "غير محدَّد" (مُكتسَب من CompoundButton.)
  • وصف نصي للولاية: شرح مستند إلى نص لما تمثله كل ولاية. (مُكتسَب من Switch.)

هذا السلوك من Switch والفئات الرئيسية له هو السلوك نفسه تقريبًا لكائنات TriSwitch. وبالتالي، يمكن أن تركّز عملية التنفيذ على زيادة عدد الحالات المحتملة من حالتَين إلى ثلاث حالات.

تحديد الأحداث المخصَّصة

عند توسيع أداة النظام، من المحتمل أن تغير جانبًا من كيفية تفاعل المستخدمين مع هذه الأداة. من المهم تحديد تغييرات التفاعل هذه بحيث يمكن لخدمات تسهيل الاستخدام تعديل التطبيق المصغّر لتطبيقك كما لو كان المستخدم يتفاعل مع التطبيق المصغّر مباشرةً.

المبدأ العام هو أنّه عليك أيضًا إعادة تحديد إجراء تسهيل الاستخدام ذي الصلة لكل معاودة اتصال مستنِدة إلى العرض تلغيها، وذلك عن طريق إلغاء ViewCompat.replaceAccessibilityAction(). في اختبارات تطبيقك، يمكنك التحقّق من صحة سلوك هذه الإجراءات المُعاد تعريفها من خلال طلب ViewCompat.performAccessibilityAction().

كيف يعمل هذا المبدأ مع كائنات TriSwitch

على عكس كائن Switch العادي، يؤدي النقر على الكائن TriSwitch إلى إجراء ثلاث حالات محتملة. لذلك، يجب تعديل إجراء تسهيل الاستخدام ACTION_CLICK المقابل له:

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;
    }
}

استخدام إشارات أخرى غير اللون

لمساعدة المستخدمين الذين يعانون من قصور في رؤية الألوان، يمكنك استخدام إشارات أخرى غير اللون لتمييز عناصر واجهة المستخدم على شاشات التطبيق. ويمكن أن تشمل هذه الأساليب استخدام أشكال أو أحجام مختلفة، أو تقديم أنماط نصية أو مرئية، أو إضافة ملاحظات صوتية أو لمسية (حسّية) لتمييز الاختلافات بين العناصر.

يوضح الشكل 1 نسختين من أحد الأنشطة. يستخدم الإصدار الأول الألوان فقط للتمييز بين إجراءَين محتملَين في سير العمل. يستخدم الإصدار الآخر أفضل الممارسات المتمثلة في تضمين الأشكال والنصوص بالإضافة إلى اللون لإبراز الاختلافات بين الخيارين:

الشكل 1. أمثلة على إنشاء عناصر واجهة المستخدم باستخدام اللون فقط (على اليسار) واستخدام اللون والأشكال والنص (على اليمين).

تسهيل الوصول إلى محتوى الوسائط

وإذا كنت تطور تطبيقًا يتضمن محتوى وسائط، مثل مقطع فيديو أو تسجيل صوتي، حاوِل مساعدة المستخدمين الذين لديهم أنواع مختلفة من احتياجات تسهيل الاستخدام في فهم هذا المحتوى. وعلى وجه الخصوص، ننصحك بما يلي:

  • ضمِّن عناصر تحكّم تسمح للمستخدمين بإيقاف الوسائط مؤقتًا أو إيقافها وتغيير مستوى الصوت والتبديل بين الترجمة (الترجمة).
  • إذا كان الفيديو يقدّم معلومات أساسية لإكمال سير العمل، قدِّم المحتوى نفسه بتنسيق بديل، مثل النص.

مراجع إضافية

لمعرفة المزيد من المعلومات حول تسهيل الوصول إلى تطبيقك، اطّلِع على الموارد الإضافية التالية:

الدروس التطبيقية حول الترميز

مشاركات المدونة