Geçiş sırasını kontrol etme

Varsayılan olarak, bir Compose uygulamasındaki erişilebilirlik ekran okuyucu davranışı, beklenen okuma sırasına göre uygulanır. Bu genellikle soldan sağa, daha sonra yukarıdan aşağıya doğru olur. Ancak algoritmanın ek ipuçları olmadan gerçek okuma sırasını belirleyemediği bazı uygulama düzeni türleri de vardır. Görünüme dayalı uygulamalarda, bu sorunları traversalBefore ve traversalAfter özelliklerini kullanarak düzeltebilirsiniz. Compose 1.5'ten itibaren Compose da aynı ölçüde esnek bir API sunar ancak yeni bir kavramsal model sunar.

isTraversalGroup ve traversalIndex, varsayılan sıralama algoritmasının uygun olmadığı senaryolarda erişilebilirliği ve TalkBack odak sırasını kontrol etmenize olanak tanıyan semantik özelliklerdir. isTraversalGroup anlam açısından önemli grupları tanımlarken traversalIndex, bu gruplar içindeki bağımsız öğelerin sırasını ayarlar. isTraversalGroup tek başına veya daha fazla özelleştirme için traversalIndex ile kullanılabilir.

Ekran okuyucu geçiş sırasını kontrol etmek için uygulamanızda isTraversalGroup ve traversalIndex kullanın.

Öğeleri isTraversalGroup ile gruplandır

isTraversalGroup, bir anlamsal düğümünün bir geçiş grubu olup olmadığını tanımlayan bir boole özelliğidir. Bu düğüm türü, düğümün alt öğelerini düzenlerken işlevi sınır veya sınır görevi görecek düğümdür.

Bir düğümde isTraversalGroup = true politikasının ayarlanması, o düğümün tüm alt öğelerinin diğer öğelere geçmeden önce ziyaret edildiği anlamına gelir. isTraversalGroup öğesini; Sütunlar, Satırlar veya Kutular gibi ekran okuyucu dışındaki odaklanılabilir düğümlerde ayarlayabilirsiniz.

Aşağıdaki örnekte isTraversalGroup kullanılmıştır. Dört metin öğesi yayar. Soldaki iki öğe bir CardBox öğesine, sağdaki iki öğe ise başka bir CardBox öğesine aittir:

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

Bu kod aşağıdakine benzer bir çıkış üretir:

İki sütunlu metinden oluşan düzende soldaki sütunda "Bu cümle sol sütundadır", sağdaki sütunda ise "Bu cümle sağdadır" yazar.
Şekil 1. İki cümleli bir düzen (biri sol sütunda ve diğeri sağ sütunda).

Hiçbir semantik ayarlanmadığı için ekran okuyucunun varsayılan davranışı, öğeleri soldan sağa ve yukarıdan aşağıya kaydırmaktır. Bu varsayılan ayar nedeniyle TalkBack, cümle parçalarını yanlış sırada okur:

"Bu cümle şuradadır:" → "Bu cümle" → "sol sütun". → "sağ tarafta".

Parçaları doğru şekilde sıralamak için orijinal snippet'i isTraversalGroup değerini true olacak şekilde değiştirin:

@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 özellikle her CardBox için ayarlandığından öğeleri sıralarken CardBox sınırları uygulanır. Bu durumda, önce soldaki CardBox, ardından sağ CardBox okunur.

TalkBack artık cümle parçalarını doğru sırada okuyor:

"Bu cümle" → "sol sütunda". → "Bu cümle" → "sağ taraftadır."

Geçiş sırasını daha fazla özelleştirin

traversalIndex, TalkBack geçiş sırasını özelleştirmenize olanak tanıyan bir kayan özelliktir. Öğelerin birlikte gruplandırılması TalkBack'in düzgün çalışması için yeterli değilse ekran okuyucu sıralamasını daha da özelleştirmek için isTraversalGroup ile birlikte traversalIndex kullanın.

traversalIndex özelliği aşağıdaki özelliklere sahiptir:

  • İlk olarak, traversalIndex değerleri daha düşük olan öğelere öncelik verilir.
  • Pozitif veya olumsuz olabilir.
  • Varsayılan değer 0f değeridir.
  • Yalnızca metin veya düğmeler gibi ekrandaki öğeler gibi ekran okuyucuya odaklanılabilir düğümleri etkiler. Örneğin, bir sütunda yalnızca traversalIndex değerinin ayarlanması (sütun için de isTraversalGroup değeri ayarlanmadıkça) herhangi bir etkisi olmaz.

Aşağıdaki örnekte traversalIndex ve isTraversalGroup özelliklerini birlikte nasıl kullanabileceğiniz gösterilmektedir.

Örnek: Çapraz saat kadranı

Saat kadranı, standart geçiş sıralamasının çalışmadığı yaygın bir senaryodur. Bu bölümdeki örnek, kullanıcının saat kadranındaki sayılar arasında gezinip saat ve dakika aralıkları için rakamları seçebileceği bir zaman seçicidir.

Üzerinde saat seçici bulunan kadran.
Şekil 2. Saat kadranı resmi.

Aşağıdaki basitleştirilmiş snippet'te, 12 ile başlayan ve çember etrafında saat yönünde hareket eden 12 rakamın çizildiği bir CircularLayout vardır:

@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())
    }
}

Saat kadranı varsayılan soldan sağa ve yukarıdan aşağıya sıralamayla mantıklı bir şekilde okunmadığından, TalkBack sayıları sırayla okur. Bu durumu düzeltmek için aşağıdaki snippet'te gösterildiği gibi artan sayaç değerini kullanın:

@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())
    }
}

Geçiş sırasını doğru şekilde ayarlamak için önce CircularLayout grubunu bir geçiş grubu yapın ve isTraversalGroup = true değerini ayarlayın. Ardından, her saat metni düzene çizilirken karşılık gelen traversalIndex değerini sayaç değerine ayarlayın.

Sayaç değeri sürekli olarak arttığı için her saat değerinin traversalIndex değeri, ekrana sayı eklendikçe büyür. Saat değeri 0'ın traversalIndex değeri 0, saat değeri 1'in traversalIndex değeri ise 1 olur. Bu şekilde, TalkBack'in mesajları okuyacağı sıra ayarlanır. Artık CircularLayout içindeki sayılar beklenen sırada okunur.

Ayarlanan traversalIndexes yalnızca aynı gruplandırma içindeki diğer dizinlere göre olduğundan ekran sıralamasının geri kalanı korunmuştur. Başka bir deyişle, önceki kod snippet'inde gösterilen anlamsal değişiklikler yalnızca isTraversalGroup = true öğesinin ayarlandığı saat kadranındaki sıralamayı değiştirir.

CircularLayout's semantiği isTraversalGroup = true olarak ayarlanmazsa traversalIndex değişikliklerinin geçerli olmaya devam edeceğini unutmayın. Ancak bu işlemleri bağlayabileceğiniz CircularLayout olmadığında saat kadranının on iki basamağı, ekrandaki diğer tüm öğeler ziyaret edildikten sonra en son okunur. Bunun nedeni, diğer tüm öğelerin varsayılan traversalIndex değerinin 0f olması ve saat metin öğelerinin diğer tüm 0f öğelerinden sonra okunmasıdır.

Örnek: Kayan işlem düğmesi için geçiş sırasını özelleştirme

Bu örnekte traversalIndex ve isTraversalGroup, Materyal Tasarım kayan işlem düğmesinin (FAB) geçiş sıralamasını kontrol eder. Bu örneğin temeli aşağıdaki düzendir:

Üst uygulama çubuğu, örnek metin, kayan işlem düğmesi ve alt uygulama çubuğu olan bir düzen.
Şekil 3. Üst uygulama çubuğu, örnek metin, kayan işlem düğmesi ve alt uygulama çubuğu içeren düzen.

Varsayılan olarak, bu örnekteki düzen TalkBack sıralamasına sahiptir:

Üst Uygulama Çubuğu → 0-6 arası örnek metinler → kayan işlem düğmesi (FAB) → Alt Uygulama Çubuğu

Ekran okuyucunun önce FAB'a odaklanmasını isteyebilirsiniz. FAB gibi bir Materyal öğesinde traversalIndex ayarlamak için aşağıdakileri yapın:

@Composable
fun FloatingBox() {
    Box(modifier = Modifier.semantics { isTraversalGroup = true; traversalIndex = -1f }) {
        FloatingActionButton(onClick = {}) {
            Icon(imageVector = Icons.Default.Add, contentDescription = "fab icon")
        }
    }
}

Bu snippet'te, isTraversalGroup değeri true olarak ayarlanmış bir kutu oluşturup aynı kutu için bir traversalIndex değeri belirlemek (-1f, 0f olan varsayılan değerden daha düşüktür) kayan kutunun diğer tüm ekrandaki öğelerden önce geldiği anlamına gelir.

Daha sonra, kayan kutuyu ve diğer öğeleri bir yapıya yerleştirebilirsiniz. Bu yapı, Materyal Tasarım düzeni uygular:

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ColumnWithFABFirstDemo() {
    Scaffold(
        topBar = { TopAppBar(title = { Text("Top App Bar") }) },
        floatingActionButtonPosition = FabPosition.End,
        floatingActionButton = { FloatingBox() },
        content = { padding -> ContentColumn(padding = padding) },
        bottomBar = { BottomAppBar { Text("Bottom App Bar") } }
    )
}

TalkBack, öğelerle aşağıdaki sırada etkileşime geçer:

FAB → Üst Uygulama Çubuğu → 0'dan 6'ya kadar örnek metinler → Alt Uygulama Çubuğu

Ek kaynaklar