درحال ادغام و پاک‌سازی

ازآنجایی‌که خدمات دسترس‌پذیری در عناصر روی صفحه پیمایش می‌کنند، مهم است که این عناصر در سطح دانه‌بندی مناسب گروه‌بندی، جدا، یا حتی پنهان شوند. وقتی هر عنصر ترکیبی سطح پایین در صفحه‌نمایش به‌طور مستقل برجسته می‌شود، کاربران برای حرکت در صفحه‌نمایش باید تعامل زیادی داشته باشند. اگر عناصر به‌صورت تهاجمی با هم ادغام شوند، کاربران ممکن است متوجه نشوند کدام عناصر ازنظر منطقی با هم مرتبط هستند. اگر عناصری در صفحه وجود داشته باشد که صرفاً تزئینی باشند، این عناصر می‌توانند از سرویس‌های دسترس‌پذیری پنهان شوند. در این موارد، می‌توانید از «میاناهای برنامه‌سازی کاربردی نوشتن» برای ادغام، پاک کردن، و پنهان کردن معناشناسی استفاده کنید.

ادغام معنایی

وقتی یک اصلاح‌کننده clickable را به یک عنصر ترکیبی والد اعمال می‌کنید، Compose به‌طور خودکار همه عناصر فرزند را در زیر آن ادغام می‌کند. برای درک اینکه چگونه عناصر تعاملی «مجموعه Compose Material» و «پایه» به‌طور پیش‌فرض از استراتژی‌های ادغام استفاده می‌کنند، بخش عناصر تعاملی را ببینید.

معمولاً یک عنصر از چندین عنصر ترکیب‌شدنی تشکیل می‌شود. این عناصر ترکیبی می‌توانند گروه منطقی تشکیل دهند و هرکدام می‌توانند حاوی اطلاعات مهم باشند، اما همچنان ممکن است بخواهید خدمات دسترس‌پذیری آن‌ها را به‌عنوان یک عنصر مشاهده کنند.

برای مثال، به یک عنصر ترکیبی فکر کنید که چهرک کاربر، نام او، و اطلاعات اضافی را نشان می‌دهد:

گروهی از عناصر واسط کاربر شامل نام کاربر. نام انتخاب شده است.
شکل ۱. گروهی از عناصر واسط کاربر شامل نام کاربر. نام انتخاب شده است.

می‌توانید «نوشتن» را فعال کنید تا این عناصر را بااستفاده از پارامتر mergeDescendants در اصلاح‌گر معنایی ادغام کنید. به این ترتیب، خدمات دسترس‌پذیری با این عنصر به‌عنوان یک نهاد واحد رفتار می‌کنند و همه دارایی‌های معنایی فرزندان ادغام می‌شوند:

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

خدمات دسترس‌پذیری اکنون به‌طور هم‌زمان روی کل محتوی تمرکز می‌کند و محتوای آن را ادغام می‌کند:

گروهی از عناصر واسط کاربر شامل نام کاربر. همه عناصر باهم انتخاب می‌شوند.
شکل ۲. گروهی از عناصر واسط کاربر شامل نام کاربر. همه عناصر باهم انتخاب می‌شوند.

هر دارایی معنایی دارای استراتژی ادغام تعریف‌شده‌ای است. برای مثال، دارایی ContentDescription همه مقادیر ContentDescription فرزند را به فهرست اضافه می‌کند. با بررسی پیاده‌سازی mergePolicy آن در SemanticsProperties.kt می‌توانید استراتژی ادغام دارایی معنایی را بررسی کنید. دارایی‌ها می‌توانند مقدار والد یا فرزند را بگیرند، مقادیر را در فهرست یا رشته ادغام کنند، اصلاً اجازه ادغام ندهند و به‌جای آن استثنا ایجاد کنند، یا هر استراتژی ادغام سفارشی دیگری.

سناریوهای دیگری وجود دارد که در آن‌ها انتظار دارید معناشناسی کودکان در معناشناسی والدین ادغام شود، اما این اتفاق نمی‌افتد. در مثال زیر، ما clickable عنصر فرزند با عنصر والد فهرست داریم، و ممکن است انتظار داشته باشیم والد همه آن‌ها را ادغام کند:

مورد فهرست با تصویر، مقداری نوشتار، و نماد نشانک
شکل ۳. مورد فهرست با تصویر، مقداری نوشتار، و نماد نشانک.

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

وقتی کاربر روی clickable مورد Row فشار می‌دهد، مقاله باز می‌شود. درون آن، BookmarkButton برای نشانک‌گذاری مقاله وجود دارد. این دکمه تودرتو به‌صورت ادغام‌نشده نشان داده می‌شود، درحالی‌که بقیه محتوای فرزند در ردیف ادغام شده است:

درخت ادغام‌شده حاوی چندین متن در فهرستی درون گره «ردیف» است. درخت ادغام‌نشده حاوی گره‌های جداگانه برای هر عنصر ترکیبی «متن» است.
شکل ۴. درخت ادغام‌شده حاوی چندین نوشتار در فهرستی درون گره Row است. درخت ادغام‌نشده شامل گره‌های جداگانه برای هر Text عنصر ترکیبی
است.

برخی‌از عناصر ترکیبی طبق طراحی به‌طور خودکار زیر عنصر والد ادغام نمی‌شوند. وقتی فرزندان نیز درحال ادغام شدن باشند، والدین نمی‌توانند فرزندانشان را ادغام کنند، چه ازطریق تنظیم mergeDescendants = true به‌صورت صریح یا با تبدیل شدن به عناصری که خودشان ادغام می‌شوند، مثل دکمه‌ها یا عناصر کلیک‌کردنی. آگاهی از اینکه برخی‌از «میاناهای برنامه‌سازی کاربردی» چگونه ادغام می‌شوند یا از ادغام شدن جلوگیری می‌کنند می‌تواند به شما کمک کند برخی‌از عملکردهای بالقوه غیرمنتظره را اشکال‌زدایی کنید.

وقتی عناصر فرزند گروه منطقی و معقولی را تحت عنصر والد تشکیل می‌دهند، از ادغام استفاده کنید. اما اگر فرزندان تودرتو نیاز به اصلاح دستی یا حذف معناشناسی خودشان داشته باشند، ممکن است میاناهای برنامه‌سازی کاربردی دیگر برای نیازهای شما مناسب‌تر باشند (برای مثال، clearAndSetSemantics).

پاک کردن و تنظیم معناشناسی

اگر اطلاعات معنایی باید کاملاً پاک شود یا رونویسی شود، میانای برنامه‌سازی کاربردی قدرتمندی که باید استفاده کنید clearAndSetSemantics است.

وقتی یک عنصر نیاز دارد معناشناسی خودش و فرزندانش پاک شود، از این API با لامبدای خالی استفاده کنید. وقتی معناشناسی آن باید بازنویسی شود، محتوای جدیدتان را در لامبدا قرار دهید.

توجه داشته باشید که هنگام پاک کردن با لامبدای خالی، معناشناسی پاک‌شده به هیچ مصرف‌کننده‌ای که از این اطلاعات استفاده می‌کند، مانند دسترس‌پذیری، تکمیل خودکار، یا آزمایش، ارسال نمی‌شود. وقتی محتوا با clearAndSetSemantics{/*semantic information*/} بازنویسی می‌شود، معناشناسی جدید جایگزین همه معناشناسی‌های قبلی عنصر و فرزندان آن می‌شود.

در زیر نمونه‌ای از عنصر کلید تغییر وضعیت سفارشی نشان داده شده است که با یک ردیف تعاملی با نماد و نوشتار نشان داده می‌شود:

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

اگرچه نماد و نوشتار اطلاعات معنایی دارند، اما باهم نشان نمی‌دهند که این عنصر قابل روشن/خاموش شدن است. ادغام کافی نیست زیرا باید اطلاعات بیشتری درباره این عنصر ارائه دهید.

ازآنجایی‌که گلچین بالا عنصر دکمه‌ای سفارشی ایجاد می‌کند، باید قابلیت دکمه‌ای و همچنین معناشناسی stateDescription، toggleableState، و role را اضافه کنید. به این ترتیب، وضعیت عنصر و کنش مرتبط دردسترس است—برای مثال، TalkBack به‌جای «برای فعال کردن دو تک‌ضرب بزنید»، اعلام می‌کند «برای روشن/خاموش کردن دو تک‌ضرب بزنید».

با پاک کردن معناشناسی اصلی و تنظیم معناشناسی جدید و توصیفی‌تر، خدمات دسترس‌پذیری اکنون می‌توانند ببینند که این یک عنصر قابل‌تغییر است که می‌تواند وضعیت را تغییر دهد.

هنگام استفاده از clearAndSetSemantics، موارد زیر را درنظر بگیرید:

  • ازآنجایی‌که وقتی این API تنظیم می‌شود سرویس‌ها هیچ اطلاعاتی دریافت نمی‌کنند، بهتر است از آن به‌ندرت استفاده کنید.
    • عوامل هوش مصنوعی و سرویس‌های مشابه می‌توانند از اطلاعات معنایی برای درک صفحه‌نمایش استفاده کنند، بنابراین این اطلاعات فقط باید درصورت لزوم پاک شود.
  • معناشناسی سفارشی ممکن است در لامبدای API تنظیم شود.
  • ترتیب اصلاح‌کننده‌ها مهم است―این API همه معناشناسی‌هایی را که پس‌از محل اعمال آن قرار دارند پاک می‌کند، صرف‌نظر از استراتژی‌های ادغام دیگر.

پنهان کردن معناشناسی

در برخی‌از سناریوها، نیازی نیست عناصر به خدمات دسترس‌پذیری ارسال شوند—شاید اطلاعات اضافی آن‌ها برای دسترس‌پذیری زائد باشد، یا صرفاً جنبه تزئینی بصری و غیرتعاملی داشته باشد. در این موارد، می‌توانید عناصر را با hideFromAccessibility API پنهان کنید.

در مثال‌های زیر، عناصری وجود دارد که ممکن است نیاز به پنهان کردن داشته باشند: یک ته‌نقش اضافی که یک عنصر را پوشش می‌دهد، و یک نویسه که برای جدا کردن اطلاعات به‌صورت تزئینی استفاده می‌شود.

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

استفاده از hideFromAccessibility در اینجا تضمین می‌کند که ته‌نقش و تزئین از خدمات دسترس‌پذیری پنهان می‌شوند، اما همچنان معنای خود را برای موارد استفاده دیگر، مانند آزمایش، حفظ می‌کنند.

تفکیک موارد استفاده

در زیر خلاصه موارد استفاده برای درک نحوه تمایز واضح بین میاناهای برنامه‌سازی کاربردی قبلی آمده است:

  • وقتی محتوا برای استفاده توسط خدمات دسترس‌پذیری درنظر گرفته نشده است:
    • وقتی محتوا احتمالاً تزئینی یا اضافی است، اما همچنان باید آزمایش شود، از hideFromAccessibility استفاده کنید.
    • وقتی معناشناسی ولی و فرزندان باید برای همه سرویس‌ها پاک شود، از clearAndSetSemantics{} با لامبدای خالی استفاده کنید.
    • وقتی معناشناسی یک عنصر باید به‌صورت دستی تنظیم شود، از clearAndSetSemantics{/*content*/} با محتوای درون لامبدا استفاده کنید.
  • وقتی محتوا باید به‌عنوان یک نهاد درنظر گرفته شود و برای کامل بودن به اطلاعات همه فرزندانش نیاز دارد:
    • از نوادگان معنایی ادغام استفاده کنید.
جدول با موارد استفاده متمایز از API.
شکل ۵. جدول با موارد استفاده متمایز از میانای برنامه‌سازی کاربردی.