مفاهیم و پیادهسازی Jetpack Compose
برای کمک به کاربرانی که نیازهای دسترسپذیری دارند، چارچوب Android به شما امکان میدهد سرویس دسترسپذیریای ایجاد کنید که بتواند محتوای برنامهها را به کاربران ارائه دهد و همچنین ازجانب آنها با برنامهها کار کند.
Android چندین سرویس دسترسپذیری سیستم ارائه میدهد، ازجمله:
- TalkBack: به افراد کمبینا یا نابینا کمک میکند. این برنامه محتوا را ازطریق صدای ترکیبی اعلام میکند و در پاسخ به اشارههای کاربر، کنشهایی را در برنامه انجام میدهد.
- دسترسی با کلید: به افراد دارای ناتوانی حرکتی کمک میکند. این کلاس عناصر تعاملی را برجسته میکند و در پاسخ به فشار دادن دکمه توسط کاربر، کنشهایی را انجام میدهد. این ویژگی امکان کنترل دستگاه را فقط بااستفاده از یک یا دو دکمه فراهم میکند.
برای کمک به افراد دارای نیازهای دسترسپذیری برای استفاده موفقیتآمیز از برنامهتان، برنامهتان باید از روالهای مطلوب شرحدادهشده در این صفحه پیروی کند که براساس دستورالعملهای شرحدادهشده در دسترسپذیرتر کردن برنامهها است.
عناصر برچسب
مهم است که برای هر عنصر تعاملی میانای کاربر در برنامهتان، برچسبهای مفید و توصیفی به کاربران ارائه دهید. هر برچسب باید معنی و هدف عنصر خاصی را توضیح دهد. صفحهخوانهایی مثل TalkBack میتوانند این برچسبها را برای کاربران بخوانند.
در بیشتر موارد، شرح عنصر میانای کاربر را در فایل منبع چیدمان که حاوی عنصر است مشخص میکنید. معمولاً برچسبها را بااستفاده از
مشخصه contentDescription اضافه میکنید، همانطور که در راهنمای دسترسپذیرتر کردن
برنامهها توضیح داده شده است. چندین روش برچسبگذاری دیگر وجود دارد که در بخشهای زیر شرح داده شده است.
عناصر قابلویرایش
هنگام برچسبگذاری عناصر قابلویرایش، مانند اشیاء EditText، مفید است که علاوهبر دردسترس قرار دادن این نوشتار نمونه برای صفحهخوانها، نوشتاری را نشان دهید که نمونهای از ورودی معتبر در خود عنصر ارائه میدهد. در این شرایط، میتوانید از مشخصه android:hint استفاده کنید، همانطور که در قطعه کد زیر نشان داده شده است:
<!-- The hint text for en-US locale would be "Apartment, suite, or building". --> <EditText android:id="@+id/addressLine2" android:hint="@string/aptSuiteBuilding" ... />
در این شرایط، مشخصه android:labelFor شیء View باید روی شناسه عنصر EditText تنظیم شود. برای جزئیات بیشتر، بخش زیر را ببینید.
جفت عنصرهایی که یکی دیگری را توصیف میکند
معمولاً عنصر EditText دارای شیء View متناظری است که آنچه را کاربران باید در عنصر EditText وارد کنند شرح میدهد. میتوانید این رابطه را با تنظیم
مشخصه android:labelFor شیء View نشان دهید.
نمونهای از برچسبگذاری چنین جفت عنصری در گزیده زیر نشان داده شده است:
<!-- Label text for en-US locale would be "Username:" --> <TextView android:id="@+id/usernameLabel" ... android:text="@string/username" android:labelFor="@+id/usernameEntry" /> <EditText android:id="@+id/usernameEntry" ... /> <!-- Label text for en-US locale would be "Password:" --> <TextView android:id="@+id/passwordLabel" ... android:text="@string/password android:labelFor="@+id/passwordEntry" /> <EditText android:id="@+id/passwordEntry" android:inputType="textPassword" ... />
عناصر موجود در مجموعه
هنگام افزودن برچسب به عناصر مجموعه، هر برچسب باید یکتا باشد. بهاینترتیب، سرویسهای دسترسپذیری سیستم میتوانند هنگام اعلام برچسب دقیقاً به یک عنصر روی صفحه ارجاع دهند. این مطابقت به کاربران اطلاع میدهد که چه زمانی در واسط کاربر جابهجا میشوند یا چه زمانی تمرکز را به عنصری منتقل میکنند که قبلاً آن را پیدا کردهاند.
بهویژه، نوشتار یا اطلاعات زمینهای بیشتری در عناصر موجود در چیدمانهای مجدداً استفادهشده (مثل اشیای RecyclerView) اضافه کنید تا هر عنصر فرزند بهطور منحصربهفردی شناسایی شود.
برای انجام این کار، شرح محتوا را بهعنوان بخشی از پیادهسازی آداپتور خود تنظیم کنید، همانطور که در تکهکد زیر نشان داده شده است:
کاتلین
data class MovieRating(val title: String, val starRating: Integer) class MyMovieRatingsAdapter(private val myData: Array<MovieRating>): RecyclerView.Adapter<MyMovieRatingsAdapter.MyRatingViewHolder>() { class MyRatingViewHolder(val ratingView: ImageView) : RecyclerView.ViewHolder(ratingView) override fun onBindViewHolder(holder: MyRatingViewHolder, position: Int) { val ratingData = myData[position] holder.ratingView.contentDescription = "Movie ${position}: " + "${ratingData.title}, ${ratingData.starRating} stars" } }
جاوا
public class MovieRating { private String title; private int starRating; // ... public String getTitle() { return title; } public int getStarRating() { return starRating; } } public class MyMovieRatingsAdapter extends RecyclerView.Adapter<MyAdapter.MyRatingViewHolder> { private MovieRating[] myData; public static class MyRatingViewHolder extends RecyclerView.ViewHolder { public ImageView ratingView; public MyRatingViewHolder(ImageView iv) { super(iv); ratingView = iv; } } @Override public void onBindViewHolder(MyRatingViewHolder holder, int position) { MovieRating ratingData = myData[position]; holder.ratingView.setContentDescription("Movie " + position + ": " + ratingData.getTitle() + ", " + ratingData.getStarRating() + " stars") } }
گروههای محتوای مرتبط
اگر برنامهتان چند عنصر میانای کاربر را نمایش میدهد که گروهی طبیعی را تشکیل میدهند، مثل
جزئیات آهنگ یا ویژگیهای پیام، این عناصر را در یک
محفظه قرار دهید که معمولاً زیرکلاسی از ViewGroup است. مشخصه
android:screenReaderFocusable
شیء دربرگیرنده را روی true و مشخصه
android:focusable
هر شیء داخلی را روی false تنظیم کنید. بهاینترتیب، سرویسهای دسترسپذیری میتوانند شرح محتوای عناصر داخلی را یکی پساز دیگری در یک اعلامیه ارائه دهند.
این ادغام عناصر مرتبط به کاربران فناوری کمکی کمک میکند
اطلاعات روی صفحه را بهطور مؤثرتری کاوش کنند.
قطعه کد زیر حاوی بخشهایی از محتوا است که به یکدیگر مرتبط هستند، بنابراین عنصر دربرگیرنده، نمونهای از ConstraintLayout، دارای ویژگی android:screenReaderFocusable تنظیمشده روی true است و عناصر داخلی TextView هرکدام دارای ویژگی android:focusable تنظیمشده روی false هستند:
<!-- In response to a single user interaction, accessibility services announce both the title and the artist of the song. --> <ConstraintLayout android:id="@+id/song_data_container" ... android:screenReaderFocusable="true"> <TextView android:id="@+id/song_title" ... android:focusable="false" android:text="@string/my_song_title" /> <TextView android:id="@+id/song_artist" android:focusable="false" android:text="@string/my_songwriter" /> </ConstraintLayout>
ازآنجاییکه سرویسهای دسترسپذیری شرح عناصر داخلی را در یک عبارت واحد اعلام میکنند، مهم است که هر شرح را تا حد امکان کوتاه نگه دارید و درعینحال معنی عنصر را منتقل کنید.
توجه: بهطور کلی، باید از ایجاد شرح محتوا برای گروه با جمعآوری نوشتار فرزندان آن خودداری کنید. انجام این کار باعث میشود شرح گروه شکننده شود و وقتی نوشتار فرزند تغییر میکند، شرح گروه ممکن است دیگر با نوشتار نمایان مطابقت نداشته باشد.
در بافت فهرست یا جدول، صفحهخوان ممکن است نوشتار فهرست یا گرههای نوشتاری فرزند عنصر جدول را ادغام کند. بهتر است از اصلاح این اعلامیه خودداری کنید.
گروههای تودرتو
اگر میانای برنامهتان اطلاعات چندبعدی ارائه میدهد، مثلاً
فهرست روزبهروز رویدادهای جشنواره، از مشخصه android:screenReaderFocusable
در محفظههای گروه داخلی استفاده کنید. این طرح برچسبگذاری توازن خوبی بین تعداد اعلانهای لازم برای کشف محتوای صفحه و طول هر اعلان برقرار میکند.
تکهکد زیر یک روش برچسبگذاری گروهها را در داخل گروههای بزرگتر نشان میدهد:
<!-- In response to a single user interaction, accessibility services announce the events for a single stage only. --> <ConstraintLayout android:id="@+id/festival_event_table" ... > <ConstraintLayout android:id="@+id/stage_a_event_column" android:screenReaderFocusable="true"> <!-- UI elements that describe the events on Stage A. --> </ConstraintLayout> <ConstraintLayout android:id="@+id/stage_b_event_column" android:screenReaderFocusable="true"> <!-- UI elements that describe the events on Stage B. --> </ConstraintLayout> </ConstraintLayout>
سرصفحهها در نوشتار
برخیاز برنامهها از عنوانها برای خلاصه کردن گروههای نوشتاری که در صفحه نشان داده میشود استفاده میکنند. اگر
عنصر View خاصی نشاندهنده عنوان باشد، میتوانید هدف آن را
برای سرویسهای دسترسپذیری با تنظیم کردن مشخصه
android:accessibilityHeading عنصر روی
true مشخص کنید.
کاربران سرویسهای دسترسپذیری میتوانند بهجای پیمایش بین پاراگرافها یا کلمهها، بین عنوانها پیمایش کنند. این انعطافپذیری تجربه پیمایش نوشتار را بهبود میبخشد.
عنوانهای قاب دسترسپذیری
در Android 9 (سطح میانای برنامهسازی کاربردی ۲۸) و بالاتر، میتوانید عنوانهای مناسب دسترسپذیری برای قابهای صفحه ارائه دهید. برای اهداف دسترسپذیری، قاب بخشی از پنجره است که ازنظر بصری متمایز است، مثل محتوای یک قطعه. برای اینکه سرویسهای دسترسپذیری بتوانند رفتار پنجرهمانند قاب را درک کنند، عنوانهای توصیفی به قابهای برنامهتان بدهید. سپس سرویسهای دسترسپذیری میتوانند اطلاعات دقیقتری را به کاربران ارائه دهند وقتی ظاهر یا محتوای قاب تغییر میکند.
برای مشخص کردن عنوان قاب، از مشخصه
android:accessibilityPaneTitle
استفاده کنید، همانطور که در قطعه کد زیر نشان داده شده است:
<!-- Accessibility services receive announcements about content changes that are scoped to either the "shopping cart view" section (top) or "browse items" section (bottom) --> <MyShoppingCartView android:id="@+id/shoppingCartContainer" android:accessibilityPaneTitle="@string/shoppingCart" ... /> <MyShoppingBrowseView android:id="@+id/browseItemsContainer" android:accessibilityPaneTitle="@string/browseProducts" ... />
عناصر تزئینی
اگر عنصری در واسط کاربر شما فقط برای فاصلهگذاری دیداری یا اهداف ظاهر دیداری وجود دارد، ویژگی android:importantForAccessibility آن را روی "no" تنظیم کنید.
افزودن کنشهای دسترسپذیری
مهم است که به کاربران سرویسهای دسترسپذیری اجازه دهید بهراحتی همه جریانهای کاربری را در برنامهتان انجام دهند. برای مثال، اگر کاربر بتواند روی موردی در فهرست تند بکشد، این کنش میتواند برای سرویسهای دسترسپذیری نیز نمایان شود تا کاربران روش جایگزینی برای تکمیل همان جریان کاربری داشته باشند.
همه کنشها را دسترسپذیر کنید
کاربر TalkBack، دسترسی صوتی، یا «دسترسی با کلید» ممکن است به روشهای جایگزینی برای تکمیل جریانهای کاربری خاصی در برنامه نیاز داشته باشد. برای کنشهای مرتبط با اشارهها مانند کشیدن و رها کردن یا تند کشیدن، برنامه شما میتواند کنشها را بهگونهای نمایش دهد که برای کاربران سرویسهای دسترسپذیری قابلدسترس باشد.
بااستفاده از کنشهای دسترسپذیری، برنامه میتواند روشهای جایگزینی برای تکمیل کنش به کاربران ارائه دهد.
برای مثال، اگر برنامهتان به کاربران اجازه میدهد روی موردی تند بکشند، میتوانید این عملکرد را ازطریق کنش دسترسپذیری سفارشی نیز نشان دهید، مانند این:
کاتلین
ViewCompat.addAccessibilityAction( // View to add accessibility action itemView, // Label surfaced to user by an accessibility service getText(R.id.archive) ) { _, _ -> // Same method executed when swiping on itemView archiveItem() true }
جاوا
ViewCompat.addAccessibilityAction( // View to add accessibility action itemView, // Label surfaced to user by an accessibility service getText(R.id.archive), (view, arguments) -> { // Same method executed when swiping on itemView archiveItem(); return true; } );
With the custom accessibility action implemented, users can access the action through the actions menu.
Make available actions understandable
When a view supports actions such as touch & hold, an accessibility service such as TalkBack announces it as "Double tap and hold to long press."
This generic announcement doesn't give the user any context about what a touch & hold action does.
To make this announcement more descriptive, you can replace the accessibility action’s announcement like so:
Kotlin
ViewCompat.replaceAccessibilityAction( // View that contains touch & hold action itemView, AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_LONG_CLICK, // Announcement read by TalkBack to surface this action getText(R.string.favorite), null )
جاوا
ViewCompat.replaceAccessibilityAction( // View that contains touch & hold action itemView, AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_LONG_CLICK, // Announcement read by TalkBack to surface this action getText(R.string.favorite), null );
This results in TalkBack announcing "Double tap and hold to favorite," helping users understand the purpose of the action.
Extend system widgets
Note: When you design your app's UI, use or extend
system-provided widgets that are as far down Android's class hierarchy as
possible. System-provided widgets that are far down the hierarchy already
have most of the accessibility capabilities your app needs. It's easier
to extend these system-provided widgets than to create your own from the more
generic View,
ViewCompat,
Canvas, and
CanvasCompat
classes.
If you must extend View or Canvas directly, which
might be necessary for a highly customized experience or a game level, see
Make custom views more
accessible.
This section uses the example of implementing a special type of
Switch called TriSwitch while following
best practices around extending system widgets. A TriSwitch
object works similarly to a Switch object, except that each instance of
TriSwitch allows the user to toggle among three possible states.
Extend from far down the class hierarchy
The Switch object inherits from several framework UI classes in its hierarchy:
View ↳ TextView ↳ Button ↳ CompoundButton ↳ Switch
بهتر است کلاس جدید TriSwitch مستقیماً از کلاس Switch گسترش یابد. بهاینترتیب، چارچوب دسترسپذیری Android بیشتر قابلیتهای دسترسپذیری موردنیاز کلاس TriSwitch را ارائه میدهد:
- کنشهای دسترسپذیری: اطلاعاتی برای سیستم درباره اینکه سرویسهای دسترسپذیری چگونه میتوانند هر ورودی احتمالی کاربر را که در شیء
TriSwitchانجام میشود شبیهسازی کنند. (ازViewبه ارث رسیده است.) - رویدادهای دسترسپذیری: اطلاعاتی برای سرویسهای دسترسپذیری درباره هر
روش ممکن برای تغییر ظاهر شیء
TriSwitchهنگام بازآوری یا بهروزرسانی صفحه. (ازViewبه ارث رسیده است.) - ویژگیها: جزئیات مربوط به هر شیء
TriSwitch، مانند محتوای هر نوشتاری که نمایش میدهد. (ازTextViewبه ارث رسیده است.) - اطلاعات وضعیت: شرح وضعیت فعلی شیء
TriSwitch، مثلاً «علامتزده» یا «علامتنزده». (ازCompoundButtonبه ارث رسیده است.) - توضیح نوشتاری وضعیت: توضیح نوشتاری درباره اینکه هر وضعیت نشاندهنده چیست. (از
Switchبه ارث رسیده است.)
این رفتار از Switch و ابرکلاسهای آن تقریباً همان رفتار برای اشیاء TriSwitch است. بنابراین، پیادهسازی شما میتواند
بر گسترش تعداد حالتهای ممکن از دو به سه تمرکز کند.
تعریف رویدادهای سفارشی
وقتی ابزاره سیستم را گسترش میدهید، احتمالاً جنبهای از نحوه تعامل کاربران با آن ابزاره را تغییر میدهید. مهم است که این تغییرات تعامل را تعریف کنید تا سرویسهای دسترسپذیری بتوانند ابزاره برنامه شما را بهروز کنند، درست مثل اینکه کاربر مستقیماً با ابزاره تعامل داشته باشد.
یک دستورالعمل کلی این است که برای هر بازخوانی مبتنی بر نما که لغو میکنید،
باید کنش دسترسپذیری مربوطه را نیز با لغو کردن
ViewCompat.replaceAccessibilityAction() بازتعریف کنید.
در آزمایشهای برنامهتان، میتوانید با فراخوانی ViewCompat.performAccessibilityAction()، رفتار این کنشهای بازتعریفشده را اعتبارسنجی کنید.
چگونه این اصل میتواند برای اشیاء TriSwitch کار کند
برخلاف شیء Switch معمولی، با تکضرب زدن روی شیء TriSwitch،
سه حالت ممکن بهصورت چرخهای تکرار میشود. بنابراین، کنش دسترسپذیری ACTION_CLICK مربوطه
باید بهروز شود:
کاتلین
class TriSwitch(context: Context) : Switch(context) { // 0, 1, or 2 var currentState: Int = 0 private set init { updateAccessibilityActions() } private fun updateAccessibilityActions() { ViewCompat.replaceAccessibilityAction(this, ACTION_CLICK, action-label) { view, args -> moveToNextState() }) } private fun moveToNextState() { currentState = (currentState + 1) % 3 } }
جاوا
public class TriSwitch extends Switch { // 0, 1, or 2 private int currentState; public int getCurrentState() { return currentState; } public TriSwitch() { updateAccessibilityActions(); } private void updateAccessibilityActions() { ViewCompat.replaceAccessibilityAction(this, ACTION_CLICK, action-label, (view, args) -> moveToNextState()); } private void moveToNextState() { currentState = (currentState + 1) % 3; } }
منابع بیشتر
برای کسب اطلاعات بیشتر درباره دسترسپذیرتر کردن برنامهتان، منابع اضافی زیر را ببینید: