Если в приложении используется специальный компонент представления, необходимо сделать его более доступным. Чтобы улучшить специальные возможности пользовательского представления, выполните следующие действия:
- Обработка нажатий на контроллер направления.
- Реализуйте методы Accessibility API.
- Отправьте объекты
AccessibilityEvent, относящиеся к вашему пользовательскому представлению. - Заполните поля
AccessibilityEventиAccessibilityNodeInfoдля представления.
Обработка нажатий на кнопки контроллера
На большинстве устройств нажатие на представление с помощью контроллера направления отправляет KeyEvent с KEYCODE_DPAD_CENTER в представление, находящееся в фокусе. Все стандартные представления Android обрабатывают KEYCODE_DPAD_CENTER надлежащим образом. При создании пользовательского элемента управления View убедитесь, что это событие имеет тот же эффект, что и нажатие на представление на сенсорном экране.
Ваш элемент управления должен обрабатывать событие KEYCODE_ENTER так же, как и KEYCODE_DPAD_CENTER. Это упрощает взаимодействие
с полной клавиатурой для пользователей.
Реализуйте методы Accessibility API
События специальных возможностей – это сообщения о взаимодействии пользователей с визуальными компонентами интерфейса приложения. Эти сообщения обрабатываются специальными возможностями, которые используют информацию из событий для создания дополнительных подсказок и обратной связи. Методы специальных возможностей являются частью классов View и View.AccessibilityDelegate. Ниже описаны доступные методы.
dispatchPopulateAccessibilityEvent()onPopulateAccessibilityEvent() для этого представления, а затем метод dispatchPopulateAccessibilityEvent() для каждого дочернего элемента этого представления. onInitializeAccessibilityEvent()TextView или Button, переопределите этот метод и задайте дополнительную информацию о представлении, например тип поля пароля, тип флажка или состояния, которые обеспечивают взаимодействие с пользователем или обратную связь с событием. Если вы переопределяете этот метод, вызовите его суперреализацию и измените только те свойства, которые не заданы суперклассом.onInitializeAccessibilityNodeInfo()View есть набор свойств представления, но если ваше специальное представление предоставляет интерактивное управление, выходящее за рамки простого TextView или Button, переопределите этот метод и задайте дополнительную информацию о представлении в объекте AccessibilityNodeInfo, обрабатываемом этим методом.onPopulateAccessibilityEvent()AccessibilityEvent в вашем представлении. Он также вызывается, если представление является дочерним по отношению к представлению, которое генерирует событие специальных возможностей.
onRequestSendAccessibilityEvent()AccessibilityEvent. На этом этапе родительское представление может добавить в событие специальных возможностей дополнительную информацию. Реализуйте этот метод, только если у вашего пользовательского представления могут быть дочерние представления и если родительское представление может предоставлять информацию о контексте для события специальных возможностей, которая полезна для сервисов специальных возможностей.sendAccessibilityEvent()- Система вызывает этот метод, когда пользователь выполняет действие в представлении. Событие классифицируется по типу действия пользователя, например
TYPE_VIEW_CLICKED. В общем случае вы должны отправлятьAccessibilityEventкаждый раз, когда меняется контент вашего специального представления. sendAccessibilityEventUnchecked()- Этот метод используется, когда вызывающий код должен напрямую управлять проверкой того, включены ли специальные возможности на устройстве (
AccessibilityManager.isEnabled()). Если вы реализуете этот метод, выполняйте вызов так, как если бы специальные возможности были включены, независимо от системных настроек. Обычно этот метод не нужно реализовывать для специального представления. dispatchPopulateAccessibilityEvent()onInitializeAccessibilityEvent()onInitializeAccessibilityNodeInfo()onPopulateAccessibilityEvent()TYPE_VIEW_CLICKEDTYPE_VIEW_FOCUSEDTYPE_VIEW_HOVER_ENTERTYPE_VIEW_HOVER_EXITTYPE_VIEW_LONG_CLICKEDTYPE_VIEW_SCROLLED- Создайте подходящий
AccessibilityEventдля интерпретированного действия по клику. - Включите специальные возможности, чтобы пользователи, которые не могут работать с сенсорным экраном, могли выполнять специальные действия по нажатию.
Чтобы обеспечить поддержку специальных возможностей, переопределите и реализуйте описанные выше методы специальных возможностей непосредственно в классе пользовательского представления.
Как минимум, реализуйте следующие методы специальных возможностей для своего класса пользовательского представления:
Подробнее о том, как реализовать эти методы, читайте в разделе Заполнение событий специальных возможностей.
Отправлять события специальных возможностей
В зависимости от особенностей пользовательского представления может потребоваться отправлять объекты AccessibilityEvent в разное время или для событий, не обрабатываемых реализацией по умолчанию. Класс View предоставляет реализацию по умолчанию для следующих типов событий:
В общем случае вы должны отправлять событие AccessibilityEvent каждый раз, когда меняется контент вашего специального представления. Например, если вы реализуете ползунок, который позволяет пользователю выбирать числовое значение, нажимая клавишу со стрелкой влево или вправо, ваше специальное представление должно генерировать событие TYPE_VIEW_TEXT_CHANGED при каждом изменении значения ползунка. В примере кода ниже показано, как использовать метод sendAccessibilityEvent(), чтобы сообщить об этом событии.
Kotlin
override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean { return when(keyCode) { KeyEvent.KEYCODE_DPAD_LEFT -> { currentValue-- sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED) true } ... } }
Java
@Override public boolean onKeyUp (int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) { currentValue--; sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED); return true; } ... }
Заполнение событий специальных возможностей
У каждого объекта AccessibilityEvent есть набор обязательных свойств, описывающих текущее состояние представления. К ним относятся название класса, описание контента и состояние флажка. Свойства, необходимые для каждого типа событий, описаны в AccessibilityEventсправочной документации.
В реализации View для этих обязательных свойств заданы значения по умолчанию. Многие из этих значений, включая название класса и временную метку события, предоставляются автоматически. Если вы создаете собственный компонент представления, то должны предоставить информацию о его контенте и характеристиках. Это может быть как простое название кнопки, так и дополнительная информация о состоянии, которую вы хотите добавить к событию.
Используйте методы onPopulateAccessibilityEvent() и onInitializeAccessibilityEvent(), чтобы заполнить или изменить информацию в AccessibilityEvent. Используйте метод onPopulateAccessibilityEvent(), чтобы добавить или изменить текстовое содержимое события, которое сервисы специальных возможностей, например TalkBack, преобразуют в голосовые подсказки. Используйте метод onInitializeAccessibilityEvent(), чтобы добавить дополнительную информацию о событии, например состояние выбора представления.
Также реализуйте метод onInitializeAccessibilityNodeInfo(). Сервисы специальных возможностей используют объекты AccessibilityNodeInfo, заполненные этим методом, чтобы исследовать иерархию View, которая генерирует событие специальных возможностей после его получения, и предоставлять пользователям соответствующую обратную связь.
В приведенном ниже примере кода показано, как переопределить эти три метода в представлении:
Kotlin
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 } }
Java
@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, содержащую компонент пользовательского интерфейса, который генерирует событие специальных возможностей. Это позволяет сервисам специальных возможностей предоставлять пользователям более подробную контекстную информацию.
Иногда сервисы специальных возможностей не могут получить нужную информацию из иерархии View. Примером может служить пользовательский элемент управления интерфейсом, который имеет две или более отдельно кликабельные области, например элемент управления календарем. В этом случае специальные возможности не могут получить нужную информацию, поскольку интерактивные подразделы не входят в иерархию View.
Рисунок 1. Настраиваемый календарь с возможностью выбора дней.
В примере на рисунке 1 весь календарь реализован как одно представление, поэтому сервисы специальных возможностей не получают достаточно информации о его содержимом и выборе пользователя, если разработчик не предоставляет дополнительные сведения. Например, если пользователь нажимает на день с меткой 17, фреймворк специальных возможностей получает только описание всего календаря. В этом случае TalkBack озвучит "Календарь" или "Календарь на апрель", и пользователь не узнает, какой день выбран.
Чтобы в таких ситуациях предоставлять сервисам специальных возможностей достаточно контекстной информации, фреймворк позволяет задавать виртуальную иерархию View. Иерархия виртуальных View – это способ, с помощью которого разработчики приложений могут предоставить сервисам специальных возможностей дополнительную иерархию View, которая более точно соответствует информации на экране. Это позволяет специальным возможностям предоставлять пользователям более полезную контекстную информацию.
Виртуальная иерархия View также может потребоваться, если пользовательский интерфейс содержит набор элементов управления View с тесно связанными функциями, когда действие с одним элементом управления влияет на содержимое одного или нескольких элементов, например средство выбора чисел с отдельными кнопками вверх и вниз. В этом случае специальные возможности не могут получить адекватную информацию, поскольку действие с одним элементом управления изменяет контент в другом, а связь между этими элементами может быть неочевидна для сервиса.
Чтобы решить эту проблему, сгруппируйте связанные элементы управления в одном контейнере и создайте виртуальную иерархию View, которая будет четко отражать информацию и поведение, предоставляемые элементами управления.
Чтобы предоставить виртуальную иерархию View для View, переопределите метод getAccessibilityNodeProvider() в пользовательском View или группе View и верните реализацию AccessibilityNodeProvider.
Вы можете реализовать виртуальную иерархию View, используя библиотеку поддержки с методом ViewCompat.getAccessibilityNodeProvider() и реализацию с AccessibilityNodeProviderCompat.
Чтобы упростить задачу предоставления информации службам специальных возможностей и управления фокусом специальных возможностей, вы можете реализовать ExploreByTouchHelper.
Он предоставляет AccessibilityNodeProviderCompat и может быть прикреплен как AccessibilityDelegateCompat представления путем вызова setAccessibilityDelegate.
Пример: ExploreByTouchHelperActivity.
ExploreByTouchHelper также используется виджетами фреймворка, такими как CalendarView, через дочернее представление SimpleMonthView.
Обработка собственных событий касания
Для специальных элементов управления может потребоваться нестандартное поведение событий касания, как показано в примерах ниже.
Как определить действия по клику
Если в виджете используется интерфейс OnClickListener или OnLongClickListener, система сама обрабатывает действия ACTION_CLICK и ACTION_LONG_CLICK. Если в вашем приложении используется более специализированный виджет, который зависит от интерфейса OnTouchListener, определите специальные обработчики для действий, связанных со специальными возможностями, которые основаны на кликах. Для этого вызовите метод replaceAccessibilityAction() для каждого действия, как показано в следующем фрагменте кода:
Kotlin
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() } }
Java
@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 и запускать специальное событие клика. Чтобы сохранить совместимость со специальными возможностями, код, обрабатывающий это специальное событие клика, должен выполнять следующие действия:
Чтобы эффективно обрабатывать эти требования, ваш код должен переопределять метод performClick(), который должен вызывать суперреализацию этого метода, а затем выполнять любые действия, необходимые для события клика. Когда будет обнаружено специальное действие-клик, этот код должен вызвать метод performClick(). Ниже приведен пример кода.
Kotlin
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 } }
Java
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(), который генерирует событие специальных возможностей и предоставляет точку входа для служб специальных возможностей, чтобы они могли действовать от имени пользователя, выполняющего специальное событие клика.