مفاهیم

جدا از اطلاعات اولیه ای که یک composable حمل می کند، مانند یک رشته متن از یک Text composable، داشتن اطلاعات تکمیلی بیشتر در مورد عناصر UI می تواند مفید باشد.

اطلاعات مربوط به معنا و نقش یک مؤلفه در Compose، معنایی نامیده می‌شود، که راهی برای ارائه زمینه اضافی در مورد ترکیب‌پذیرها به خدماتی مانند دسترسی، تکمیل خودکار و آزمایش است. به عنوان مثال، یک نماد دوربین از نظر بصری ممکن است فقط یک تصویر باشد، اما معنای معنایی آن می تواند "عکس گرفتن" باشد.

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

Material and Compose UI و Foundation APIها دارای معنایی داخلی هستند که از نقش و عملکرد خاص خود پیروی می کنند، اما شما همچنین می توانید این معناشناسی را برای APIهای موجود تغییر دهید یا بر اساس نیازهای خاص خود، موارد جدیدی را برای مؤلفه های سفارشی تنظیم کنید.

ویژگی های معنایی

ویژگی های معنایی معنای ترکیب پذیر مربوطه را می رساند. به عنوان مثال، Text composable حاوی یک text ویژگی معنایی است، زیرا این معنای آن composable است. یک Icon حاوی ویژگی contentDescription است (اگر توسط توسعه دهنده تنظیم شده باشد) که معنای نماد را به صورت متنی نشان می دهد.

در نظر بگیرید که چگونه ویژگی های معنایی معنای یک ترکیب پذیر را منتقل می کنند. یک Switch در نظر بگیرید. این شکلی است که برای کاربر به نظر می رسد:

شکل 1. یک Switch در حالت "روشن" و "خاموش" خود.

برای توصیف معنای این عنصر، می‌توانید موارد زیر را بگویید: "این یک سوئیچ است که یک عنصر قابل تغییر در حالت "روشن" است. می‌توانید روی آن کلیک کنید تا با آن تعامل داشته باشید.

این دقیقاً همان چیزی است که از ویژگی های معنایی استفاده می شود. گره معنایی این عنصر سوئیچ دارای ویژگی های زیر است که با Layout Inspector به تصویر کشیده شده است:

Layout Inspector که ویژگی‌های Semantics یک Switch را نشان می‌دهد
شکل 2. Layout Inspector که ویژگی های Semantics یک Switch قابل ترکیب را نشان می دهد.

Role نوع عنصر را نشان می دهد. StateDescription نحوه ارجاع به حالت "روشن" را توضیح می دهد. به‌طور پیش‌فرض، این یک نسخه محلی‌شده از کلمه «روشن» است، اما می‌توان آن را بر اساس زمینه خاص‌تر کرد (به عنوان مثال، «فعال»). ToggleableState وضعیت فعلی سوئیچ است. ویژگی OnClick به روش استفاده شده برای تعامل با این عنصر اشاره می کند.

ردیابی ویژگی‌های معنایی هر یک از اجزای سازنده در برنامه‌تان، بسیاری از احتمالات قدرتمند را باز می‌کند:

  • سرویس‌های دسترس‌پذیری از ویژگی‌ها برای نمایش رابط کاربری نشان‌داده‌شده روی صفحه استفاده می‌کنند و به کاربران اجازه می‌دهند با آن تعامل داشته باشند. برای Switch composable، Talkback ممکن است اعلام کند: «روشن؛ سوئیچ؛ برای جابجایی دو ضربه سریع بزنید». کاربر می تواند روی صفحه نمایش خود دو بار ضربه بزند تا Switch را خاموش کند.
  • چارچوب تست از ویژگی ها برای یافتن گره ها، تعامل با آنها و اظهار نظر استفاده می کند:
    val mySwitch = SemanticsMatcher.expectValue(
        SemanticsProperties.Role, Role.Switch
    )
    composeTestRule.onNode(mySwitch)
        .performClick()
        .assertIsOff()

Composable ها و Modifier هایی که در بالای کتابخانه Compose foundation ساخته شده اند، قبلاً ویژگی های مربوطه را به طور پیش فرض برای شما تنظیم می کنند. به صورت اختیاری، می‌توانید این ویژگی‌ها را به‌صورت دستی تغییر دهید، تا پشتیبانی دسترس‌پذیری را برای موارد استفاده خاص بهبود بخشید، یا استراتژی ادغام یا پاک‌سازی اجزای سازنده خود را تغییر دهید.

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

برای فهرست کامل ویژگی‌های معنایی، شی SemanticsProperties را ببینید. برای لیست کاملی از اقدامات امکان دسترسی، به شی SemanticsActions مراجعه کنید.

سرفصل ها

برنامه‌ها اغلب حاوی صفحه‌هایی با محتوای غنی از متن هستند، مانند مقالات طولانی یا صفحات خبری، که معمولاً به زیربخش‌های مختلف با عنوان تقسیم می‌شوند:

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

کاربرانی که نیازهای دسترسی دارند می توانند در پیمایش آسان چنین صفحه ای با مشکل مواجه شوند. برای بهبود تجربه ناوبری، برخی از خدمات دسترس‌پذیری امکان ناوبری آسان‌تر را مستقیماً بین بخش‌ها یا عنوان‌ها فراهم می‌کنند. برای فعال کردن این کار، با تعریف ویژگی semantics آن، نشان دهید که جزء شما یک heading است:

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

هشدارها و پاپ آپ ها

اگر مؤلفه شما یک هشدار یا یک پاپ آپ است، مانند Snackbar ، ممکن است بخواهید به خدمات دسترس پذیری سیگنال دهید که ساختار یا به روز رسانی های جدید در محتوا می تواند به کاربران منتقل شود.

اجزای هشدار مانند را می توان با ویژگی معنایی liveRegion علامت گذاری کرد. این به خدمات دسترس‌پذیری اجازه می‌دهد تا به‌طور خودکار کاربر را از تغییرات این مؤلفه یا فرزندان آن مطلع کنند:

PopupAlert(
    message = "You have a new message",
    modifier = Modifier.semantics {
        liveRegion = LiveRegionMode.Polite
    }
)

شما باید از liveRegionMode.Polite در بیشتر مواردی استفاده کنید که توجه کاربران فقط باید برای مدت کوتاهی به هشدارها یا تغییرات مهم محتوای روی صفحه جلب شود.

شما باید از liveRegion.Assertive کم استفاده کنید تا از بازخوردهای مخرب جلوگیری کنید. باید برای موقعیت هایی استفاده شود که در آن آگاهی کاربران از محتوای حساس به زمان بسیار مهم است:

PopupAlert(
    message = "Emergency alert incoming",
    modifier = Modifier.semantics {
        liveRegion = LiveRegionMode.Assertive
    }
)

مناطق زنده نباید در محتوایی که مکرراً به‌روزرسانی می‌شوند، مانند تایمرهای شمارش معکوس، استفاده شود تا کاربران با بازخورد دائمی تحت تأثیر قرار نگیرند.

اجزای پنجره مانند

اجزای سفارشی پنجره مانند، شبیه به ModalBottomSheet ، به سیگنال های اضافی نیاز دارند تا آنها را از محتوای اطراف متمایز کند. برای این کار، می توانید از semantics paneTitle استفاده کنید، به طوری که هر گونه تغییر پنجره یا صفحه مربوطه ممکن است به طور مناسب توسط سرویس های دسترسی همراه با اطلاعات معنایی اصلی آن نشان داده شود:

ShareSheet(
    message = "Choose how to share this photo",
    modifier = Modifier
        .fillMaxWidth()
        .align(Alignment.TopCenter)
        .semantics { paneTitle = "New bottom sheet" }
)

برای مرجع، ببینید Material 3 چگونه از paneTitle برای اجزای خود استفاده می کند .

اجزای خطا

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

در این مثال، TalkBack اطلاعات متن خطای اصلی را می‌خواند و به دنبال آن پیام‌های گسترده و اضافی را می‌خواند:

Error(
    errorText = "Fields cannot be empty",
    modifier = Modifier
        .semantics {
            error("Please add both email and password")
        }
)

اجزای ردیابی پیشرفت

برای مؤلفه‌های سفارشی که پیشرفت را دنبال می‌کنند، ممکن است بخواهید کاربران را از تغییرات پیشرفت آنها از جمله مقدار پیشرفت فعلی، محدوده آن و اندازه گام مطلع کنید. شما می توانید این کار را با معناشناسی progressBarRangeInfo انجام دهید - این تضمین می کند که سرویس های دسترسی از تغییرات پیشرفت آگاه هستند و می توانند کاربران را متناسب با آن به روز کنند. فناوری های مختلف کمکی نیز ممکن است راه های منحصر به فردی برای اشاره به افزایش و کاهش پیشرفت داشته باشند.

ProgressInfoBar(
    modifier = Modifier
        .semantics {
            progressBarRangeInfo =
                ProgressBarRangeInfo(
                    current = progress,
                    range = 0F..1F
                )
        }
)

لیست و اطلاعات مورد

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

با استفاده از معنی‌شناسی collectionInfo و collectionItemInfo به ترتیب در لیست و آیتم‌ها، در این فهرست طولانی، سرویس‌های دسترسی می‌توانند علاوه بر اطلاعات معنایی متنی، به کاربران اطلاع دهند که از کل مجموعه در چه فهرستی قرار دارند:

MilkyWayList(
    modifier = Modifier
        .semantics {
            collectionInfo = CollectionInfo(
                rowCount = milkyWay.count(),
                columnCount = 1
            )
        }
) {
    milkyWay.forEachIndexed { index, text ->
        Text(
            text = text,
            modifier = Modifier.semantics {
                collectionItemInfo =
                    CollectionItemInfo(index, 0, 0, 0)
            }
        )
    }
}

شرح حالت

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

اقدامات سفارشی

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

برای اینکه تند کشیدن برای رد کردن اشاره بیشتر در دسترس باشد، می‌توانید آن را به یک کنش سفارشی پیوند دهید و کنش رد کردن و برچسب را در آنجا ارسال کنید:

SwipeToDismissBox(
    modifier = Modifier.semantics {
        // Represents the swipe to dismiss for accessibility
        customActions = listOf(
            CustomAccessibilityAction(
                label = "Remove article from list",
                action = {
                    removeArticle()
                    true
                }
            )
        )
    },
    state = rememberSwipeToDismissBoxState(),
    backgroundContent = {}
) {
    ArticleListItem()
}

یک سرویس دسترس‌پذیری مانند TalkBack سپس مؤلفه را برجسته می‌کند و به این نکته اشاره می‌کند که اقدامات بیشتری در منوی آن موجود است که نشان‌دهنده تند کشیدن برای رد کردن عملکرد در آنجا است:

تجسم منوی عمل TalkBack
شکل 4. تجسم منوی عمل TalkBack.

یکی دیگر از موارد استفاده برای کنش‌های سفارشی، فهرست‌های طولانی با مواردی است که کنش‌های در دسترس بیشتری دارند، زیرا ممکن است برای کاربران خسته‌کننده باشد که هر عمل را برای هر مورد جداگانه انجام دهند:

= تجسم ناوبری Switch Access روی صفحه
شکل 5. تجسم ناوبری دسترسی سوئیچ روی صفحه.

برای بهبود تجربه ناوبری، که مخصوصاً برای فناوری‌های کمکی مبتنی بر تعامل مانند دسترسی سوئیچ یا دسترسی صوتی مفید است، می‌توانید از کنش‌های سفارشی روی کانتینر برای انتقال کنش‌ها به خارج از پیمایش فردی و به منوی عملکرد جداگانه استفاده کنید:

ArticleListItemRow(
    modifier = Modifier
        .semantics {
            customActions = listOf(
                CustomAccessibilityAction(
                    label = "Open article",
                    action = {
                        openArticle()
                        true
                    }
                ),
                CustomAccessibilityAction(
                    label = "Add to bookmarks",
                    action = {
                        addToBookmarks()
                        true
                    }
                ),
            )
        }
) {
    Article(
        modifier = Modifier.clearAndSetSemantics { },
        onClick = openArticle,
    )
    BookmarkButton(
        modifier = Modifier.clearAndSetSemantics { },
        onClick = addToBookmarks,
    )
}

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

با استفاده از Switch Access به عنوان مثال، منوی آن پس از انتخاب کانتینر باز می شود و اقدامات تودرتوی موجود را در آنجا فهرست می کند:

هایلایت دسترسی سوئیچ مورد فهرست مقاله
شکل 6. Switch Access برجسته مورد فهرست مقاله.
تجسم منوی عملکرد Switch Access.
شکل 7. تجسم منوی عمل Switch Access.

درخت معناشناسی

یک ترکیب ، رابط کاربری برنامه شما را توصیف می کند و با اجرای composable ها تولید می شود. ترکیب یک ساختار درختی است که از ترکیب‌هایی تشکیل شده است که رابط کاربری شما را توصیف می‌کنند.

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

یک سلسله مراتب رابط کاربری معمولی و درخت معنایی آن
شکل 8. یک سلسله مراتب رابط کاربری معمولی و درخت معنایی آن.

اگر برنامه شما از ترکیب‌کننده‌ها و اصلاح‌کننده‌های Compose Foundation و کتابخانه مواد تشکیل شده باشد، درخت Semantics به‌طور خودکار برای شما پر و تولید می‌شود. با این حال، هنگامی که ترکیب‌پذیرهای سطح پایین سفارشی را اضافه می‌کنید، باید معنای آن را به صورت دستی ارائه کنید . همچنین ممکن است شرایطی وجود داشته باشد که درخت شما به درستی یا به طور کامل معنای عناصر روی صفحه را نشان ندهد، در این صورت می توانید درخت را تطبیق دهید.

به عنوان مثال این تقویم سفارشی را در نظر بگیرید:

یک تقویم سفارشی قابل تنظیم با عناصر روز قابل انتخاب
شکل 9. یک تقویم سفارشی قابل تنظیم با عناصر روز قابل انتخاب.

در این مثال، کل تقویم به‌عنوان یک قابل ترکیب سطح پایین، با استفاده از Layout composable و رسم مستقیم روی Canvas پیاده‌سازی می‌شود. اگر کار دیگری انجام ندهید، سرویس‌های دسترس‌پذیری اطلاعات کافی در مورد محتوای قابل تنظیم و انتخاب کاربر در تقویم دریافت نمی‌کنند. به عنوان مثال، اگر کاربر روی روز حاوی 17 کلیک کند، چارچوب دسترسی فقط اطلاعات توضیحات را برای کل کنترل تقویم دریافت می کند. در این صورت، سرویس دسترس‌پذیری TalkBack «تقویم» یا کمی بهتر، «تقویم آوریل» را اعلام می‌کند و کاربر می‌خواهد بداند چه روزی انتخاب شده است. برای دسترسی بیشتر به این ترکیب، باید اطلاعات معنایی را به صورت دستی اضافه کنید.

درخت ادغام شده و ادغام نشده

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

نمونه ای از چنین ترکیب پذیری Button است. شما می توانید در مورد یک دکمه به عنوان یک عنصر استدلال کنید، حتی اگر ممکن است دارای چندین گره فرزند باشد:

Button(onClick = { /*TODO*/ }) {
    Icon(
        imageVector = Icons.Filled.Favorite,
        contentDescription = null
    )
    Spacer(Modifier.size(ButtonDefaults.IconSpacing))
    Text("Like")
}

در درخت Semantics، ویژگی‌های نوادگان دکمه ادغام می‌شوند و دکمه به صورت یک گره برگ در درخت ارائه می‌شود:

نمایش معنایی تک برگ ادغام شده
شکل 10. نمایش معنایی تک برگ ادغام شده.

ترکیب‌کننده‌ها و اصلاح‌کننده‌ها می‌توانند با فراخوانی Modifier.semantics (mergeDescendants = true) {} نشان دهند که می‌خواهند ویژگی‌های معنایی فرزندان خود را ادغام کنند. تنظیم این ویژگی روی true نشان می دهد که ویژگی های semantics باید ادغام شوند. در مثال Button ، Button composable از اصلاح کننده clickable به صورت داخلی استفاده می کند که شامل این اصلاح کننده semantics است. بنابراین، نودهای نود دکمه ادغام می شوند. اسناد دسترس‌پذیری را بخوانید تا درباره اینکه چه زمانی باید رفتار ادغام را در ترکیب‌بندی خود تغییر دهید ، بیشتر بدانید.

چندین اصلاح کننده و ترکیب پذیر در کتابخانه های Foundation و Material Compose دارای این ویژگی هستند. به عنوان مثال، اصلاح کننده های clickable و toggleable به طور خودکار فرزندان خود را ادغام می کنند. همچنین، ListItem composable نوادگان خود را ادغام می کند.

درخت را بررسی کنید

درخت معناشناسی در واقع دو درخت متفاوت است. یک درخت Semantics ادغام شده وجود دارد که وقتی mergeDescendants روی true تنظیم شده است، گره های نسل را ادغام می کند. همچنین یک درخت Semantics ادغام نشده وجود دارد که ادغام را اعمال نمی کند، اما هر گره را دست نخورده نگه می دارد. سرویس‌های دسترسی از درخت ادغام نشده استفاده می‌کنند و الگوریتم‌های ادغام خود را با در نظر گرفتن ویژگی mergeDescendants اعمال می‌کنند. چارچوب تست به طور پیش فرض از درخت ادغام شده استفاده می کند.

می توانید هر دو درخت را با متد printToLog() بررسی کنید. به طور پیش فرض، و مانند مثال های قبلی، درخت ادغام شده ثبت می شود. برای چاپ درخت ادغام نشده، پارامتر useUnmergedTree تطبیق دهنده onRoot() را روی true تنظیم کنید:

composeTestRule.onRoot(useUnmergedTree = true).printToLog("MY TAG")

Layout Inspector به شما امکان می دهد هر دو درخت Semantics ادغام شده و ادغام نشده را با انتخاب مورد ترجیحی در فیلتر view نمایش دهید:

گزینه‌های نمای Layout Inspector، که امکان نمایش درخت Semantics ادغام‌شده و ادغام نشده را فراهم می‌کند.
شکل 11. گزینه های نمای Layout Inspector که امکان نمایش درخت Semantics ادغام شده و ادغام نشده را فراهم می کند.

برای هر گره در درخت شما، Layout Inspector هر دو Semantics ادغام شده و Semantics را روی آن گره در پانل خواص نشان می دهد:

ویژگی های معنایی ادغام و تنظیم شدند
شکل 12. ویژگی های معنایی ادغام و تنظیم شدند.

به طور پیش‌فرض، تطبیق‌کنندگان در چارچوب تست از درخت Semantics ادغام شده استفاده می‌کنند. به همین دلیل است که می توانید با یک Button با تطبیق متن نشان داده شده در داخل آن تعامل داشته باشید:

composeTestRule.onNodeWithText("Like").performClick()

با تنظیم پارامتر useUnmergedTree تطبیق‌دهنده‌ها روی true ، مانند تطبیق onRoot ، این رفتار را نادیده بگیرید.

درخت را تطبیق دهید

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

{% کلمه به کلمه %} {% آخر کلمه %} {% کلمه به کلمه %} {% آخر کلمه %}