Compose'da erişilebilirliği iyileştirmek için atılacak temel adımlar

Erişilebilirlik ihtiyacı olan kullanıcıların uygulamanızı başarıyla kullanmasına yardımcı olmak için uygulamanızı temel erişilebilirlik gereksinimlerini destekleyecek şekilde tasarlayın.

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önergelerini doğru şekilde uygulamak için minimum boyutu 48 dp'ye ayarladığınızdan emin olun.

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

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

Şekil 1. Dolgu içermeyen bir onay kutusu.

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 da selectable 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 bulunmaktadır. Dokunma hedefi alanı, otomatik olarak Box sınırlarının dışına genişletildiğinden Box öğesinin yanına dokunmak 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 composable için her zaman yeterince büyük bir minimum boyut kullanın. Bu örnekte, iç kutu için minimum boyutu ayarlamak üzere sizeIn değiştiricisinin kullanılması 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ştiricisinde 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ştiricisinde ayarlayın:

@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 uygulamanın neyi görüntülediğini otomatik olarak anlaması mümkün değildir. 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

Sadece simgeden yola çıkarak Android çerçevesi bunu görme engelli bir kullanıcıya açıklayamaz. Android çerçevesi, simge için ek bir metin açıklaması gerektirir.

contentDescription parametresi, görsel bir öğeyi açıklar. Kullanıcının görebileceği yerelleştirilmiş bir dize kullanın.

@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ç duyacağı 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 odaklandığında, kullanıcıların ekranda hareket etmek için çok fazla etkileşimde bulunması gerekir. Öğeler aşırı agresif bir şekilde birleştirilirse kullanıcılar hangi öğelerin

Bir composable'a clickable değiştirici uyguladığınızda Compose bu composable'ın 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üntüler.

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 bu öğeleri birleştirmek için Oluştur'u etkinleştirebilirsiniz. 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 container'a odaklanır:

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österildiğ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çilmediğinden emin olmak için yer işareti simgesinin davranışını açıkça kaldırmanız gerektiğini de unutmayın. Bu işlem, clearAndSetSemantics değiştiricisiyle 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 öğenin durumunu açıklama

composable, Android çerçevesinin composable'ın bulunduğu durumu sesli olarak okumak için kullandığı anlamlar için bir stateDescription tanımlayabilir. Örneğin, geçiş yapılabilir bir composable, "işaretli" veya "işaretsiz" durumunda olabilir. Bazı durumlarda, Oluştur'un kullandığı varsayılan durum açıklaması etiketlerini geçersiz kılmak isteyebilirsiniz. Bir composable'ı geçiş yapılabilir olarak tanımlamadan önce durum açıklama etiketlerini açıkça belirterek bunu 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 kaydırılabilir bir kapsayıcının içinde tek bir ekranda çok sayıda 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 gezinirken zorlanır. Gezinmeye yardımcı olmak için hangi öğelerin başlık olduğunu belirtin. Yukarıdaki örnekte, her alt bölüm başlığı bir 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 semantics özelliğini tanımlayarak bir composable'ın heading olduğunu belirtirsiniz:

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

Özel composable'ları yönetin

Uygulamanızdaki belirli Material bileşenlerini özel sürümlerle değiştirdiğinizde, erişilebilirlikle ilgili konuları aklınızda bulundurmanız gerekir.

Checkbox Materyalini kendi uygulamanızla değiştirdiğinizi varsayalım. Bu bileşenin erişilebilirlik özelliklerini işleyen triStateToggleable değiştiricisini eklemeyi unutabilirsiniz.

Genel bir kural olarak, bileşenin Malzeme kitaplığındaki uygulamasına bakın ve bulduğunuz erişilebilirlik davranışlarının aynısını yapın. 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.

Davranışını doğrulamak için özel bileşen uygulamanızı birden çok erişilebilirlik hizmetiyle test edin.

Ek kaynaklar