لماذا إمكانية الوصول في Kotlin هي قوة خارقة وليست مجرد علامة اختيار
\n\nتخيل: لقد كتبت تطبيقًا رائعًا بلغة Kotlin. تصميم Material جميل، رسوم متحركة سلسة، تدفقات تفاعلية. أنت فخور بالكود. ولكن بعد ذلك يتبين أن شخصًا ضعيف البصر لا يستطيع الضغط على زر "شراء"، لأن TalkBack يقرأه كـ "زر غير مُصنف". أو أن مستخدمًا يعاني من رعشة في اليد يخطئ في التبديل الصغير.
\n\nإمكانية الوصول (Accessibility) ليست بيروقراطية مملة. إنها تتعلق بجعل كود Kotlin الخاص بك يعمل للجميع. وفقًا لمنظمة الصحة العالمية، هناك أكثر من مليار شخص من ذوي الإعاقة. تجاهلهم يعني خسارة الجمهور، وبصراحة، كونك مطورًا سيئًا.
\n\nفي هذه المقالة، سنشرح كيفية كتابة كود متاح بلغة Kotlin دون ألم. بدون أطنان من الكود النمطي. مع أمثلة حية يمكنك نسخها ولصقها الآن.
\n\nالمبادئ الأساسية: ContentDescription هو أفضل صديق لك
\n\nأكثر فشل شائع في إمكانية الوصول هو عدم وجود وصف لعناصر الواجهة. نظام TalkBack (قارئ الشاشة على Android) لا يستطيع قراءة الصور أو الأيقونات. إنه يحتاج إلى نص.
\n\nفي Kotlin، يتم ذلك من خلال الخاصية contentDescription. ولكن ليس مجرد "زر"، بل بشكل ذي معنى.
مثال سيء (لا تفعل ذلك)
\nimageView.contentDescription = "زر"\n\nمثال جيد
\nbinding.fabAddTask.contentDescription = "إضافة مهمة جديدة"\n\nإذا كانت الأيقونة تزيينية (مجرد زخرفة) - قم بتعيين contentDescription = null لتجاهلها من قبل TalkBack.
// أيقونة تزيينية - لا نشتت المستخدم\nbinding.decorativeIcon.contentDescription = null\n\nنصيحة: استخدم موارد النصوص (strings.xml) لدعم التوطين. لا تقم بترميز النص العربي في ملفات Kotlin.
التصنيف الدلالي: إخبار النظام بما هو مهم
\n\nالعناصر القياسية View (Button, TextView) تؤدي المهمة بشكل جيد، لكن المكونات المخصصة غالبًا ما تكسر إمكانية الوصول. في Kotlin، يمكننا تحديد الدلالات بشكل صريح.
إضافة دور للعنصر
\nbinding.customView.importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES\nbinding.customView.accessibilityDelegate = object : View.AccessibilityDelegate() {\n override fun onInitializeAccessibilityNodeInfo(host: View, info: AccessibilityNodeInfo) {\n super.onInitializeAccessibilityNodeInfo(host, info)\n info.text = "القيمة الحالية: ${viewModel.value}"\n info.contentDescription = "منزلق الصوت"\n }\n}\n\nهذا الكود يخبر النظام: "مرحبًا، هذا ليس مجرد مربع، إنه منزلق!". سيفهم TalkBack أن العنصر تفاعلي وسيتمكن من التفاعل بشكل صحيح.
\n\nتجميع العناصر
\nإذا كان لديك بطاقة منتج تحتوي على صورة وسعر واسم - قم بدمجها في تركيز واحد. وإلا، سيستمع المستخدم إلى ثلاثة عناصر بشكل منفصل.
\n\nbinding.cardProduct.apply {\n isFocusable = true\n contentDescription = "المنتج: ${product.name}، السعر: ${product.price} ريال"\n}\n\nالآن سيقرأ TalkBack جميع المعلومات بنقرة واحدة.
\n\nالتنقل بالإجراءات: لوحة المفاتيح و D-pad
\n\nليس كل المستخدمين يلمسون الشاشة بأصابعهم. البعض يستخدم لوحة المفاتيح (عبر OTG) أو D-pad (جهاز التحكم). يجب أن يكون تطبيقك قابلاً للتحكم دون لمس.
\n\nفي Kotlin، يتم حل ذلك من خلال focusable وترتيب التركيز (nextFocusDown، nextFocusForward).
binding.buttonNext.nextFocusForwardId = R.id.buttonConfirm\nbinding.buttonConfirm.nextFocusDownId = R.id.buttonCancel\n\nتحقق من أن جميع العناصر التفاعلية تحصل على التركيز. خطأ شائع هو TextView مع ClickableSpan (رابط في النص). افتراضيًا، لا يتم التركيز عليه. نصلح ذلك:
binding.textWithLink.movementMethod = LinkMovement