Caso seu aplicativo precise de um componente de visualização personalizado, será necessário tornar a visualização mais acessível. As etapas abaixo podem melhorar a acessibilidade da sua visualização personalizada, conforme descrito nesta página:
- Processar cliques do controle direcional.
- Implementar métodos da API de acessibilidade.
- Enviar objetos
AccessibilityEvent
específicos para sua visualização personalizada. - Preencher
AccessibilityEvent
eAccessibilityNodeInfo
na visualização.
Processar cliques do controle direcional
Na maioria dos dispositivos, clicar em uma visualização usando um controle direcional envia um
KeyEvent
com
KEYCODE_DPAD_CENTER
para a visualização em foco no momento. Todas as visualizações padrão do Android processam
KEYCODE_DPAD_CENTER
corretamente. Ao criar um controle
View
personalizado, verifique se esse evento tem o mesmo efeito que tocar a visualização na tela touchscreen.
Seu controle personalizado precisa tratar o evento
KEYCODE_ENTER
da mesma forma que KEYCODE_DPAD_CENTER
. Isso facilita as interações do usuário com um teclado
completo.
Implementar métodos da API de acessibilidade
Eventos de acessibilidade são mensagens sobre interações dos usuários com os componentes da interface visual
do app. Essas mensagens são processadas pelos serviços de acessibilidade,
que usam as informações nesses eventos para gerar feedback e solicitações complementares. Os métodos
de acessibilidade fazem parte das classes
View
e
View.AccessibilityDelegate
. Os métodos são estes:
dispatchPopulateAccessibilityEvent()
onPopulateAccessibilityEvent()
para a visualização.
Em seguida, chama o método dispatchPopulateAccessibilityEvent()
para cada filha
dessa visualização. onInitializeAccessibilityEvent()
TextView
ou
Button
, substitua esse método
e defina as outras informações sobre sua visualização, como o tipo de campo de senha, o tipo de
caixa de seleção ou estados que oferecem interação ou feedback do usuário no
evento. Se você substituir esse método, chame a superimplementação e modifique somente as propriedades
que não são definidas pela superclasse.onInitializeAccessibilityNodeInfo()
View
tem um conjunto modelo de propriedades de visualização, mas se a
visualização personalizada oferecer controle de interação além de uma TextView
ou um
Button
simples, substitua esse método e defina as outras informações sobre sua visualização
no objeto AccessibilityNodeInfo
processado por esse método.onPopulateAccessibilityEvent()
AccessibilityEvent
para sua
visualização. Ele também é chamado se a visualização for filha de uma outra que gera um evento
de acessibilidade.
onRequestSendAccessibilityEvent()
AccessibilityEvent
. Essa etapa permite que a visualização mãe adicione
mais informações ao evento de acessibilidade. Implemente esse método somente se a visualização personalizada puder ter
visualizações filhas e se a mãe puder fornecer informações de contexto ao evento
que sejam úteis para os serviços de acessibilidade.sendAccessibilityEvent()
- O sistema chama esse método quando um usuário realiza uma ação em uma visualização. O evento é classificado com
um tipo de ação do usuário, como
TYPE_VIEW_CLICKED
. Em geral, será necessário enviar umAccessibilityEvent
sempre que o conteúdo da visualização personalizada mudar. sendAccessibilityEventUnchecked()
- Esse método é usado quando o código de chamada precisa controlar diretamente a verificação de
acessibilidade sendo ativada no dispositivo
(
AccessibilityManager.isEnabled()
). Se você implementar esse método, faça a chamada como se a acessibilidade estivesse ativada, independente da configuração do sistema. Normalmente, não é necessário implementar esse método para uma visualização personalizada. dispatchPopulateAccessibilityEvent()
onInitializeAccessibilityEvent()
onInitializeAccessibilityNodeInfo()
onPopulateAccessibilityEvent()
TYPE_VIEW_CLICKED
TYPE_VIEW_FOCUSED
TYPE_VIEW_HOVER_ENTER
TYPE_VIEW_HOVER_EXIT
TYPE_VIEW_LONG_CLICKED
TYPE_VIEW_SCROLLED
- Gerar um
AccessibilityEvent
adequado para a ação de clique interpretada. - Ativar os serviços de acessibilidade para realizar a ação de clique personalizada para usuários que não podem usar uma tela touchscreen.
Para oferecer suporte à acessibilidade, substitua e implemente os métodos anteriores diretamente na classe de visualização personalizada.
No mínimo, implemente os métodos de acessibilidade abaixo para a classe da visualização personalizada:
Para mais informações sobre como implementar esses métodos, consulte a seção sobre como preencher eventos de acessibilidade.
Enviar eventos de acessibilidade
Dependendo das especificidades da sua visualização personalizada, pode ser necessário enviar objetos
AccessibilityEvent
em momentos diferentes ou para eventos não processados pela implementação
padrão. A classe View
oferece uma implementação padrão para estes tipos de
evento:
Em geral, será necessário enviar um AccessibilityEvent
sempre que o conteúdo da visualização
personalizada mudar. Por exemplo, se você estiver implementando uma barra de controle deslizante personalizada que
permite ao usuário selecionar um valor numérico pressionando as setas para a esquerda ou para a direita, sua visualização personalizada precisará emitir um evento do tipo
TYPE_VIEW_TEXT_CHANGED
sempre que o valor do controle deslizante mudar. O exemplo de código abaixo demonstra o uso do método
sendAccessibilityEvent()
para relatar esse evento.
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; } ... }
Preencher eventos de acessibilidade
Cada AccessibilityEvent
tem um conjunto de propriedades obrigatórias que descrevem o estado
atual da visualização. Essas propriedades incluem itens como o nome da classe da visualização, a descrição do
conteúdo e o estado verificado. As propriedades específicas obrigatórias para cada tipo de evento são descritas
na
documentação de referência do
AccessibilityEvent
.
A implementação View
oferece valores padrão para essas
propriedades obrigatórias. Muitos desses valores, incluindo o nome da classe e o carimbo de data/hora do evento, são
disponibilizados automaticamente. Se você estiver criando um componente de visualização personalizado, será preciso fornecer informações sobre o conteúdo e
as características da visualização. Essas informações podem ser simples como uma etiqueta de
botão e podem incluir outras informações de estado que você queira adicionar ao evento.
Use os métodos
onPopulateAccessibilityEvent()
e
onInitializeAccessibilityEvent()
para preencher ou modificar as informações em um AccessibilityEvent
. Use o método
onPopulateAccessibilityEvent()
especificamente para adicionar ou modificar o conteúdo de texto
do evento, que é transformado em solicitações audíveis por serviços de acessibilidade, como o
TalkBack. Use o método onInitializeAccessibilityEvent()
para preencher outras informações
sobre o evento, como o estado de seleção da visualização.
Além disso, implemente o
método
onInitializeAccessibilityNodeInfo()
. Os serviços de acessibilidade usam os objetos AccessibilityNodeInfo
preenchidos por esse
método para investigar a hierarquia de visualização que gera um evento de acessibilidade após o recebimento
e fornecer feedback adequado aos usuários.
O exemplo de código abaixo mostra como substituir esses três métodos na visualização:
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); } }
É possível implementar esses métodos diretamente na sua classe de visualização personalizada.
Oferecer um contexto de acessibilidade personalizado
Os serviços de acessibilidade podem inspecionar a hierarquia de visualização que contém um componente da interface do usuário que gera um evento de acessibilidade. Isso permite que os serviços de acessibilidade forneçam informações contextuais mais avançadas para ajudar os usuários.
Há casos em que os serviços de acessibilidade não recebem informações adequadas da hierarquia de visualização. Um exemplo disso é um controle de interface personalizado que tem duas ou mais áreas clicáveis separadas, como um controle de agenda. Nesse caso, os serviços não recebem as informações adequadas porque as subseções clicáveis não fazem parte da hierarquia de visualização.
No exemplo da Figura 1, toda a agenda é implementada como uma única visualização. Dessa forma, os serviços de acessibilidade não recebem informações suficientes sobre a seleção do usuário e o conteúdo na visualização, a menos que o desenvolvedor forneça outros informações. Por exemplo, se o usuário clicar no dia marcado como 17, o framework de acessibilidade receberá apenas as informações de descrição de todo o controle da agenda. Nesse caso, o serviço de acessibilidade do TalkBack anuncia "Agenda" ou "Agenda de abril", e o usuário não sabe qual dia está selecionado.
Para oferecer informações de contexto adequadas para serviços de acessibilidade em situações como essa, o framework traz uma maneira de especificar uma hierarquia de visualização virtual. Uma hierarquia de visualização virtual é uma maneira de os desenvolvedores de apps fornecerem uma hierarquia de visualização complementar aos serviços de acessibilidade, que corresponde melhor às informações na tela. Essa abordagem permite que os serviços de acessibilidade ofereçam informações de contexto mais úteis aos usuários.
Outra situação em que uma hierarquia de visualização virtual pode ser necessária é uma interface do usuário que contém
um conjunto de controles View
com funções estreitamente relacionadas, em que uma ação em um
controle afeta o conteúdo de um ou mais elementos, como um seletor de número com botões diferentes para cima
e para baixo. Nesse caso, os serviços de acessibilidade não recebem informações adequadas, porque uma
ação em um controle muda o conteúdo em outro, e a relação desses controles pode não
estar visível para o serviço.
Para lidar com essa situação, agrupe os controles relacionados em um contêiner de visualização e forneça uma hierarquia de visualização virtual desse contêiner para representar claramente as informações e o comportamento disponibilizados pelos controles.
Para oferecer uma hierarquia virtual para uma visualização, substitua o método
getAccessibilityNodeProvider()
na visualização personalizada ou no grupo e retorne uma implementação de
AccessibilityNodeProvider
.
É possível implementar uma hierarquia de visualização virtual usando a Biblioteca de Suporte com o método
ViewCompat.getAccessibilityNodeProvider()
e fornecer uma implementação com
AccessibilityNodeProviderCompat
.
Para gerenciar
o foco em acessibilidade e fornecer informações a esses serviços de forma mais simples, você pode implementar o
ExploreByTouchHelper
.
Ele fornece um AccessibilityNodeProviderCompat
e pode ser anexado como o
AccessibilityDelegateCompat
de uma visualização chamando
setAccessibilityDelegate
.
Para conferir um exemplo, consulte
ExploreByTouchHelperActivity
.
ExploreByTouchHelper
também é usado por widgets de framework, como
CalendarView
,
pela visualização filha
SimpleMonthView
.
Processar eventos de toque personalizados
Os controles de visualização personalizados podem exigir um comportamento de evento de toque não padrão, conforme demonstrado nos exemplos abaixo.
Definir ações com base em cliques
Se o widget usar a interface
OnClickListener
ou
OnLongClickListener
,
o sistema vai gerenciar as ações
ACTION_CLICK
e
ACTION_LONG_CLICK
para você. Se o app usar um widget mais personalizado que depende da interface
OnTouchListener
,
você precisará definir gerenciadores personalizados para as ações de acessibilidade com base em cliques. Para fazer isso, chame o método
replaceAccessibilityAction()
para cada ação, conforme mostrado no snippet de código abaixo:
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()); }
Criar eventos de clique personalizados
Um controle personalizado pode usar o método listener onTouchEvent(MotionEvent)
para detectar
os eventos ACTION_DOWN
e
ACTION_UP
e
acionar um evento de clique especial. Para manter a compatibilidade com os serviços de acessibilidade, o código que
processa esse evento de clique personalizado precisa fazer o seguinte:
Para processar esses requisitos de forma eficiente, seu código precisa substituir o
método performClick()
e depois chamar a superimplementação desse método e executar as ações necessárias
para o evento de clique. Quando a ação de clique personalizada for detectada, o código vai chamar o
método performClick()
. O exemplo de código abaixo demonstra esse padrão.
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; } }
O padrão anterior ajuda a garantir que o evento de clique personalizado seja compatível com os serviços
de acessibilidade usando o método performClick()
para gerar um evento de acessibilidade.
Além disso, ele fornece um ponto de entrada para que os serviços de acessibilidade atuem em nome de um usuário realizando o evento de
clique personalizado.