Birleştirme ve temizleme

Erişilebilirlik hizmetleri ekrandaki öğeler arasında gezinirken bu öğelerin doğru ayrıntı düzeyinde gruplandırılması, ayrılması ve hatta gizlenmesi önemlidir. Ekranınızdaki her bir alt düzey composable bağımsız olarak vurgulandığında kullanıcıların ekranda gezinmek için çok fazla etkileşimde bulunması gerekir. Öğeler çok agresif bir şekilde birleştirilirse kullanıcılar hangi öğelerin mantıksal olarak bir arada olduğunu anlamayabilir. Ekranda yalnızca dekoratif amaçlı öğeler varsa bunlar erişilebilirlik hizmetlerinden gizlenebilir. Bu gibi durumlarda, semantiği birleştirmek, temizlemek ve gizlemek için Compose API'lerini kullanabilirsiniz.

Semantiği birleştirme

Üst düzey bir composable'a clickable değiştiricisi uyguladığınızda Compose, altındaki tüm alt öğeleri otomatik olarak birleştirir. Etkileşimli Compose Material 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 şekillendirilebilir öğeden oluşması yaygın bir durumdur. Bu composable'lar 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, kullanıcının avatarını, adını ve bazı ek bilgileri gösteren bir composable düşünün:

Kullanıcı adını içeren bir kullanıcı arayüzü öğeleri grubu. Ad seçilir.
Şekil 1. Kullanıcı adı da dahil olmak üzere bir grup kullanıcı arayüzü öğesi. Ad seçilir.

Semantik değiştiricideki mergeDescendants parametresini kullanarak Compose'un bu öğeleri birleştirmesini sağlayabilirsiniz. Bu sayede erişilebilirlik hizmetleri, bileşeni tek bir varlık olarak ele alır ve alt öğelerin tüm semantik ö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 tüm kapsayıcıya aynı anda odaklanır ve içeriğini birleştirir:

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

Her semantik özelliğin tanımlanmış bir birleştirme stratejisi vardır. Örneğin, ContentDescription özelliği, tüm alt ContentDescription değerlerini bir listeye ekler. Semantik özelliğinin birleştirme stratejisini, SemanticsProperties.kt dosyasındaki mergePolicy uygulamasını kontrol ederek öğrenebilirsiniz. Özellikler, üst veya alt değeri alabilir, değerleri bir liste ya da dize halinde birleştirebilir, birleştirmeye hiç izin vermeyip bunun yerine bir istisna oluşturabilir veya başka bir özel birleştirme stratejisi uygulayabilir.

Alt semantiklerin üst semantiklerle 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 var ve üst öğenin tüm alt öğeleri 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. Bunun içinde, makaleyi yer işaretlerine eklemek için bir BookmarkButton bulunur. Bu iç içe yerleştirilmiş düğme, birleştirilmemiş olarak gösterilirken satırdaki diğer alt içerikler birleştirilir:

Birleştirilmiş ağaç, Satır düğümünün içindeki bir listede birden fazla metin içerir. Birleştirilmemiş ağaç, her Text composable 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 composable için ayrı düğümler içerir.

Bazı composable'lar, tasarım gereği bir üst öğe altında otomatik olarak birleştirilmez. Alt öğeler de birleştirilirken üst öğe, alt öğelerini birleştiremez. Bu durum, mergeDescendants = true ayarının açıkça belirlenmesiyle veya düğmeler ya da tıklanabilir öğeler gibi kendilerini birleştiren bileşenler aracılığıyla gerçekleşebilir. Belirli API'lerin nasıl birleştirildiğini veya birleştirmenin nasıl engellendiğini bilmek, beklenmedik davranışlarda hata ayıklamanıza yardımcı olabilir.

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

Anlamsal öğeleri temizleme ve ayarlama

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

Bir bileşenin kendi semantiğinin ve alt bileşenlerinin semantiğinin temizlenmesi gerektiğinde bu API'yi boş bir lambda ile kullanın. Semantiğinin üzerine yazılması gerektiğinde yeni içeriğinizi lambda'ya ekleyin.

Boş bir lambda ile temizleme yapıldığında, temizlenen semantiklerin erişilebilirlik, otomatik doldurma veya test gibi bu bilgileri kullanan hiçbir tüketiciye gönderilmediğini unutmayın. İçerik clearAndSetSemantics{/*semantic information*/} ile üzerine 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 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ı semantik bilgiler içerse de birlikte bu bileşenin açma/kapatma düğmesi olduğunu göstermezler. Bileşen hakkında ek bilgi vermeniz gerektiğinden birleştirme yeterli değildir.

Yukarıdaki snippet özel bir açma/kapatma bileşeni oluşturduğundan açma/kapatma özelliğinin yanı sıra stateDescription, toggleableState ve role semantiğini de eklemeniz gerekir. Bu sayede, bileşen durumu ve ilişkili işlem kullanılabilir olur. Örneğin, TalkBack "Etkinleştirmek için iki kez dokunun" yerine "Açmak/kapatmak için iki kez dokunun" şeklinde duyuru yapar.

Orijinal semantiği temizleyip yeni ve daha açıklayıcı semantikler ayarlayarak erişilebilirlik hizmetlerinin, bunun durumu değişebilen, açılıp kapatılabilir bir bileşen olduğunu görmesini sağlayabilirsiniz.

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

  • Bu API ayarlandığında hizmetler bilgi almadığından, API'yi dikkatli kullanmak daha iyidir.
    • Semantik bilgiler, yapay zeka temsilcileri ve benzer hizmetler tarafından ekranı anlamak için kullanılabilir. Bu nedenle, yalnızca gerektiğinde temizlenmelidir.
  • Özel semantikler, API lambda içinde ayarlanabilir.
  • Değiştiricilerin sıralaması önemlidir. Bu API, diğer birleştirme stratejilerinden bağımsız olarak, uygulandığı yerden sonraki tüm semantiği temizler.

Anlamsal öğeleri gizle

Bazı senaryolarda öğ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'sini kullanarak öğeleri gizleyebilirsiniz.

Aşağıdaki örneklerde, gizlenmesi gerekebilecek bileşenler gösterilmektedir: bir bileşeni kapsayan 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ıldığında filigran ve dekorasyon, erişilebilirlik hizmetlerinden gizlenir ancak test gibi diğer kullanım alanları için semantiği korunur.

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 bir özeti verilmiştir:

  • İçeriğin erişilebilirlik hizmetleri tarafından kullanılması amaçlanmadığında:
    • İçerik muhtemelen dekoratif veya gereksiz olduğunda ancak yine de test edilmesi gerektiğinde hideFromAccessibility kullanın.
    • Üst ve alt semantiğin tüm hizmetler için temizlenmesi gerektiğinde boş bir lambda ile clearAndSetSemantics{} kullanın.
    • Bir bileşenin semantiğinin manuel olarak ayarlanması gerektiğinde lambda içindeki 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:
    • Anlamsal alt öğeleri birleştirme özelliğini kullanın.
Farklı API kullanım alanlarını içeren tablo.
Şekil 5. Farklı API kullanım alanlarını içeren tablo.