Birleştirme ve temizleme

Erişilebilirlik hizmetleri ekrandaki öğeler arasında gezinirken bu öğelerin doğru ayrıntı düzeyinde gruplandırılmış, ayrılmış veya hatta gizlenmiş olması önemlidir. Ekranınızdaki her düşük düzey bileşen bağımsız olarak vurgulandığında kullanıcıların ekranda gezinmek için çok fazla etkileşim kurması gerekir. Öğeler çok agresif bir şekilde birleştirilirse kullanıcılar hangi öğelerin mantıksal olarak birlikte ait olduğunu anlayamayabilir. Ekranda tamamen dekoratif öğeler varsa bunlar erişilebilirlik hizmetlerinden gizlenebilir. Bu gibi durumlarda, anlamları birleştirmek, temizlemek ve gizlemek için Compose API'lerini kullanabilirsiniz.

Birleştirme semantikleri

Bir üst bileşene clickable değiştirici uyguladığınızda, Oluştur, altındaki tüm alt öğeleri otomatik olarak birleştirir. Etkileşimli Compose materyali ve Foundation bileşenlerinin varsayılan olarak birleştirme stratejilerini nasıl kullandığını anlamak için Etkileşimli öğeler bölümüne bakın.

Bir bileşenin birden fazla bileşenden oluşması yaygın bir durumdur. Bu bileşenler mantıksal bir grup oluşturabilir ve her biri önemli bilgiler içerebilir. Ancak yine de erişilebilirlik hizmetlerinin bunları tek bir öğe olarak görüntülemesini isteyebilirsiniz.

Örneğin, bir kullanıcının avatarını, adını ve bazı ek bilgileri gösteren bir bileşeni düşünün:

Kullanıcının adını içeren bir kullanıcı arayüzü öğesi grubu. Ad seçilidir.
Şekil 1. Kullanıcının adını içeren bir kullanıcı arayüzü öğesi grubu. Ad seçili olmalıdır.

Anlam değiştiricide mergeDescendants parametresini kullanarak bu öğeleri birleştirmek için Oluştur'u etkinleştirebilirsiniz. Bu sayede erişilebilirlik hizmetleri, bileşeni tek bir varlık olarak ele alır 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 kapsayıcının tamamına odaklanır ve içeriğini birleştirir:

Kullanıcının adını içeren bir kullanıcı arayüzü öğesi grubu. Tüm öğeler birlikte seçilir.
Şekil 2. Kullanıcının adını içeren bir kullanıcı arayüzü öğesi grubu. Tüm öğeler birlikte seçilir.

Her anlamsal mülkün tanımlanmış bir birleştirme stratejisi vardır. Örneğin, ContentDescription mülkü tüm alt öğe ContentDescription değerlerini bir listeye ekler. Bir semantik mülkün birleştirme stratejisini, SemanticsProperties.kt dosyasında mergePolicy uygulamasını kontrol ederek kontrol edebilirsiniz. Mülkler, üst veya alt öğe değerini alabilir, değerleri bir liste veya dize halinde birleştirebilir, hiç birleştirilmesine izin vermeyip bunun yerine bir istisna oluşturabilir ya da başka bir özel birleştirme stratejisi uygulayabilir.

Alt öğelerin semantiklerinin bir üst öğeyle birleştirilmesini beklediğiniz ancak bunun gerçekleşmediği başka senaryolar da vardır. Aşağıdaki örnekte, alt öğeleri olan bir clickable liste öğesi üst öğesi vardır ve üst öğenin bunların tümünü birleştirmesini bekleyebiliriz:

Resim, metin ve yer işareti simgesi içeren liste öğesi
Şekil 3. Resim, metin ve yer işareti simgesi içeren liste öğesi.

@Composable
private fun ArticleListItem(
    openArticle: () -> Unit,
    addToBookmarks: () -> Unit,
) {

    Row(modifier = Modifier.clickable { openArticle() }) {
        // Merges with parent clickable:
        Icon(
            painter = painterResource(R.drawable.ic_logo),
            contentDescription = "Article thumbnail"
        )
        ArticleDetails()

        // Defies the merge due to its own clickable:
        BookmarkButton(onClick = addToBookmarks)
    }
}

Kullanıcı clickable öğesine Row bastığında makale açılır. İçinde, makaleye yer işareti koymak için BookmarkButton simgesi bulunur. Satırdaki diğer alt içerikler birleştirilmişken bu iç içe yerleştirilmiş düğme birleştirilmemiş olarak görünür:

Birleştirilmiş ağaç, satır düğümünün içindeki bir listede birden fazla metin içerir. Birleştirilmemiş ağaç, her metin bileşeni için ayrı düğümler içerir.
Şekil 4. Birleştirilmiş ağaç, Row düğümünün içindeki bir listede birden fazla metin içerir. Birleştirilmemiş ağaç, her Text bileşeni
için ayrı düğümler içerir.

Bazı derlenebilirler, tasarım gereği bir üst öğe altında otomatik olarak birleştirilmez. Alt öğeler de birleştirilirken mergeDescendants = true ayarını açıkça yaparak veya düğmeler ya da tıklanabilir öğeler gibi kendilerini birleştiren bileşenler olarak bir üst öğe, alt öğelerini birleştiremez. Belirli API'lerin nasıl birleştirildiğini veya birleştirilmediğini bilmek, beklenmedik olabilecek bazı davranışlarla ilgili sorunları gidermenize yardımcı olabilir.

Alt öğeler, üst öğelerinin altında mantıklı ve makul bir grup oluşturduğunda birleştirme işlemini kullanın. Ancak iç içe yerleştirilmiş alt öğelerin manuel olarak ayarlanması veya kendi anlamlarının kaldırılması gerekiyorsa ihtiyaçlarınıza diğer API'ler (ör. clearAndSetSemantics) daha uygun olabilir.

Anlamları temizleme ve ayarlama

Anlamsal bilgilerin tamamen silinmesi veya üzerine yazılması gerekiyorsa clearAndSetSemantics güçlü bir API'dir.

Bir bileşenin kendi ve alt öğelerinin semantiklerinin temizlenmesi gerektiğinde bu API'yi boş bir lambda ile kullanın. Anlamının üzerine yazılmasının gerektiği durumlarda yeni içeriğinizi lambda içine ekleyin.

Boş bir lambda ile temizleme işlemi yapılırken, temizlenen semantiklerin erişilebilirlik, otomatik doldurma veya test gibi bu bilgileri kullanan tüketicilere gönderilmediğini unutmayın. İçeriğin üzerine clearAndSetSemantics{/*semantic information*/} ile yazıldığında yeni semantikler, öğenin ve alt öğelerinin önceki tüm semantiklerinin yerini alır.

Aşağıda, simge ve metin içeren etkileşimli bir satırla temsil edilen özel bir açma/kapatma düğmesi bileşeni örneği verilmiştir:

// Developer might intend this to be a toggleable.
// Using `clearAndSetSemantics`, on the Row, a clickable modifier is applied,
// a custom description is set, and a Role is applied.

@Composable
fun FavoriteToggle() {
    val checked = remember { mutableStateOf(true) }
    Row(
        modifier = Modifier
            .toggleable(
                value = checked.value,
                onValueChange = { checked.value = it }
            )
            .clearAndSetSemantics {
                stateDescription = if (checked.value) "Favorited" else "Not favorited"
                toggleableState = ToggleableState(checked.value)
                role = Role.Switch
            },
    ) {
        Icon(
            imageVector = Icons.Default.Favorite,
            contentDescription = null // not needed here

        )
        Text("Favorite?")
    }
}

Simge ve metin bazı anlamsal bilgilere sahip olsa da birlikte bu bileşenin açma/kapatma düğmesi olduğunu belirtmez. Bileşen hakkında ek bilgi sağlamanız gerektiği için birleştirme işlemi yeterli değildir.

Yukarıdaki snippet özel bir açma/kapatma düğmesi bileşeni oluşturduğundan açma/kapatma özelliğinin yanı sıra stateDescription, toggleableState ve role semantiklerini eklemeniz gerekir. Bu sayede bileşen durumu ve ilişkili işlem kullanılabilir. Örneğin, TalkBack "Etkinleştirmek için iki kez dokunun" yerine "Etkinleştirmek veya devre dışı bırakmak için iki kez dokunun"u duyurur.

Orijinal anlamları temizleyip daha açıklayıcı yeni anlamlar ayarlayarak erişilebilirlik hizmetleri artık bunun durumunu değiştirebilen bir açma/kapatma düğmesi bileşeni olduğunu görebilir.

clearAndSetSemantics'ü kullanırken aşağıdakileri göz önünde bulundurun:

  • Bu API ayarlandığında hizmetler hiçbir bilgi almadığı için bu API'yi az miktarda kullanmak daha iyidir.
    • Anlam bilgisi, ekranı anlamak için yapay zeka aracıları ve benzer hizmetler tarafından kullanılabileceğinden yalnızca gerektiğinde temizlenmelidir.
  • API lambda içinde özel anlamlar ayarlanabilir.
  • Değiştiricilerin sıralaması önemlidir. Bu API, diğer birleştirme stratejilerinden bağımsız olarak, uygulandığı yerden sonraki tüm semantikleri temizler.

Anlamları gizle

Bazı durumlarda öğelerin erişilebilirlik hizmetlerine gönderilmesi gerekmez. Bu öğelerin ek bilgileri erişilebilirlik için gereksiz olabilir veya tamamen görsel olarak dekoratif ve etkileşimli olmayabilir. Bu gibi durumlarda, hideFromAccessibility API'yi kullanarak öğeleri gizleyebilirsiniz.

Aşağıdaki örneklerde, gizlenmesi gerekebilecek bileşenler gösterilmektedir: Bir bileşeni kaplayan gereksiz bir filigran ve bilgileri dekoratif olarak ayırmak için kullanılan bir karakter.

@Composable
fun WatermarkExample(
    watermarkText: String,
    content: @Composable () -> Unit,
) {
    Box {
        WatermarkedContent()
        // Mark the watermark as hidden to accessibility services.
        WatermarkText(
            text = watermarkText,
            color = Color.Gray.copy(alpha = 0.5f),
            modifier = Modifier
                .align(Alignment.BottomEnd)
                .semantics { hideFromAccessibility() }
        )
    }
}

@Composable
fun DecorativeExample() {
    Text(
        modifier =
        Modifier.semantics {
            hideFromAccessibility()
        },
        text = "A dot character that is used to decoratively separate information, like •"
    )
}

Burada hideFromAccessibility kullanılması, filigran ve süslemenin erişilebilirlik hizmetlerinden gizlenmesini sağlar ancak test gibi diğer kullanım alanları için anlamlarını korur.

Kullanım alanlarının dökümü

Aşağıda, önceki API'ler arasındaki farkları net bir şekilde anlamak için kullanım alanlarının özeti verilmiştir:

  • İçerik, erişilebilirlik hizmetleri tarafından kullanılmak üzere tasarlanmadığında:
    • İçerik muhtemelen dekoratif veya gereksiz olsa da yine de test edilmesi gerektiğinde hideFromAccessibility simgesini kullanın.
    • Tüm hizmetler için ebeveyn ve çocuk semantiklerinin temizlenmesi gerektiğinde boş bir lambda ile clearAndSetSemantics{} kullanın.
    • Bir bileşenin semantiklerinin manuel olarak ayarlanması gerektiğinde lambda içinde içerikle birlikte clearAndSetSemantics{/*content*/} kullanın.
  • İçeriğin tek bir varlık olarak ele alınması gerektiğinde ve tüm alt öğelerinin bilgilerinin eksiksiz olması gerektiğinde:
    • Semantik alt öğeleri birleştirme özelliğini kullanın.
Farklı API kullanım alanlarını gösteren tablo.
Şekil 5. Farklı API kullanım alanlarını gösteren tablo.