إذا كان تطبيقك يتطلّب مكوّن عرض مخصّص، عليك أن تجعل العرض أكثر سهولة في الاستخدام. يمكن أن تساعد الخطوات التالية في تحسين إمكانية الوصول إلى العرض المخصّص، كما هو موضّح في هذه الصفحة:
- التعامل مع نقرات وحدة التحكّم الاتجاهية
- تنفيذ طرق واجهة برمجة التطبيقات Accessibility API
- أرسِل عناصر
AccessibilityEventخاصة بطريقة العرض المخصّصة. - املأ الحقلَين
AccessibilityEventوAccessibilityNodeInfoللعرض.
التعامل مع نقرات وحدة التحكّم الاتجاهية
في معظم الأجهزة، يؤدي النقر على طريقة عرض باستخدام وحدة تحكّم اتجاهية إلى إرسال
KeyEvent مع
KEYCODE_DPAD_CENTER
إلى طريقة العرض محل التركيز حاليًا. تتعامل جميع طرق العرض العادية في Android مع
KEYCODE_DPAD_CENTER بشكل مناسب. عند إنشاء عنصر تحكّم مخصّص، تأكَّد من أنّ هذا الحدث له التأثير نفسه الذي يحدث عند النقر على العرض على شاشة اللمس.View
يجب أن يتعامل عنصر التحكّم المخصّص مع الحدث
KEYCODE_ENTER
بالطريقة نفسها التي يتعامل بها مع KEYCODE_DPAD_CENTER. ويسهّل ذلك على المستخدمين التفاعل مع لوحة مفاتيح كاملة.
تنفيذ طرق واجهة برمجة التطبيقات الخاصة بإمكانية الوصول
أحداث تسهيل الاستخدام هي رسائل حول تفاعلات المستخدمين مع عناصر واجهة المستخدم المرئية في تطبيقك. تتعامل خدمات تسهيل الاستخدام مع هذه الرسائل، وهي تستخدم المعلومات الواردة في هذه الأحداث لإنشاء ملاحظات ومطالبات تكميلية. تشكّل طرق تسهيل الاستخدام جزءًا من الفئتَين View وView.AccessibilityDelegate. الطرق هي كما يلي:
dispatchPopulateAccessibilityEvent()onPopulateAccessibilityEvent() لهذا العرض، ثم تستدعي الطريقة dispatchPopulateAccessibilityEvent() لكل عنصر فرعي من هذا العرض. onInitializeAccessibilityEvent()TextView أو Button، عليك إلغاء هذا الإجراء وتحديد المعلومات الإضافية عن العرض، مثل نوع حقل كلمة المرور أو نوع مربّع الاختيار أو الحالات التي توفّر تفاعل المستخدم أو ملاحظات منه في الحدث، وذلك باستخدام هذا الإجراء. إذا ألغيت هذه الطريقة، عليك استدعاء التنفيذ الفائق لها وتعديل الخصائص التي لم يتم ضبطها بواسطة الفئة الفائقة فقط.onInitializeAccessibilityNodeInfo()View مجموعة عادية من خصائص العرض، ولكن إذا كان العرض المخصّص يوفّر عنصر تحكّم تفاعليًا يتجاوز TextView أو Button بسيطًا، عليك إلغاء هذه الطريقة وضبط المعلومات الإضافية حول العرض في العنصر AccessibilityNodeInfo الذي تعالجه هذه الطريقة.onPopulateAccessibilityEvent()AccessibilityEvent للعرض. يتم أيضًا استدعاء هذه الطريقة إذا كان العرض تابعًا لعرض آخر ينشئ حدثًا متعلقًا بإمكانية الوصول.
onRequestSendAccessibilityEvent()AccessibilityEvent. تتيح هذه الخطوة للعرض المخصَّص للوالدَين تعديل حدث تسهيل الاستخدام بإضافة معلومات إضافية. لا تنفِّذ هذه الطريقة إلا إذا كان بإمكان العرض المخصّص أن يتضمّن عروضًا فرعية، وإذا كان بإمكان العرض الرئيسي تقديم معلومات سياقية لحدث تسهيل الاستخدام تكون مفيدة لخدمات تسهيل الاستخدام.sendAccessibilityEvent()- يستدعي النظام هذا الإجراء عندما يتّخذ المستخدم إجراءً بشأن طريقة العرض. يتم تصنيف الحدث حسب نوع إجراء المستخدم، مثل
TYPE_VIEW_CLICKED. بشكل عام، يجب إرسالAccessibilityEventكلما تغيّر محتوى العرض المخصّص. sendAccessibilityEventUnchecked()- يتم استخدام هذه الطريقة عندما يحتاج رمز الاتصال إلى التحكّم مباشرةً في عملية التحقّق من تفعيل إعدادات تسهيل الاستخدام على الجهاز
(
AccessibilityManager.isEnabled()). في حال تنفيذ هذه الطريقة، عليك إجراء المكالمة كما لو كانت إعدادات تسهيل الاستخدام مفعَّلة، بغض النظر عن إعدادات النظام. لا تحتاج عادةً إلى تنفيذ هذه الطريقة لعرض مخصّص. dispatchPopulateAccessibilityEvent()onInitializeAccessibilityEvent()onInitializeAccessibilityNodeInfo()onPopulateAccessibilityEvent()TYPE_VIEW_CLICKEDTYPE_VIEW_FOCUSEDTYPE_VIEW_HOVER_ENTERTYPE_VIEW_HOVER_EXITTYPE_VIEW_LONG_CLICKEDTYPE_VIEW_SCROLLED- أنشئ
AccessibilityEventمناسبًا لإجراء النقر الذي تم تفسيره. - تفعيل خدمات تسهيل الاستخدام لتنفيذ إجراء النقر المخصّص للمستخدمين الذين لا يمكنهم استخدام شاشة لمس
لإتاحة إمكانية الوصول، عليك تجاهل طرق إمكانية الوصول السابقة وتنفيذها مباشرةً في فئة العرض المخصّص.
يجب تنفيذ طرق تسهيل الاستخدام التالية لفئة العرض المخصّص على الأقل:
لمزيد من المعلومات حول تنفيذ هذه الطرق، راجِع القسم حول تعبئة أحداث تسهيل الاستخدام.
إرسال أحداث إمكانية الوصول
استنادًا إلى تفاصيل العرض المخصّص، قد تحتاج إلى إرسال عناصر AccessibilityEvent في أوقات مختلفة أو لأحداث لا يتم التعامل معها من خلال التنفيذ التلقائي. تقدّم الفئة View عملية تنفيذ تلقائية لأنواع الأحداث التالية:
بشكل عام، يجب إرسال AccessibilityEvent كلما تغيّر محتوى العرض المخصّص. على سبيل المثال، إذا كنت تنفّذ شريط تمرير مخصّصًا يتيح للمستخدم اختيار قيمة رقمية من خلال الضغط على مفتاح السهم المتّجه لليسار أو لليمين، يجب أن يرسل العرض المخصّص حدثًا من النوع TYPE_VIEW_TEXT_CHANGED
عندما تتغيّر قيمة شريط التمرير. يوضّح نموذج الرمز البرمجي التالي كيفية استخدام طريقة sendAccessibilityEvent() لتسجيل هذا الحدث.
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; } ... }
تعبئة أحداث تسهيل الاستخدام
يحتوي كل AccessibilityEvent على مجموعة من السمات المطلوبة التي تصف الحالة الحالية للعرض. وتشمل هذه الخصائص عناصر مثل اسم فئة العرض ووصف المحتوى وحالة التحديد. يتم وصف السمات المحدّدة المطلوبة لكل نوع من الأحداث في
AccessibilityEvent
المستندات المرجعية.
يوفر تنفيذ View قيمًا تلقائية لهذه الخصائص المطلوبة. يتم توفير العديد من هذه القيم تلقائيًا، بما في ذلك اسم الفئة والطابع الزمني للحدث. إذا كنت بصدد إنشاء مكوّن عرض مخصّص، عليك تقديم معلومات حول محتوى العرض وخصائصه. يمكن أن تكون هذه المعلومات بسيطة مثل تصنيف زر، ويمكن أن تتضمّن معلومات إضافية عن الحالة تريد إضافتها إلى الحدث.
استخدِم الطريقتَين
onPopulateAccessibilityEvent()
و
onInitializeAccessibilityEvent()
لملء المعلومات أو تعديلها في AccessibilityEvent. استخدِم طريقة
onPopulateAccessibilityEvent() تحديدًا لإضافة محتوى نصي أو تعديله في الحدث، والذي تحوّله خدمات تسهيل الاستخدام، مثل TalkBack، إلى طلبات صوتية. استخدِم طريقة onInitializeAccessibilityEvent() لملء معلومات إضافية حول الحدث، مثل حالة التحديد للعرض.
بالإضافة إلى ذلك، نفِّذ الطريقة
onInitializeAccessibilityNodeInfo(). تستخدم خدمات تسهيل الاستخدام عناصر AccessibilityNodeInfo التي يتم تعبئتها بواسطة هذه الطريقة للتحقّق من هيكلية طرق العرض التي تنشئ حدث تسهيل استخدام بعد تلقّيه وتقديم الملاحظات المناسبة للمستخدمين.
يوضّح مثال الرمز البرمجي التالي كيفية إلغاء هذه الطرق الثلاث في طريقة العرض:
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); } }
يمكنك تنفيذ هذه الطرق مباشرةً في فئة العرض المخصّص.
توفير سياق مخصّص لإمكانية الوصول
يمكن لخدمات تسهيل الاستخدام فحص هيكلية طرق العرض التي تحتوي على أحد عناصر واجهة المستخدم الذي ينشئ حدثًا متعلقًا بتسهيل الاستخدام. يتيح ذلك لخدمات تسهيل الاستخدام تقديم معلومات سياقية أكثر تفصيلاً لمساعدة المستخدمين.
هناك حالات لا يمكن فيها لخدمات تسهيل الاستخدام الحصول على معلومات كافية من التسلسل الهرمي للعرض. ومن الأمثلة على ذلك عنصر تحكّم مخصّص في الواجهة يتضمّن منطقتَين أو أكثر يمكن النقر عليهما بشكل منفصل، مثل عنصر تحكّم في التقويم. في هذه الحالة، لا يمكن للخدمات الحصول على معلومات كافية لأنّ الأقسام الفرعية القابلة للنقر ليست جزءًا من هيكلية طرق العرض.
الشكل 1: طريقة عرض تقويم مخصّصة تتضمّن عناصر أيام قابلة للاختيار
في المثال الوارد في الشكل 1، يتم تنفيذ التقويم بأكمله كطريقة عرض واحدة، لذا لا تتلقّى خدمات تسهيل الاستخدام معلومات كافية حول محتوى طريقة العرض واختيار المستخدم ضمن طريقة العرض ما لم يقدّم المطوّر معلومات إضافية. على سبيل المثال، إذا نقر المستخدم على اليوم الذي يحمل التصنيف 17، لن يتلقّى إطار عمل تسهيل الاستخدام سوى معلومات الوصف الخاصة بعنصر التحكّم في التقويم بالكامل. في هذه الحالة، تعلن خدمة مخصّصة لتسهيل الاستخدام TalkBack عن "التقويم" أو "تقويم أبريل"، ولا يعرف المستخدم اليوم المحدّد.
لتوفير معلومات سياقية كافية لخدمات تسهيل الاستخدام في حالات مثل هذه، يوفّر إطار العمل طريقة لتحديد هيكلية طرق العرض الافتراضية. التسلسل الهرمي لطرق العرض الافتراضية هو طريقة تتيح لمطوّري التطبيقات تقديم تسلسل هرمي تكميلي لطرق العرض لخدمات تسهيل الاستخدام، ويكون هذا التسلسل أكثر تطابقًا مع المعلومات المعروضة على الشاشة. يتيح هذا الأسلوب لخدمات تسهيل الاستخدام تقديم معلومات سياقية أكثر فائدة للمستخدمين.
هناك حالة أخرى قد تكون فيها هيكلية طرق العرض الافتراضية مطلوبة، وهي واجهة المستخدم التي تحتوي على مجموعة من عناصر التحكّم التي لها وظائف مرتبطة ارتباطًا وثيقًا، حيث يؤثر إجراء على أحد عناصر التحكّم في محتوى عنصر واحد أو أكثر، مثل أداة اختيار الأرقام التي تحتوي على زرَّين منفصلَين للانتقال للأعلى وللأسفل.View في هذه الحالة، لا يمكن لخدمات تسهيل الاستخدام الحصول على معلومات كافية لأنّ إجراءً على أحد عناصر التحكّم يغيّر المحتوى في عنصر آخر، وقد لا تكون العلاقة بين عناصر التحكّم هذه واضحة للخدمة.
للتعامل مع هذه الحالة، عليك تجميع عناصر التحكّم ذات الصلة في طريقة عرض حاوية وتوفير هيكلية طرق العرض الافتراضية من هذه الحاوية لتمثيل المعلومات والسلوك المقدَّمَين من خلال عناصر التحكّم بوضوح.
لتوفير هيكلية طرق عرض افتراضية، عليك إلغاء طريقة
getAccessibilityNodeProvider()
في العرض المخصّص أو مجموعة طرق عرض وإرجاع تنفيذ
AccessibilityNodeProvider.
يمكنك تنفيذ هيكلية طرق عرض افتراضية باستخدام "مكتبة الدعم" مع الطريقة
ViewCompat.getAccessibilityNodeProvider()
وتوفير عملية تنفيذ باستخدام
AccessibilityNodeProviderCompat.
لتسهيل مهمة تقديم المعلومات إلى خدمات تسهيل الاستخدام وإدارة تركيز تسهيل الاستخدام، يمكنك بدلاً من ذلك تنفيذ ExploreByTouchHelper.
توفّر هذه السمة AccessibilityNodeProviderCompat ويمكن إرفاقها كـ AccessibilityDelegateCompat للعرض من خلال استدعاء setAccessibilityDelegate.
للاطّلاع على مثال، راجِع
ExploreByTouchHelperActivity.
يتم استخدام ExploreByTouchHelper أيضًا من خلال أدوات إطار العمل، مثل CalendarView، وذلك من خلال SimpleMonthView، وهو العرض الفرعي.
التعامل مع أحداث اللمس المخصّصة
قد تتطلّب عناصر التحكّم في العرض المخصّص سلوكًا غير عادي لحدث اللمس، كما هو موضّح في الأمثلة التالية.
تحديد الإجراءات المستندة إلى النقرات
إذا كانت الأداة تستخدم واجهة
OnClickListener أو
OnLongClickListener، سيتولّى النظام تنفيذ الإجراءين
ACTION_CLICK
و
ACTION_LONG_CLICK. إذا كان تطبيقك يستخدم أداة أكثر تخصيصًا تعتمد على واجهة OnTouchListener، حدِّد معالِجات مخصّصة لإجراءات تسهيل الاستخدام المستندة إلى النقر. لإجراء ذلك، استدعِ طريقة
replaceAccessibilityAction()
لكل إجراء، كما هو موضّح في مقتطف الرمز التالي:
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()); }
إنشاء أحداث نقر مخصّصة
يمكن لعنصر تحكّم مخصّص استخدام طريقة onTouchEvent(MotionEvent)
listener لرصد الأحداث ACTION_DOWN وACTION_UP
وتشغيل حدث ناتج عن النقر خاص. للحفاظ على التوافق مع خدمات تسهيل الاستخدام، يجب أن ينفّذ الرمز الذي يتعامل مع حدث ناتج عن النقر المخصّص ما يلي:
للتعامل مع هذه المتطلبات بكفاءة، يجب أن يتجاوز الرمز البرمجي الطريقة performClick()، التي يجب أن تستدعي التنفيذ الفائق لهذه الطريقة ثم تنفّذ أي إجراءات يتطلبها حدث ناتج عن النقر. عند رصد إجراء النقر المخصّص، يجب أن يستدعي هذا الرمز بعد ذلك طريقة performClick(). يوضّح مثال الرمز التالي هذا النمط.
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; } }
يساعد النمط السابق في ضمان توافق حدث النقر المخصّص مع خدمات تسهيل الاستخدام من خلال استخدام طريقة performClick() لإنشاء حدث تسهيل الاستخدام وتوفير نقطة دخول لخدمات تسهيل الاستخدام من أجل تنفيذ إجراء نيابةً عن المستخدم الذي ينفّذ حدث النقر المخصّص.