In my Android app I have a custom layout that is being used as a button - it consists of some TextViews and an ImageView, additionally it has some gradient background.
I'm aligning my app now to conform to the Accessibility rules. In order to do so, I would need to convert this layout into a button, so that TalkBack can correctly indicate the action, that this whole layout is clickable and serves like a button.
I know that on iOS there is a possibility to set the UIAccessibilityTraits to treat such view as a button - this kind of solution would save me a huge amount of work in terms of migration.
Is there any similar solution on Android for that? What approach should I follow in order to make this layout recognized correctly by TalkBack?
No, there's no concept of accessibility traits on Android - but you can still get a good accessibility experience without needing to specifically convert your layout into a Button.
Generally, it's most important that TalkBack (or whatever accessibility service is being used - remember, it's not just TalkBack) is able to detect that the widget is clickable and to be able to read a coherent description of what it does. The additional information that it's a button, specifically, isn't super useful, especially because there are so many different kinds of UI elements that it's often a very ambiguous question whether something even is a button.
You can test this by selecting it in TalkBack and confirming that it reads the content description properly, says something along the lines of "Double tap to activate," and performs the correct action when you double tap.
If it's not correct, make sure the content description, clickable flag, and click action are set correctly on the widget's AccessibilityNodeInfo.
Related
I'm implementing a screen for Android TV, which has a screen title and a button on the left side. And a list of custom views/rows(selectable/clickable), arranged vertically, on the right side of the page.
We want the button on the left to be in focus when the user sees that screen. For that, I'm calling button.requestFocus() in the onResume() of the fragment.
This breaks the accessibility. When talkback is enabled, the first thing announced is the button's label. What I want is to announce the title first and then the button's label.
I tried to announce a custom text(could be title) by
rootView.announceForAccessibility(accessibilityText)
where rootView is the root of the xml layout and accessibilityText a text which needs to be announced.
But it doesn't help, and the button's label gets the priority.
How can I solve the issue?
I would ask you to consider WCAG Guideline 3.2.1:
The intent of this Success Criterion is to ensure that functionality is predictable as visitors navigate their way through a document. Any component that is able to trigger an event when it receives focus must not change the context. Examples of changing context when a component receives focus include, but are not limited to:
forms submitted automatically when a component receives focus;
new windows launched when a component receives focus;
focus is changed to another component when that component receives focus; <-- emphasis here
Also a quote from the Android Accessibility Team:
So something similar that people like to do is manage accessibility focus themselves. And again, this is a bad idea. accessibility focus has to be determined by the accessibility service, and just like announcements this creates an inconsistency in experience. And actually, that one of the biggest issues that accessibility users face, inconsistency, across applications and over time.
With that said, you may want to consider looking at ensuring the focus order / priority of the component using the following attributes:
android:nextFocusUp
android:nextFocusDown
android:nextFocusLeft
android:nextFocusRight
And also ensure that any group component that may get highlighted has the importantForAccessibility attribute set.
I'd like to try help some more, but without an example XML file, it's difficult to get to your particular use case. Have you tried testing the view layout with accessibility users?
I took a cue from this article by ATAUL MUNIM. I added a check if talkback is enabled, before requesting the focus explicitly.
protected fun isTalkBackEnabled(): Boolean {
val a11yServices = context?.getSystemService(ACCESSIBILITY_SERVICE) as? AccessibilityManager
return a11yServices?.isTouchExplorationEnabled?:false
}
and
if(isTalkBackEnabled().not()) {
button.requestFocus()
}
This solution pretty much bailed me out from the problem I was facing. It was also the only way forward for me because my app's min API level is 21 which eliminates the option to use android:screenReaderFocusable
There is an element I want TalkBack to skip when reading out lout the screen.
I can set it's contentDescription to null.
I also read about ImportantForAccessibility: indicates whether an element is visible or not to the Accessibility API.
Which other APIs are there?
Is it cleaner to use ImportantForAccessibility=false over contentDescription=null?
ImportantForAccessibility=false is used to hide any element from the accessibility tree, including buttons, content etc.
contentDescription=null is only useful for things like images (there may be other items I can't think of) that you want to hide as otherwise the Accessibility Tree will do it's best to find a suitable name for an item.
A prime example would be an ImageButton - if you use contentDescription=null then it will announce 'button' and the destination / button text. If you use ImportantForAccessibility=falseit would hide that item completely from the accessibility tree.
The best analogy I can come up with (if you are familiar with Web Standards) is that contentDescription is like an alt attribute or aria-labelledby attribute and ImportantForAccessibility=false is similar to aria-hidden="true".
One thing I would caution you on - other than decorative items you should not really be hiding items from the accessibility tree, just be careful that you are not giving a different experience to screen reader users (you didn't specify your use case, just wanted to hammer that point home).
Final thing - try it with TalkBack, testing it on a device is the quickest way to know if you got it right!
I'm trying to make TalkBack work for my Android app, in my app layout I have a list of CardViews inside a RecyclerView, each CardView contains several TextViews and several Buttons: App layout image
When I turn on Android TalkBack, and tap on a CardView, TalkBack announces all the TextViews, but doesn't announce any Button. If I continue swiping right, the buttons will gain focus one by one. Not sure if my understanding is correct, but seems like TalkBack treats TextViews and Buttons differently because the buttons are focusable.
Is there a way to make TalkBack announce all content on the CardView (TextViews and Buttons) and keep the Buttons focusable (so I'll still be able to set focus on Buttons by swiping)?
PS:
One solution that worked is to directly set a content description on the CardView, and make the content description contain all content of TextViews and Buttons, but that's kind of hacky, so I'm trying to find a better solution here.
Any help is appreciated. Thanks!
The behaviour you've described is expected and your solution is the correct one.
For ViewGroups with no explicit content description, TalkBack will attempt to infer one from the children, by concatenating the content description (or text value if view extends from TextView) of all the children.
Since the buttons themselves are actionable (focusable/clickable), they are not included in the inferred content description.
Setting an explicit content description on the card is the correct approach to take - your goal should be to describe the information that the card represents. IMO, it should not contain the description for the buttons since they will be separately focusable and will be read aloud when the user focuses on them.
The best approach IMO is to hide the buttons if TalkBack is enabled, and to present the cards as entities that contain a single action and a single, explicitly-set content description.
It's important not to reduce functionality for TalkBack users - all the user goals that were achievable should still be achievable; there is no requirement that the goals must be achieved in the same way by all users. So in this case, you could make the card's primary action as the click action, and offer the actions from the buttons in another place in the app.
One pattern which works well is to display a dialog on click, and this contains all the actions. This has a couple of benefits:
the card is single action so navigating between cards is single-swipe only
the default dialog is accessible by default - no extra work needed
I wrote a blog post explaining how you can do it with (and without) a library I helped write. The section under "actions dialog" is the relevant bit.
A button is actionable. If you want your users to be able to do the actions of each button, they need to be separately focusable. If they aren't actionable, they shouldn't be buttons.
I need to make an android app that has a very dynamic ui.
For example imagine we have an activity with only one button inside that in center. Then when someone clicks the button. The button converts to edittext. When user enters some text, the editText converts to textview. And then under the textview, two new buttons automatically Appear. This proccess will go on untill user create a binary-tree like structure. I want this kind of UIs.
I Hope you Undrestand My Problem.
My question is, how can i achieve this kind of UIs?
Can you give any suggestion?
It depends on what the requirements are, are we talking about simple animations like fading out and fading in, maybe some pulsation and then edit text appearance. It depends because it might be enough to use existing tools like using the ObjectAnimator or even xml or you will have to do some fancy stuff as in writing animation code that meets your particular requirements. Apart from that a factor is what versions of Android are you trying to support JellyBean and above or Lollipop and above.
I have a custom view for which I want the user to be able to enter characters from an app-defined set of characters. To do this, as I understand it, I need to write an input method service. The user not only needs to install it, but then needs to enable the IME in the Settings > Language & keyboard, and then select the custom IME for use in the view.
This seems really crazy. I want this IME to be used for just one view in one application. I don't want it to be available system-wide or force the user to make global setting changes.
The only alternative I can see is defining my own in-app custom view and simulate an IME (probably a full-screen one) when the view gains focus. Isn't there anything better?
I do not think the IMEs are conceived for that kind of task. Their concept is to allow user input in a standardized way so it can be used across multiple applications from different vendors.
My strategy would be similar to what you are thinking:
prevent the soft keyboard from appearing,
intercept the menu button key press to show your own instead,
add a custom layout (probably a GridView or a TableView inside a RelativeLayout with bottom gravity)
use an OnItemClickListener
send the required KeyEvents to the root View. If the characters are invented, the KeyCodes do not even need to relate to the ASCII character. You just intercept the code and use at will.
Sorry I can't give you an option as you asked, but this alternative does not seem to be much more work than creating a whole new IME.
Edit: upon reading the related question, it makes sense to use android.inputmethodservice.KeyboardView instead of reinventing the wheel with the GridView.