تعديل ترتيب التنقّل

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

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

استخدِم isTraversalGroup وtraversalIndex في تطبيقك للتحكّم في ترتيب التمرير في قارئ الشاشة.

تجميع العناصر للتنقّل

‫isTraversalGroup هي سمة منطقية تحدّد ما إذا كانت عقدة semantics تمثّل مجموعة تنقّل. هذا النوع من العُقد هو الذي تتمثل وظيفته في العمل كحدود في تنظيم العناصر الفرعية للعقدة.

يعني ضبط السمة isTraversalGroup = true على عقدة معيّنة أنّه تتم زيارة جميع العناصر الفرعية لتلك العقدة قبل الانتقال إلى عناصر أخرى. يمكنك ضبط isTraversalGroup على العُقد التي لا يمكن التركيز عليها في قارئ الشاشة، مثل الأعمدة أو الصفوف أو المربّعات.

يستخدم المثال التالي السمة isTraversalGroup. ويصدر أربعة عناصر نصية. ينتمي العنصران على اليمين إلى عنصر CardBox واحد، بينما ينتمي العنصران على اليسار إلى عنصر CardBox آخر:

// CardBox() function takes in top and bottom sample text.
@Composable
fun CardBox(
    topSampleText: String,
    bottomSampleText: String,
    modifier: Modifier = Modifier
) {
    Box(modifier) {
        Column {
            Text(topSampleText)
            Text(bottomSampleText)
        }
    }
}

@Composable
fun TraversalGroupDemo() {
    val topSampleText1 = "This sentence is in "
    val bottomSampleText1 = "the left column."
    val topSampleText2 = "This sentence is "
    val bottomSampleText2 = "on the right."
    Row {
        CardBox(
            topSampleText1,
            bottomSampleText1
        )
        CardBox(
            topSampleText2,
            bottomSampleText2
        )
    }
}

ينتج عن الرمز مخرجات مشابهة لما يلي:

تنسيق يتضمّن عمودَين من النص، حيث يظهر في العمود الأيمن النص "هذه الجملة في العمود الأيمن" وفي العمود الأيسر النص "هذه الجملة في العمود الأيسر".
الشكل 1. تنسيق يتضمّن جملتَين (واحدة في العمود الأيمن والأخرى في العمود الأيسر)

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

"هذه الجملة في" ← "هذه الجملة" ← "العمود الأيمن". → "على اليمين"

لترتيب الأجزاء بشكل صحيح، عدِّل المقتطف الأصلي لضبط isTraversalGroup على true:

@Composable
fun TraversalGroupDemo2() {
    val topSampleText1 = "This sentence is in "
    val bottomSampleText1 = "the left column."
    val topSampleText2 = "This sentence is"
    val bottomSampleText2 = "on the right."
    Row {
        CardBox(
//      1,
            topSampleText1,
            bottomSampleText1,
            Modifier.semantics { isTraversalGroup = true }
        )
        CardBox(
//      2,
            topSampleText2,
            bottomSampleText2,
            Modifier.semantics { isTraversalGroup = true }
        )
    }
}

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

والآن، يقرأ TalkBack أجزاء الجملة بالترتيب الصحيح:

"هذه الجملة في" ← "العمود الأيمن". → "هذه الجملة" → "على اليمين"

تخصيص ترتيب التمرير

traversalIndex هي سمة عائمة تتيح لك تخصيص ترتيب التمرير في TalkBack. إذا لم يكن تجميع العناصر معًا كافيًا لكي تعمل ميزة TalkBack بشكل صحيح، استخدِم traversalIndex مع isTraversalGroup لتخصيص ترتيب قارئ الشاشة بشكل أكبر.

تتضمّن السمة traversalIndex الخصائص التالية:

  • وتُعطى الأولوية أولاً للعناصر ذات قيم traversalIndex الأقل.
  • يمكن أن تكون إيجابية أو سلبية.
  • تكون القيمة التلقائية 0f.
  • لكي يؤثر فهرس المسح في سلوك المسح، يجب ضبطه على مكوّن يمكن اختياره والتركيز عليه من خلال خدمات تسهيل الاستخدام، مثل العناصر التي تظهر على الشاشة، كالنصوص أو الأزرار.
    • على سبيل المثال، لن يكون لضبط traversalIndex فقط على Column أي تأثير، ما لم يتم ضبط isTraversalGroup على العمود أيضًا.

يوضّح المثال التالي كيفية استخدام traversalIndex وisTraversalGroup معًا.

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

تصميم ساعة مع أداة اختيار الوقت فوقه
الشكل 2. صورة لتصميم ساعة

في المقتطف المبسّط التالي، هناك CircularLayout يتم فيه رسم 12 رقمًا، بدءًا من 12 والتحرك في اتجاه عقارب الساعة حول الدائرة:

@Composable
fun ClockFaceDemo() {
    CircularLayout {
        repeat(12) { hour ->
            ClockText(hour)
        }
    }
}

@Composable
private fun ClockText(value: Int) {
    Box(modifier = Modifier) {
        Text((if (value == 0) 12 else value).toString())
    }
}

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

@Composable
fun ClockFaceDemo() {
    CircularLayout(Modifier.semantics { isTraversalGroup = true }) {
        repeat(12) { hour ->
            ClockText(hour)
        }
    }
}

@Composable
private fun ClockText(value: Int) {
    Box(modifier = Modifier.semantics { this.traversalIndex = value.toFloat() }) {
        Text((if (value == 0) 12 else value).toString())
    }
}

لضبط ترتيب التمرير بشكل صحيح، يجب أولاً جعل CircularLayout مجموعة تمرير وضبط isTraversalGroup = true. بعد ذلك، عند رسم كل نص ساعة على التنسيق، اضبط السمة traversalIndex المقابلة له على قيمة العدّاد.

بما أنّ قيمة العدّاد تزداد باستمرار، تكون قيمة traversalIndex لكل قيمة ساعة أكبر مع إضافة الأرقام إلى الشاشة، فقيمة الساعة 0 لها قيمة traversalIndex تساوي 0، وقيمة الساعة 1 لها قيمة traversalIndex تساوي 1. بهذه الطريقة، يتم ضبط الترتيب الذي تقرأ به ميزة TalkBack العناصر. الآن، تتم قراءة الأرقام داخل CircularLayout بالترتيب المتوقع.

بما أنّ traversalIndexes التي تم ضبطها مرتبطة فقط بالفهارس الأخرى ضمن المجموعة نفسها، تم الحفاظ على بقية ترتيب الشاشة. بمعنى آخر، إنّ التغييرات الدلالية المعروضة في مقتطف الرمز السابق تعدّل فقط الترتيب داخل تصميم الساعة الذي تم ضبط isTraversalGroup = true له.

يُرجى العِلم أنّه بدون ضبط دلالات CircularLayout's على isTraversalGroup = true، سيستمر تطبيق تغييرات traversalIndex. ومع ذلك، بدون CircularLayout لربطها، تتم قراءة الأرقام الاثني عشر لتصميم الساعة أخيرًا، بعد زيارة جميع العناصر الأخرى على الشاشة. يحدث هذا لأن جميع العناصر الأخرى لها traversalIndex افتراضي من 0f، وتتم قراءة عناصر نص الساعة بعد جميع عناصر 0f الأخرى.

اعتبارات واجهة برمجة التطبيقات

يُرجى مراعاة ما يلي عند استخدام واجهات برمجة التطبيقات الخاصة بالتنقّل:

  • يجب ضبط السمة isTraversalGroup = true على العنصر الرئيسي الذي يحتوي على العناصر المجمّعة.
  • يجب ضبط السمة traversalIndex على مكوّن فرعي يحتوي على دلالات وسيتم اختياره من خلال خدمات تسهيل الاستخدام.
  • تأكَّد من أنّ جميع العناصر التي تفحصها تقع على مستوى zIndex نفسه، لأنّ ذلك يؤثر أيضًا في الدلالات وترتيب التمرير.
  • تأكَّد من عدم دمج أي دلالات بدون داعٍ، لأنّ ذلك قد يؤثر في المكوّنات التي يتم تطبيق فهارس التنقّل عليها.