مراحل کلیدی برای بهبود دسترسی Compose

برای کمک به افرادی که نیازهای دسترسی دارند از برنامه شما با موفقیت استفاده کنند، برنامه خود را طوری طراحی کنید که از الزامات دسترسی کلیدی پشتیبانی کند.

حداقل اندازه های هدف لمسی را در نظر بگیرید

هر عنصر روی صفحه‌ای که کسی می‌تواند روی آن کلیک کند، لمس کند یا با آن تعامل داشته باشد باید به اندازه کافی بزرگ باشد تا تعامل قابل اعتمادی داشته باشد. هنگام اندازه‌گیری این عناصر، مطمئن شوید که حداقل اندازه را روی 48dp تنظیم کنید تا به درستی از دستورالعمل‌های دسترسی طراحی مواد پیروی کنید.

اجزای متریال - مانند Checkbox ، RadioButton ، Switch ، Slider و Surface - این حداقل اندازه را به صورت داخلی تنظیم می‌کنند، اما فقط زمانی که مؤلفه بتواند اقدامات کاربر را دریافت کند. به عنوان مثال، هنگامی که یک Checkbox پارامتر onCheckedChange خود را روی یک مقدار غیر تهی تنظیم می کند، چک باکس شامل padding می شود که عرض و ارتفاع حداقل 48 dp داشته باشد.

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

هنگامی که پارامتر onCheckedChange روی null تنظیم می شود، padding شامل نمی شود، زیرا نمی توان مستقیماً با مؤلفه تعامل کرد.

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

شکل 1. یک چک باکس بدون بالشتک.

هنگام اجرای کنترل‌های انتخابی مانند Switch ، RadioButton ، یا Checkbox ، معمولاً رفتار قابل کلیک را به یک محفظه والد برمی‌دارید، بازگشت تماس کلیکی را در composable روی null تنظیم می‌کنید و یک اصلاح‌کننده toggleable یا selectable به Composable والد اضافه می‌کنید.

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

هنگامی که اندازه یک ترکیب قابل کلیک کوچکتر از حداقل اندازه هدف لمسی باشد، Compose همچنان اندازه هدف لمسی را افزایش می دهد. این کار را با گسترش اندازه هدف لمسی در خارج از مرزهای قابل ترکیب انجام می دهد.

مثال زیر حاوی یک Box بسیار کوچک قابل کلیک است. ناحیه هدف لمسی به طور خودکار فراتر از مرزهای Box گسترش می‌یابد، بنابراین ضربه زدن در کنار Box همچنان رویداد کلیک را آغاز می‌کند.

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

برای جلوگیری از همپوشانی احتمالی بین نواحی لمسی مواد ترکیب‌پذیر مختلف، همیشه از حداقل اندازه کافی بزرگ برای ترکیب‌سازی استفاده کنید. در مثال، این به معنای استفاده از تغییر دهنده sizeIn برای تنظیم حداقل اندازه برای جعبه داخلی است:

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

برچسب های کلیک را اضافه کنید

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

برچسب کلیک را با ارسال یک پارامتر در اصلاح کننده clickable تنظیم کنید:

@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
        )
    ) {
        // ..
    }
}

همچنین، اگر به اصلاح کننده قابل کلیک دسترسی ندارید، برچسب کلیک را در اصلاح کننده معنایی تنظیم کنید:

@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)
        }
    ) {
        // ..
    }
}

عناصر بصری را توصیف کنید

وقتی یک Image یا Icon قابل ترکیب تعریف می‌کنید، هیچ راه خودکاری برای چارچوب Android برای درک آنچه برنامه نمایش می‌دهد وجود ندارد. شما باید یک توضیح متنی از عنصر بصری را ارسال کنید.

صفحه ای را تصور کنید که در آن کاربر می تواند صفحه فعلی را با دوستان خود به اشتراک بگذارد. این صفحه شامل یک نماد اشتراک گذاری قابل کلیک است:

نواری از نمادهای قابل کلیک، با

بر اساس آیکون به تنهایی، چارچوب اندروید نمی تواند آن را برای یک کاربر کم بینا توصیف کند. فریم ورک اندروید نیاز به توضیح متنی اضافی از نماد دارد.

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

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

برخی از عناصر بصری صرفاً تزئینی هستند و ممکن است نخواهید آنها را به کاربر منتقل کنید. هنگامی که پارامتر contentDescription را روی null تنظیم می کنید، به چارچوب Android نشان می دهید که این عنصر عملکرد یا وضعیت مرتبطی ندارد.

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

این شما هستید که تصمیم می گیرید آیا یک عنصر بصری معین به یک contentDescription نیاز دارد یا خیر. از خود بپرسید که آیا این عنصر اطلاعاتی را که کاربر برای انجام وظیفه خود به آن نیاز دارد منتقل می کند یا خیر. اگر نه، بهتر است توضیحات را کنار بگذارید.

ادغام عناصر

سرویس‌های دسترس‌پذیری مانند Talkback و Switch Access به کاربران اجازه می‌دهند تا فوکوس را روی عناصر روی صفحه تغییر دهند. مهم است که عناصر در دانه بندی مناسب متمرکز شوند. هنگامی که تک تک قطعات کامپوزیشن سطح پایین در صفحه نمایش شما به طور مستقل فوکوس می شود، کاربران باید برای حرکت در صفحه نمایش تعامل زیادی داشته باشند. اگر عناصر به شدت با هم ادغام شوند، کاربران ممکن است متوجه نشوند که کدام عناصر به یکدیگر تعلق دارند

هنگامی که یک اصلاح کننده clickable را روی یک composable اعمال می کنید، Compose به طور خودکار همه عناصر موجود در composable را ادغام می کند. این همچنین برای ListItem صادق است. عناصر موجود در یک آیتم لیست با هم ادغام می شوند و سرویس های دسترسی آنها را به عنوان یک عنصر می بینند.

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

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

می توانید Compose را برای ادغام این عناصر با استفاده از پارامتر mergeDescendants در اصلاح کننده semantics فعال کنید. به این ترتیب، سرویس‌های دسترسی فقط عنصر ادغام شده را انتخاب می‌کنند و تمام ویژگی‌های معنایی فرزندان ادغام می‌شوند.

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

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

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

افزودن اقدامات سفارشی

به آیتم لیست زیر نگاهی بیندازید:

یک آیتم لیست معمولی، حاوی عنوان مقاله، نویسنده و نماد نشانک.

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

مورد فهرست، با همه عناصر انتخاب شده با هم.

مورد فهرست، تنها با انتخاب نماد نشانک

در یک لیست طولانی، این می تواند بسیار تکراری شود. یک رویکرد بهتر، تعریف یک اقدام سفارشی است که به کاربر اجازه می دهد آیتم را نشانک کند. به خاطر داشته باشید که باید رفتار خود نماد نشانک را نیز به صراحت حذف کنید تا مطمئن شوید که توسط سرویس دسترس‌پذیری انتخاب نشده است. این کار با اصلاح کننده clearAndSetSemantics انجام می شود:

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

وضعیت یک عنصر را توصیف کنید

یک composable می‌تواند یک stateDescription برای معناشناسی تعریف کند که چارچوب Android از آن برای خواندن وضعیتی که composable در آن قرار دارد استفاده می‌کند. به عنوان مثال، یک composable قابل تغییر می‌تواند در حالت "checked" یا "unchecked" باشد. در برخی موارد، ممکن است بخواهید برچسب‌های توصیف حالت پیش‌فرض را که Compose استفاده می‌کند لغو کنید. می‌توانید این کار را با مشخص کردن صریح برچسب‌های توصیف وضعیت قبل از تعریف یک composable به عنوان toggleable انجام دهید:

@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() }
            )
    ) {
        /* ... */
    }
}

سرفصل ها را تعریف کنید

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

عکس صفحه یک پست وبلاگ، با متن مقاله در یک ظرف قابل پیمایش.

کاربرانی که نیازهای دسترسی دارند در پیمایش چنین صفحه‌ای مشکل دارند. برای کمک به ناوبری، مشخص کنید کدام عناصر سرفصل هستند. در مثال قبل، هر عنوان زیربخش را می توان به عنوان عنوانی برای دسترسی تعریف کرد. برخی از سرویس‌های دسترس‌پذیری، مانند Talkback، به کاربران اجازه می‌دهند که مستقیماً از سرفصل به عنوان حرکت کنند.

در Compose، با تعریف خاصیت semantics آن، نشان می‌دهید که composable یک عنوان است:

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

با ترکیبات سفارشی مدیریت کنید

هر زمان که اجزای Material خاصی را در برنامه خود با نسخه های سفارشی جایگزین می کنید، باید ملاحظات دسترسی را در نظر داشته باشید.

فرض کنید که Checkbox Material را با پیاده سازی خودتان جایگزین می کنید. ممکن است فراموش کنید که اصلاح کننده triStateToggleable را اضافه کنید، که ویژگی های دسترسی برای این مؤلفه را کنترل می کند.

به عنوان یک قانون سرانگشتی، به اجرای مؤلفه در کتابخانه Material نگاه کنید و هر گونه رفتار دسترسی را که می توانید پیدا کنید، تقلید کنید. به‌علاوه، از اصلاح‌کننده‌های بنیاد، برخلاف اصلاح‌کننده‌های سطح UI، به شدت استفاده کنید، زیرا این موارد شامل ملاحظات دسترسی خارج از جعبه است.

اجرای مؤلفه سفارشی خود را با چندین سرویس دسترسی آزمایش کنید تا رفتار آن را تأیید کنید.

منابع اضافی