Oluşturma özelliğinde erişilebilirlik

Compose'da yazılan uygulamalar farklı gereksinimleri olan kullanıcılar için erişilebilirliği desteklemelidir. Erişilebilirlik hizmetleri, ekranda gösterilenleri belirli bir ihtiyacı olan kullanıcılara daha uygun bir biçime dönüştürmek için kullanılır. Erişilebilirlik hizmetlerini desteklemek amacıyla uygulamalar, kullanıcı arayüzü öğeleri hakkında semantik bilgileri göstermek için Android çerçevesindeki API'leri kullanır. Ardından Android çerçevesi erişilebilirlik hizmetlerini bu anlamsal bilgiler hakkında bilgilendirir. Her erişilebilirlik hizmeti, uygulamayı kullanıcıya en iyi nasıl açıklayacağını seçebilir. Android, TalkBack ve Anahtar Erişimi gibi çeşitli erişilebilirlik hizmetleri sağlar.

Anlambilim

Compose, erişilebilirlik hizmetlerine bilgi iletmek için anlamsal özellikleri kullanır. Anlamsal özellikler, kullanıcıya gösterilen kullanıcı arayüzü öğeleri hakkında bilgi sağlar. Text ve Button gibi çoğu yerleşik composable, bu semantik özelliklerini composable ve alt öğelerinden elde edilen bilgilerle doldurur. toggleable ve clickable gibi bazı değiştiriciler de belirli anlamsal özellikleri ayarlar. Ancak bazen bir kullanıcı arayüzü öğesinin kullanıcıya nasıl açıklanacağını anlamak için çerçevenin daha fazla bilgiye ihtiyacı olur.

Bu dokümanda, Android çerçevesiyle doğru şekilde açıklanabilmesi için bir composable'a açıkça ekstra bilgi eklemeniz gereken çeşitli durumlar açıklanmaktadır. Anlamsal bilgilerin belirli bir composable için tamamen nasıl değiştirileceği de açıklanmaktadır. Android'de erişilebilirlik ile ilgili temel bilgilere sahip olunduğunu varsayar.

Yaygın kullanım alanları

Erişilebilirlik ihtiyacı olan kullanıcıların uygulamanızı başarıyla kullanmasına yardımcı olmak için uygulamanızın bu sayfada açıklanan en iyi uygulamaları izlemesi gerekir.

Minimum dokunma hedefi boyutlarını göz önünde bulundurun

Ekranda gösterilen ve kullanıcıların tıklayabileceği, dokunabileceği veya etkileşimde bulunabileceği öğeler, güvenilir etkileşim için yeterli büyüklükte olmalıdır. Bu öğeleri boyutlandırırken Materyal Tasarım Erişilebilirlik Yönergeleri'ne doğru şekilde uymak için minimum boyutu 48 dp'ye ayarladığınızdan emin olun.

Checkbox, RadioButton, Switch, Slider ve Surface gibi malzeme bileşenleri, bu minimum boyutu dahili olarak yalnızca bileşen kullanıcı işlemleri alabildiği durumlarda ayarlar. Örneğin, bir Checkbox öğesinin onCheckedChange parametresi null olmayan bir değere ayarlandığında, en az 48 dp genişliğe ve yüksekliğe sahip olacak şekilde dolgu içerir.

@Composable
private fun CheckableCheckbox() {
    Checkbox(checked = true, onCheckedChange = {})
}

onCheckedChange parametresi null değerine ayarlandığında bileşenle doğrudan etkileşim kurulamayacağından dolgu dahil edilmez.

@Composable
private fun NonClickableCheckbox() {
    Checkbox(checked = true, onCheckedChange = null)
}

Switch, RadioButton veya Checkbox gibi seçim kontrollerini uygularken genellikle tıklanabilir davranışı üst kapsayıcıya kaldırır, composable'da tıklama geri çağırmasını null olarak ayarlar ve üst composable'a toggleable ya daselectable değiştiricisi eklersiniz.

@Composable
private fun CheckableRow() {
    MaterialTheme {
        var checked by remember { mutableStateOf(false) }
        Row(
            Modifier
                .toggleable(
                    value = checked,
                    role = Role.Checkbox,
                    onValueChange = { checked = !checked }
                )
                .padding(16.dp)
                .fillMaxWidth()
        ) {
            Text("Option", Modifier.weight(1f))
            Checkbox(checked = checked, onCheckedChange = null)
        }
    }
}

Tıklanabilir composable'ın boyutu minimum dokunma hedefi boyutundan küçük olsa da Oluşturma, dokunma hedefi boyutunu artırır. Bunu, dokunma hedefi boyutunu composable'ın sınırlarının dışına genişleterek yapılır.

Aşağıdaki örnekte çok küçük bir tıklanabilir Box oluşturduk. Dokunma hedefi alanı, otomatik olarak Box sınırlarının dışına genişletildiğinden Box öğesinin yanına dokunulması da tıklama etkinliğini tetikler.

@Composable
private fun SmallBox() {
    var clicked by remember { mutableStateOf(false) }
    Box(
        Modifier
            .size(100.dp)
            .background(if (clicked) Color.DarkGray else Color.LightGray)
    ) {
        Box(
            Modifier
                .align(Alignment.Center)
                .clickable { clicked = !clicked }
                .background(Color.Black)
                .size(1.dp)
        )
    }
}

Farklı composable'ların dokunma alanları arasında olası çakışmaları önlemek amacıyla her zaman composable için yeterince büyük bir minimum boyut kullanmayı hedeflemelisiniz. Örneğimizde bu, iç kutu için minimum boyutu ayarlamak üzere sizeIn değiştiricisini kullanmak anlamına gelir:

@Composable
private fun LargeBox() {
    var clicked by remember { mutableStateOf(false) }
    Box(
        Modifier
            .size(100.dp)
            .background(if (clicked) Color.DarkGray else Color.LightGray)
    ) {
        Box(
            Modifier
                .align(Alignment.Center)
                .clickable { clicked = !clicked }
                .background(Color.Black)
                .sizeIn(minWidth = 48.dp, minHeight = 48.dp)
        )
    }
}

Tıklama etiketi ekleme

Bir composable'ın tıklama davranışına anlamsal anlam eklemek için tıklama etiketi kullanabilirsiniz. Tıklama etiketleri, kullanıcı composable ile etkileşimde bulunduğunda ne olacağını açıklar. Erişilebilirlik hizmetleri, uygulamayı belirli ihtiyaçları olan kullanıcılara açıklamak için tıklama etiketleri kullanır.

clickable değiştiricide bir parametre ileterek tıklama etiketini ayarlayın:

@Composable
private fun ArticleListItem(openArticle: () -> Unit) {
    Row(
        Modifier.clickable(
            // R.string.action_read_article = "read article"
            onClickLabel = stringResource(R.string.action_read_article),
            onClick = openArticle
        )
    ) {
        // ..
    }
}

Alternatif olarak, tıklanabilir değiştiriciye erişiminiz yoksa tıklama etiketini anlamsal değiştiricide ayarlayabilirsiniz:

@Composable
private fun LowLevelClickLabel(openArticle: () -> Boolean) {
    // R.string.action_read_article = "read article"
    val readArticleLabel = stringResource(R.string.action_read_article)
    Canvas(
        Modifier.semantics {
            onClick(label = readArticleLabel, action = openArticle)
        }
    ) {
        // ..
    }
}

Görsel öğeleri açıklama

Bir Image veya Icon composable tanımladığınızda Android çerçevesinin görüntülenen içeriği anlamasını otomatik olarak sağlayacak bir yöntem yoktur. Görsel öğenin metin biçiminde bir açıklamasını iletmeniz gerekir.

Kullanıcının mevcut sayfayı arkadaşlarıyla paylaşabildiği bir ekran düşünün. Bu ekranda tıklanabilir paylaş simgesi bulunur:

Bu simgenin bulunduğu tıklanabilir simgeler şeridi

Android çerçevesi, sadece simgeye bakarak onu görme engelli bir kullanıcıya nasıl açıklayacağını belirleyemez. Android çerçevesi, simge için ek metin açıklaması gerektirir.

contentDescription parametresi, görsel öğeleri tanımlamak için kullanılır. Kullanıcıya iletileceği için yerelleştirilmiş bir dize kullanmalısınız.

@Composable
private fun ShareButton(onClick: () -> Unit) {
    IconButton(onClick = onClick) {
        Icon(
            imageVector = Icons.Filled.Share,
            contentDescription = stringResource(R.string.label_share)
        )
    }
}

Bazı görsel öğeler tamamen dekoratiftir ve bunları kullanıcıya anlatmak istemeyebilirsiniz. contentDescription parametresini null olarak ayarladığınızda Android çerçevesine bu öğenin ilişkili işlemleri veya durumu olmadığını belirtirsiniz.

@Composable
private fun PostImage(post: Post, modifier: Modifier = Modifier) {
    val image = post.imageThumb ?: painterResource(R.drawable.placeholder_1_1)

    Image(
        painter = image,
        // Specify that this image has no semantic meaning
        contentDescription = null,
        modifier = modifier
            .size(40.dp, 40.dp)
            .clip(MaterialTheme.shapes.small)
    )
}

Belirli bir görsel öğe için contentDescription gerekli olup olmadığına karar vermek size kalmıştır. Öğenin, kullanıcının görevini yerine getirmek için ihtiyaç duyduğu bilgileri aktarıp aktarmadığını kendinize sorun. Yoksa açıklamayı yerine koymamak daha iyidir.

Öğeleri birleştir

TalkBack ve Anahtar Erişimi gibi erişilebilirlik hizmetleri, kullanıcıların odağı ekrandaki öğeler arasında taşımasına olanak tanır. Öğelerin doğru ayrıntı düzeyinde odaklanması önemlidir. Ekranınızdaki her düşük seviye composable bağımsız olarak odaklanmışsa kullanıcının ekranda hareket etmek için çok fazla etkileşimde bulunması gerekir. Öğeler çok agresif bir şekilde birleştirilirse kullanıcılar hangi öğelerin birbirine ait olduğunu anlayamayabilir.

Bir composable'a clickable değiştirici uyguladığınızda Compose içerdiği tüm öğeleri otomatik olarak birleştirir. Bu durum ListItem için de geçerlidir. Bir liste öğesindeki öğeler birleştirilir ve erişilebilirlik hizmetleri bunları tek bir öğe olarak görür.

Mantıksal bir grup oluşturan bir grup composable'ın olması mümkündür, ancak bu grup tıklanabilir veya bir liste öğesinin parçası değildir. Erişilebilirlik hizmetlerinin bunları da tek bir öğe olarak görmesini istersiniz. Örneğin, bir kullanıcının avatarını, adını ve bazı ek bilgileri gösteren bir composable düşünün:

Kullanıcının adını içeren bir kullanıcı arayüzü öğeleri grubu. Ad seçilir.

semantics değiştiricisinde mergeDescendants parametresini kullanarak Compose'a bu öğeleri birleştirmesini söyleyebilirsiniz. Bu şekilde, erişilebilirlik hizmetleri yalnızca birleştirilmiş öğeyi seçer ve alt öğelerin tüm anlambilim özellikleri birleştirilir.

@Composable
private fun PostMetadata(metadata: Metadata) {
    // Merge elements below for accessibility purposes
    Row(modifier = Modifier.semantics(mergeDescendants = true) {}) {
        Image(
            imageVector = Icons.Filled.AccountCircle,
            contentDescription = null // decorative
        )
        Column {
            Text(metadata.author.name)
            Text("${metadata.date} • ${metadata.readTimeMinutes} min read")
        }
    }
}

Erişilebilirlik hizmetleri artık içeriklerini birleştirerek aynı anda tüm kapsayıcıya odaklanacak:

Kullanıcının adını içeren bir kullanıcı arayüzü öğeleri grubu. Tüm öğeler birlikte seçildi.

Özel işlemler ekleme

Şu liste öğesine göz atın:

Makale başlığını, yazarı ve yer işareti simgesini içeren tipik bir liste öğesidir.

Ekranda nelerin görüntülendiğini duymak için Talkback gibi bir ekran okuyucu kullandığınızda, uygulama önce öğenin tamamını, sonra da yer işareti simgesini seçer.

Tüm öğelerin birlikte seçildiği liste öğesi.

Yalnızca yer işareti simgesi seçili halde liste öğesi

Uzun bir listede bu durum çok fazla tekrarlanabilir. Kullanıcının öğeye yer işareti koymasına izin veren özel bir işlem tanımlamak daha iyi bir yaklaşım olur. Erişilebilirlik hizmeti tarafından seçilmemesi için yer işareti simgesinin davranışını açık bir şekilde kaldırmanız gerekeceğini de unutmayın. Bu, clearAndSetSemantics değiştiriciyle yapılır:

@Composable
private fun PostCardSimple(
    /* ... */
    isFavorite: Boolean,
    onToggleFavorite: () -> Boolean
) {
    val actionLabel = stringResource(
        if (isFavorite) R.string.unfavorite else R.string.favorite
    )
    Row(
        modifier = Modifier
            .clickable(onClick = { /* ... */ })
            .semantics {
                // Set any explicit semantic properties
                customActions = listOf(
                    CustomAccessibilityAction(actionLabel, onToggleFavorite)
                )
            }
    ) {
        /* ... */
        BookmarkButton(
            isBookmarked = isFavorite,
            onClick = onToggleFavorite,
            // Clear any semantics properties set on this node
            modifier = Modifier.clearAndSetSemantics { }
        )
    }
}

Bir elementin durumunu açıklama

Bir composable, Android çerçevesi tarafından composable'ın içinde bulunduğu durumu sesli olarak okumak için kullanılan, anlamlar için bir stateDescription tanımlayabilir. Örneğin, geçiş yapılabilir bir composable, "İşaretlendi" veya "İşaretsiz" durumunda olabilir. Bazı durumlarda, Oluştur tarafından kullanılan varsayılan durum açıklama etiketlerini geçersiz kılmak isteyebilirsiniz. Bunu, bir composable'ı geçiş yapılabilir olarak tanımlamadan önce durum açıklama etiketlerini açıkça belirterek yapabilirsiniz.

@Composable
private fun TopicItem(itemTitle: String, selected: Boolean, onToggle: () -> Unit) {
    val stateSubscribed = stringResource(R.string.subscribed)
    val stateNotSubscribed = stringResource(R.string.not_subscribed)
    Row(
        modifier = Modifier
            .semantics {
                // Set any explicit semantic properties
                stateDescription = if (selected) stateSubscribed else stateNotSubscribed
            }
            .toggleable(
                value = selected,
                onValueChange = { onToggle() }
            )
    ) {
        /* ... */
    }
}

Başlıkları tanımlayın

Uygulamalar bazen tek bir ekranda, kaydırılabilir bir kapsayıcıda o kadar çok içerik gösterir. Örneğin, bir ekranda kullanıcının okumakta olduğu makalenin tam içeriği gösterilebilir:

Makale metninin kaydırılabilir bir kapsayıcıda yer aldığı bir blog yayınının ekran görüntüsü.

Erişilebilirlik ihtiyacı olan kullanıcılar, böyle bir ekranda gezinmekte zorlanır. Gezinmeye yardımcı olması için hangi öğelerin başlık olduğunu belirtebilirsiniz. Yukarıdaki örnekte, her alt bölüm başlığı erişilebilirlik başlığı olarak tanımlanabilir. TalkBack gibi bazı erişilebilirlik hizmetleri, kullanıcıların doğrudan bir başlıktan başlığa gitmesine olanak tanır.

Compose'da bir composable'ın semantik özelliğini tanımlayarak bir başlık olduğunu belirtirsiniz:

@Composable
private fun Subsection(text: String) {
    Text(
        text = text,
        style = MaterialTheme.typography.headlineSmall,
        modifier = Modifier.semantics { heading() }
    )
}

Erişilebilirlik özelliklerinin otomatik olarak test edilmesi

Uygulamanızın anlamsal özelliklerini özelleştirirken (örneğin, yukarıda listelenen kullanım alanlarını takip ederken) otomatik kullanıcı arayüzü testlerini kullanarak doğruluğu doğrulayabilir ve regresyonları önleyebilirsiniz.

Örneğin, bir öğenin tıklama etiketinin doğru şekilde ayarlanıp ayarlanmadığını test etmek için aşağıdaki kodu kullanın:

@Test
fun test() {
    composeTestRule
        .onNode(nodeMatcher)
        .assert(
            SemanticsMatcher("onClickLabel is set correctly") {
                it.config.getOrNull(SemanticsActions.OnClick)?.label == "My Click Label"
            }
        )
}

Özel düşük seviye composable'lar oluşturma

Daha gelişmiş bir kullanım alanı, uygulamanızdaki belirli Material bileşenlerini özel sürümlerle değiştirmeyi içerir. Bu senaryoda erişilebilirlikle ilgili hususları aklınızda bulundurmanız çok önemlidir. Materyal Checkbox bölümünü kendi uygulamanızla değiştirdiğinizi varsayalım. Bu bileşenin erişilebilirlik özelliklerini işleyen triStateToggleable değiştiricisini eklemeyi unutmak çok kolay.

Genel bir kural olarak, bileşenin Malzeme kitaplığındaki uygulamasına bakmanız ve bulduğunuz erişilebilirlik davranışlarının aynısını yapmanız gerekir. Ek olarak, kullanıcı arayüzü düzeyi değiştiricilerinden farklı olarak Temel değiştiricileri yoğun bir şekilde kullanın. Bunlar, kullanıma hazır erişilebilirlik özellikleri içerir. Özel bileşen uygulamanızı birden çok erişilebilirlik hizmetiyle test ederek davranışını doğrulayın.

isTraversalGroup ve traversalIndex ile geçiş sırasını değiştirme

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üm tabanlı 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.

Bu sayfada, ekran okuyucu geçiş sırasını kontrol etmek için uygulamanızda isTraversalGroup ve traversalIndex özelliklerini nasıl kullanacağınız açıklanmaktadır.

Öğ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ü, işlevi alt öğelerini düzenlerken sınır veya sınır işlevi gören 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.

Bu örnekte, bir snippet isTraversalGroup kullanacak şekilde değiştirilmiştir. Aşağıdaki snippet 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ğda".

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ıralanırken CardBox sınırları dikkate alını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ğda."

Geçiş sırasını traversalIndex ile daha fazla özelleştirme

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 olmuyorsa ekran okuyucu sıralamasını daha da özelleştirmek için isTraversalGroup ile birlikte traversalIndex öğesini kullanabilirsiniz.

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ütun için isTraversalGroup politikası da ayarlanmadıkça bir Sütun için yalnızca traversalIndex belirlemenin hiç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çiciye dayalıdır.

Ü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 ekrana sayı eklendikçe her saat değerinin traversalIndex daha büyük olur. Saat değeri 0 olan traversalIndex değeri 0, saat değeri 1'in traversalIndex değeri 1 olur ve bu böyle devam eder. 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ı gruptaki diğer endekslere göre olduğundan ekran sıralamasının geri kalanı korunmuştur. Diğer bir deyişle, yukarıdaki 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, Materyal Tasarım'lı kayan işlem düğmesinin (FAB) geçiş sıralamasını kontrol etmek için traversalIndex ve isTraversalGroup kullanılmaktadır. Bu örnek, aşağıdaki düzene dayanır:

Ü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 yukarıdaki düzende TalkBack aşağıdaki sırayı alır:

Ü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 ekrandaki diğer tüm öğelerden önce geldiği anlamına gelir.

Daha sonra, kayan kutuyu ve diğer öğeleri bir yapıya yerleştirebilirsiniz. Bu yapı basit bir 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

Daha fazla bilgi

Compose kodunuzda erişilebilirliği destekleme hakkında daha fazla bilgi edinmek için Jetpack Compose'da erişilebilirlik codelab'ine göz atın.