نماهای سفارشی را قابل دسترس‌تر کنید (Views)

مفاهیم و پیاده‌سازی Jetpack Compose

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

  • کنترل کلیک‌های کنترلر جهت‌دار.
  • پیاده‌سازی متدهای API دسترسی‌پذیری
  • اشیاء AccessibilityEvent مخصوص نمای سفارشی خود را ارسال کنید.
  • برای نمای خود AccessibilityEvent و AccessibilityNodeInfo را وارد کنید.

کنترل کلیک‌های کنترلر جهت‌دار

در اکثر دستگاه‌ها، کلیک کردن روی یک نما با استفاده از یک کنترلر جهت‌دار، یک KeyEvent با KEYCODE_DPAD_CENTER را به نمای فعلی که در حال فوکوس است، ارسال می‌کند. همه نماهای استاندارد اندروید KEYCODE_DPAD_CENTER را به طور مناسب مدیریت می‌کنند. هنگام ساخت یک کنترل View سفارشی، مطمئن شوید که این رویداد همان تأثیر ضربه زدن روی نما روی صفحه لمسی را دارد.

کنترل سفارشی شما باید با رویداد KEYCODE_ENTER همانند KEYCODE_DPAD_CENTER رفتار کند. این کار تعامل با یک صفحه کلید کامل را برای کاربران آسان‌تر می‌کند.

پیاده‌سازی متدهای API دسترسی‌پذیری

رویدادهای دسترسی، پیام‌هایی در مورد تعاملات کاربران با اجزای رابط بصری برنامه شما هستند. این پیام‌ها توسط سرویس‌های دسترسی مدیریت می‌شوند که از اطلاعات موجود در این رویدادها برای تولید بازخورد و اعلان‌های تکمیلی استفاده می‌کنند. متدهای دسترسی بخشی از کلاس‌های View و View.AccessibilityDelegate هستند. این متدها به شرح زیر هستند:

dispatchPopulateAccessibilityEvent()
سیستم این متد را زمانی فراخوانی می‌کند که نمای سفارشی شما یک رویداد دسترسی ایجاد کند. پیاده‌سازی پیش‌فرض این متد، متد onPopulateAccessibilityEvent() را برای این نما و سپس متد dispatchPopulateAccessibilityEvent() را برای هر فرزند این نما فراخوانی می‌کند.
onInitializeAccessibilityEvent()
سیستم این متد را برای دریافت اطلاعات اضافی در مورد وضعیت نمای فراتر از محتوای متن فراخوانی می‌کند. اگر نمای سفارشی شما کنترل تعاملی فراتر از یک TextView یا Button ساده ارائه می‌دهد، این متد را بازنویسی کنید و اطلاعات اضافی در مورد نمای خود - مانند نوع فیلد رمز عبور، نوع کادر انتخاب یا حالت‌هایی که تعامل کاربر یا بازخورد در رویداد را فراهم می‌کنند - را با استفاده از این متد تنظیم کنید. اگر این متد را بازنویسی می‌کنید، پیاده‌سازی فوق العاده آن را فراخوانی کنید و فقط ویژگی‌هایی را که توسط کلاس فوق العاده تنظیم نشده‌اند، تغییر دهید.
onInitializeAccessibilityNodeInfo()
این متد، اطلاعاتی در مورد وضعیت view به سرویس‌های دسترسی ارائه می‌دهد. پیاده‌سازی پیش‌فرض View دارای مجموعه‌ای استاندارد از ویژگی‌های view است، اما اگر view سفارشی شما کنترل تعاملی فراتر از یک TextView یا Button ساده ارائه می‌دهد، این متد را بازنویسی کنید و اطلاعات اضافی در مورد view خود را در شیء AccessibilityNodeInfo که توسط این متد مدیریت می‌شود، قرار دهید.
onPopulateAccessibilityEvent()
این متد، متن اعلان صوتی AccessibilityEvent را برای نمای شما تنظیم می‌کند. همچنین اگر نمای مورد نظر فرزند نمای دیگری باشد که یک رویداد دسترسی ایجاد می‌کند، این متد فراخوانی می‌شود.
onRequestSendAccessibilityEvent()
سیستم این متد را زمانی فراخوانی می‌کند که یک فرزند از نمای شما یک AccessibilityEvent ایجاد کند. این مرحله به نمای والد اجازه می‌دهد تا رویداد دسترسی را با اطلاعات اضافی اصلاح کند. این متد را فقط در صورتی پیاده‌سازی کنید که نمای سفارشی شما بتواند نماهای فرزند داشته باشد و نمای والد بتواند اطلاعات زمینه‌ای را برای رویداد دسترسی فراهم کند که برای سرویس‌های دسترسی مفید باشد.
sendAccessibilityEvent()
سیستم این متد را زمانی فراخوانی می‌کند که کاربر روی یک نما (view) اقدامی انجام دهد. این رویداد با نوع اقدام کاربر، مانند TYPE_VIEW_CLICKED ، طبقه‌بندی می‌شود. به طور کلی، هر زمان که محتوای نمای سفارشی شما تغییر کند، باید یک AccessibilityEvent ارسال کنید.
sendAccessibilityEventUnchecked()
این متد زمانی استفاده می‌شود که کد فراخوانی نیاز دارد تا مستقیماً بررسی فعال بودن دسترسی‌پذیری در دستگاه را کنترل کند ( AccessibilityManager.isEnabled() ). اگر این متد را پیاده‌سازی کنید، فراخوانی را طوری انجام دهید که گویی دسترسی‌پذیری فعال است، صرف نظر از تنظیمات سیستم. معمولاً نیازی به پیاده‌سازی این متد برای یک نمای سفارشی ندارید.

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

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

  • dispatchPopulateAccessibilityEvent()
  • onInitializeAccessibilityEvent()
  • onInitializeAccessibilityNodeInfo()
  • onPopulateAccessibilityEvent()

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

ارسال رویدادهای دسترسی‌پذیری

بسته به مشخصات نمای سفارشی شما، ممکن است لازم باشد اشیاء AccessibilityEvent را در زمان‌های مختلف یا برای رویدادهایی که توسط پیاده‌سازی پیش‌فرض مدیریت نمی‌شوند، ارسال کند. کلاس View یک پیاده‌سازی پیش‌فرض برای این نوع رویدادها ارائه می‌دهد:

به طور کلی، هر زمان که محتوای نمای سفارشی شما تغییر کند، باید یک AccessibilityEvent ارسال کنید. برای مثال، اگر در حال پیاده‌سازی یک نوار لغزنده سفارشی هستید که به کاربر اجازه می‌دهد با فشار دادن کلید جهت چپ یا راست، یک مقدار عددی را انتخاب کند، نمای سفارشی شما باید هر زمان که مقدار کشویی تغییر می‌کند، یک رویداد TYPE_VIEW_TEXT_CHANGED منتشر کند. نمونه کد زیر استفاده از متد sendAccessibilityEvent() را برای گزارش این رویداد نشان می‌دهد.

کاتلین

override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean {
    return when(keyCode) {
        KeyEvent.KEYCODE_DPAD_LEFT -> {
            currentValue--
            sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED)
            true
        }
        ...
    }
}

جاوا

@Override
public boolean onKeyUp (int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) {
        currentValue--;
        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);
        return true;
    }
    ...
}

رویدادهای دسترسی‌پذیری را ثبت کنید

هر AccessibilityEvent مجموعه‌ای از ویژگی‌های مورد نیاز دارد که وضعیت فعلی view را توصیف می‌کنند. این ویژگی‌ها شامل مواردی مانند نام کلاس view، توضیحات محتوا و وضعیت بررسی شده هستند. ویژگی‌های خاص مورد نیاز برای هر نوع رویداد در مستندات مرجع AccessibilityEvent شرح داده شده‌اند.

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

از متدهای onPopulateAccessibilityEvent() و onInitializeAccessibilityEvent() برای پر کردن یا تغییر اطلاعات در یک AccessibilityEvent استفاده کنید. از متد onPopulateAccessibilityEvent() به طور خاص برای اضافه کردن یا تغییر محتوای متنی رویداد استفاده کنید، که توسط سرویس‌های دسترسی مانند TalkBack به اعلان‌های صوتی تبدیل می‌شود. از متد onInitializeAccessibilityEvent() برای پر کردن اطلاعات اضافی در مورد رویداد، مانند وضعیت انتخاب view، استفاده کنید.

علاوه بر این، متد onInitializeAccessibilityNodeInfo() را پیاده‌سازی کنید. سرویس‌های دسترسی از اشیاء AccessibilityNodeInfo که توسط این متد پر شده‌اند، برای بررسی سلسله مراتب view که یک رویداد دسترسی را پس از دریافت ایجاد می‌کند، استفاده می‌کنند و بازخورد مناسبی را به کاربران ارائه می‌دهند.

مثال کد زیر نحوه‌ی بازنویسی این سه متد را در view شما نشان می‌دهد:

کاتلین

override fun onPopulateAccessibilityEvent(event: AccessibilityEvent?) {
    super.onPopulateAccessibilityEvent(event)
    // Call the super implementation to populate its text for the
    // event. Then, add text not present in a super class.
    // You typically only need to add the text for the custom view.
    if (text?.isNotEmpty() == true) {
        event?.text?.add(text)
    }
}

override fun onInitializeAccessibilityEvent(event: AccessibilityEvent?) {
    super.onInitializeAccessibilityEvent(event)
    // Call the super implementation to let super classes
    // set appropriate event properties. Then, add the new checked
    // property that is not supported by a super class.
    event?.isChecked = isChecked()
}

override fun onInitializeAccessibilityNodeInfo(info: AccessibilityNodeInfo?) {
    super.onInitializeAccessibilityNodeInfo(info)
    // Call the super implementation to let super classes set
    // appropriate info properties. Then, add the checkable and checked
    // properties that are not supported by a super class.
    info?.isCheckable = true
    info?.isChecked = isChecked()
    // You typically only need to add the text for the custom view.
    if (text?.isNotEmpty() == true) {
        info?.text = text
    }
}

جاوا

@Override
public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
    super.onPopulateAccessibilityEvent(event);
    // Call the super implementation to populate its text for the
    // event. Then, add the text not present in a super class.
    // You typically only need to add the text for the custom view.
    CharSequence text = getText();
    if (!TextUtils.isEmpty(text)) {
        event.getText().add(text);
    }
}

@Override
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
    super.onInitializeAccessibilityEvent(event);
    // Call the super implementation to let super classes
    // set appropriate event properties. Then, add the new checked
    // property that is not supported by a super class.
    event.setChecked(isChecked());
}

@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
    super.onInitializeAccessibilityNodeInfo(info);
    // Call the super implementation to let super classes set
    // appropriate info properties. Then, add the checkable and checked
    // properties that are not supported by a super class.
    info.setCheckable(true);
    info.setChecked(isChecked());
    // You typically only need to add the text for the custom view.
    CharSequence text = getText();
    if (!TextUtils.isEmpty(text)) {
        info.setText(text);
    }
}

شما می‌توانید این متدها را مستقیماً در کلاس view سفارشی خود پیاده‌سازی کنید.

ارائه یک زمینه دسترسی سفارشی

سرویس‌های دسترسی می‌توانند سلسله مراتب نمای یک جزء رابط کاربری که یک رویداد دسترسی ایجاد می‌کند را بررسی کنند. این امر به سرویس‌های دسترسی اجازه می‌دهد تا اطلاعات زمینه‌ای غنی‌تری را برای کمک به کاربران ارائه دهند.

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

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

در مثال شکل ۱، کل تقویم به صورت یک نمای واحد پیاده‌سازی شده است، بنابراین سرویس‌های دسترسی اطلاعات کافی در مورد محتوای نما و انتخاب کاربر در نما دریافت نمی‌کنند، مگر اینکه توسعه‌دهنده اطلاعات اضافی ارائه دهد. به عنوان مثال، اگر کاربری روی روزی که با برچسب ۱۷ مشخص شده است کلیک کند، چارچوب دسترسی فقط اطلاعات توصیفی را برای کل کنترل تقویم دریافت می‌کند. در این حالت، سرویس دسترسی TalkBack عبارت «تقویم» یا «تقویم آوریل» را اعلام می‌کند و کاربر نمی‌داند چه روزی انتخاب شده است.

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

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

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

برای ارائه یک سلسله مراتب نمای مجازی برای یک نما، متد getAccessibilityNodeProvider() را در نمای سفارشی یا گروه نمای خود بازنویسی کنید و یک پیاده‌سازی از AccessibilityNodeProvider را برگردانید. می‌توانید با استفاده از کتابخانه پشتیبانی با متد ViewCompat.getAccessibilityNodeProvider() یک سلسله مراتب نمای مجازی پیاده‌سازی کنید و یک پیاده‌سازی با AccessibilityNodeProviderCompat ارائه دهید.

برای ساده‌سازی وظیفه ارائه اطلاعات به سرویس‌های دسترسی و مدیریت تمرکز دسترسی، می‌توانید ExploreByTouchHelper را پیاده‌سازی کنید. این متد یک AccessibilityNodeProviderCompat ارائه می‌دهد و می‌تواند با فراخوانی setAccessibilityDelegate به عنوان AccessibilityDelegateCompat به یک view متصل شود. برای مثال، به ExploreByTouchHelperActivity مراجعه کنید. ExploreByTouchHelper همچنین توسط ویجت‌های فریم‌ورک مانند CalendarView ، از طریق view فرزند آن SimpleMonthView ، استفاده می‌شود.

مدیریت رویدادهای لمسی سفارشی

کنترل‌های نمای سفارشی ممکن است به رفتار رویداد لمسی غیر استاندارد نیاز داشته باشند، همانطور که در مثال‌های زیر نشان داده شده است.

تعریف اقدامات مبتنی بر کلیک

اگر ویجت شما از رابط OnClickListener یا OnLongClickListener استفاده می‌کند، سیستم اکشن‌های ACTION_CLICK و ACTION_LONG_CLICK را برای شما مدیریت می‌کند. اگر برنامه شما از ویجت سفارشی‌تری استفاده می‌کند که به رابط OnTouchListener متکی است، هندلرهای سفارشی برای اکشن‌های دسترسی مبتنی بر کلیک تعریف کنید. برای انجام این کار، متد replaceAccessibilityAction() را برای هر اکشن فراخوانی کنید، همانطور که در قطعه کد زیر نشان داده شده است:

کاتلین

override fun onCreate(savedInstanceState: Bundle?) {
    ...

    // Assumes that the widget is designed to select text when tapped, and selects
    // all text when tapped and held. In its strings.xml file, this app sets
    // "select" to "Select" and "select_all" to "Select all".
    ViewCompat.replaceAccessibilityAction(
        binding.textSelectWidget,
        ACTION_CLICK,
        getString(R.string.select)
    ) { view, commandArguments ->
        selectText()
    }

    ViewCompat.replaceAccessibilityAction(
        binding.textSelectWidget,
        ACTION_LONG_CLICK,
        getString(R.string.select_all)
    ) { view, commandArguments ->
        selectAllText()
    }
}

جاوا

@Override
protected void onCreate(Bundle savedInstanceState) {
    ...

    // Assumes that the widget is designed to select text when tapped, and select
    // all text when tapped and held. In its strings.xml file, this app sets
    // "select" to "Select" and "select_all" to "Select all".
    ViewCompat.replaceAccessibilityAction(
            binding.textSelectWidget,
            ACTION_CLICK,
            getString(R.string.select),
            (view, commandArguments) -> selectText());

    ViewCompat.replaceAccessibilityAction(
            binding.textSelectWidget,
            ACTION_LONG_CLICK,
            getString(R.string.select_all),
            (view, commandArguments) -> selectAllText());
}

ایجاد رویدادهای کلیک سفارشی

یک کنترل سفارشی می‌تواند از متد شنونده‌ی onTouchEvent(MotionEvent) برای شناسایی رویدادهای ACTION_DOWN و ACTION_UP استفاده کند و یک رویداد کلیک ویژه را فعال کند. برای حفظ سازگاری با سرویس‌های دسترسی، کدی که این رویداد کلیک سفارشی را مدیریت می‌کند باید موارد زیر را انجام دهد:

  1. یک AccessibilityEvent مناسب برای عمل کلیک تفسیر شده ایجاد کنید.
  2. سرویس‌های دسترسی را فعال کنید تا عملکرد کلیک سفارشی را برای کاربرانی که قادر به استفاده از صفحه لمسی نیستند، انجام دهند.

برای مدیریت کارآمد این الزامات، کد شما باید متد performClick() را بازنویسی کند، که باید پیاده‌سازی فوق این متد را فراخوانی کند و سپس هر عملی را که توسط رویداد کلیک مورد نیاز است، اجرا کند. هنگامی که عمل کلیک سفارشی شناسایی شد، آن کد باید متد performClick() شما را فراخوانی کند. مثال کد زیر این الگو را نشان می‌دهد.

کاتلین

class CustomTouchView(context: Context) : View(context) {

    var downTouch = false

    override fun onTouchEvent(event: MotionEvent): Boolean {
        super.onTouchEvent(event)

        // Listening for the down and up touch events.
        return when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                downTouch = true
                true
            }

            MotionEvent.ACTION_UP -> if (downTouch) {
                downTouch = false
                performClick() // Call this method to handle the response and
                // enable accessibility services to
                // perform this action for a user who can't
                // tap the touchscreen.
                true
            } else {
                false
            }

            else -> false  // Return false for other touch events.
        }
    }

    override fun performClick(): Boolean {
        // Calls the super implementation, which generates an AccessibilityEvent
        // and calls the onClick() listener on the view, if any.
        super.performClick()

        // Handle the action for the custom click here.

        return true
    }
}

جاوا

class CustomTouchView extends View {

    public CustomTouchView(Context context) {
        super(context);
    }

    boolean downTouch = false;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        super.onTouchEvent(event);

        // Listening for the down and up touch events
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                downTouch = true;
                return true;

            case MotionEvent.ACTION_UP:
                if (downTouch) {
                    downTouch = false;
                    performClick(); // Call this method to handle the response and
                                    // enable accessibility services to
                                    // perform this action for a user who can't
                                    // tap the touchscreen.
                    return true;
                }
        }
        return false; // Return false for other touch events.
    }

    @Override
    public boolean performClick() {
        // Calls the super implementation, which generates an AccessibilityEvent
        // and calls the onClick() listener on the view, if any.
        super.performClick();

        // Handle the action for the custom click here.

        return true;
    }
}

الگوی قبلی با استفاده از متد performClick() برای تولید یک رویداد دسترسی و فراهم کردن یک نقطه ورود برای سرویس‌های دسترسی جهت عمل به نمایندگی از کاربری که رویداد کلیک سفارشی را انجام می‌دهد، به تضمین سازگاری رویداد کلیک سفارشی با سرویس‌های دسترسی کمک می‌کند.