So I have a requirement around accessibility wherein a button needs to be read out when it comes in focus and then another text to be read out after user clicks on it.
For example,
An "OK" button when focussed should read "OK" but when a user taps on it, it should read out some other text eg. "Navigating to the other page".
Is there a way in Android to implement this?
I have not been able to find anything around it.
You can use View#announceForAccessibility(CharSequence) to make a general announcement - so in your OnClickListener get a reference to some View (e.g. your Button, it doesn't matter what it is) and call that on it.
Like it says in the docs, this is a convenience function that creates a very general "something is getting announced for no specific reason" event - you might want to give more context, like creating a TYPE_VIEW_CLICKED event. This might be more helpful to the user (depending on how the accessibility service handles it) and could provide a better experience, since stuff that's read out is prioritised depending on what it is. I don't have time to get into it here, but it's something you can investigate if you want
Also I'm not sure if this is what you mean, but just in case - if the user focuses your button, it should say "[OK] button, double tap to [some description]". The bits in brackets you can customise, the rest is standard description for a Button in the UI. You shouldn't change this to just say "OK".
That predictable and consistent system is there for a reason, to help partially sighted and blind people understand exactly what's going on with the app they're using. It might sound clunky at times, but it's meant to be functional, not slick. So we shouldn't try to get around it and make it "sound better" by removing important info and context that some people really need. I don't know if that's what you meant, but it's always worth mentioning!
Related
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.
I currently retrieve the root node of the active window with getRootInActiveWindow(). Afterwards, I perform a breadth first search to get a list of all nodes.
My questions:
How can I traverse this list of nodes to get the focus order? Is this list ordered according to the nodes' focus order already? Is there a different approach of retrieving the focus order?
Thanks in advance.
Ultimately, no. You cannot, and the accessibility api documentation on this is very misleading.
One might think that the "focusSearch(int direction)" function of AccessibilityNodeInfo would be the way to go. But, ultimately it is broken, and if you go back into the inards of the Android Accessibility APIs you end up finding a function that's documented as such:
Searches for the nearest view in the specified direction that can take the input focus.
COOL, just what we want right? HOLD ON A SECOND. This function calls a function
AccessibilityInteractionClient.getInstance().focusSearch(mConnectionId, mWindowId, mSourceNodeId, direction);
And this function is documented as such:
Finds the accessibility focused {#link android.view.accessibility.AccessibilityNodeInfo}. The search is performed in the window whose id is specified and starts from the node whose accessibility id is specified.
Dissappointingly, you will also find that this function does neither of those jobs (directional focus search, or directional accessibility focus search) well.
Ultimately, when an element in TalkBack gains accessibility focus due to the focus of an element, it's actually responding to the actual focus event coming from the OS and NOT any calculation that TalkBack is doing on input focus ordering.
In order to do this, you would basically have to implement the entire logic from the OS for Tab Ordering yourself, with less information the system itself has to do it, because the System has real android.widget.View objects to deal with, not fake AccessibilityNodeInfo objects with limited information. Any way you can come up to do this is going to be exceptionally fragile, and not guaranteed to line up with what the system actually does.
Your inclination to try a breadth first traversal of "input focusable" (the focusable property of AccessibilityNodeInfo) is a pretty reasonable solution. The problem is going to be, that the real system Tab Ordering is going to be a subset of that, which ignores things like elements existing on different windows and such.
Seriously, open up the TalkBack settings screen, turn on TalkBack, connect a hardware keyboard, go crazy on the tab and shift tab keys, and marvel at how you can't put focus on the "Settings" button. Your traversal function would get this wrong, because the "Settings" button is focusable but somehow, you can't put focus there. The reason why digs into some serious AOSP nonsense that I will omit, and no, I'm not sure if the information that would keep this corner case from coming up in your theoretical algorithm is available to AccessibilityServices.
To summarize, the breadth first traversal can theoretically work, but there are going to be a ton of corner cases, and no I'm not sure if the information to deal with each corner case correctly is available to AccessibilityServices. Also, notably, because of this particular issue, if you'd like the solution to be correct across 4.4, 5.0 and 7.0+ devices, you will have to have different variations of this calculation for different OS versions. YEP, those corner cases are going to be different! (Queue Evil Laughter).
My honest recommendation... give up.
this is my ImageButton. my confusion is that i do not know how to determine whether the user clicked on the Login half, or the Sign up half, so i can take them to the respective Activity. i would be incredibly grateful if somebody can 1) name a resource i can use to learn on my own, or 2) explain the process of finding view locations and any possible algorithms used to determine if a click was in bounds of a specific portion of a View or not.
You should divide your button into 2 different ones, and process clicks separately.
Have 2 image buttons and try to achieve the same look and feel you want.
In your case, even though this kind of thing is doable. It is expensive and complicated and unnecessary. And you will have problems with styling of the buttons (eg: focus)
Edit:
Yes I understand, But I suggest you not to over complicate things. Because, your device has limited resources (eg: battery),
But you can still get the experience on how to create a circle like button with two sides on it, using 2 buttons which is still exciting :D.
I have done live user testing for a list in my app, and have come to an interesting puzzle.
A list, specifically a single-column table in iOS, may often have a swipe right gesture for more actions, like the twitter app and mail app, and a million other apps. But when important functionality is embedded in the UI beyond that action, and a user cannot figure it out, the only thing that comes to mind to alleviate that is something like the accessory button, ie. a right-pointing triangle or chevron button.
There is probably another way but it's not coming to me. Maybe making the rows taller than normal?
This was meant as a comment but was rejected as too-long.
So after thinking more about this, the ideal solution is a visual clue, rather than painfully obvious text saying "swipe a row for more options". Perhaps when a list (UITableView, etc) is shown and rows (UITableViewCell, etc) are created and added, then as they appear an animation begins of the main visible content sliding into place in the OPPOSITE direction of the desired swipe animation, with a minimal visual indicator afterward, reminding the user that the content is moveable!
Sound good? Optionally, immediately before the animation begins, any underlying content may be shown for a split second (if its supposed to appear underneath). Sweet!
I apologize if I'm just missing the obvious. I'm fairly new to Android development, and while I searched for this particular topic, I wasn't exactly sure what to look for (in terms of a "name").
In an application I'm writing, I have a section where the user can enter the names of players. However, this can range anywhere from 1 to whatever, no limit. However, I'm not sure what the best approach for this kind of feature is, or if there's a component that already does something like it.
Basically, the functionality I'm looking for is similar to what you can see in the Edit Contact screen of the phone book; for the phone numbers and email addresses, you can push a little plus button to add a new number/address, or hit the little minus button to remove a number/address.
I can think of several ways to potentially implement this, but in the end I think wrapping it in a custom component would be best (so that you could call "Get Players" and have it return a list of strings by going through each of the inputs and getting the values).
Am I just overthinking this? Is there a component that does that already? Is there some example code that demonstrates a good way to do this?
Thanks!
Could you just use ListView and add a menu with a "Add Players" option? You could customize the list view to have a little checkbox, for example, and then begin the game by pressing the menu ... or add new players dynamically by pressing another menu button.
After playing around with some ideas, I came across a solution that I think will suit what I'm doing. I created a custom component extending LinearLayout. As part of the creation of the component, it creates a row that says "Add new..." with a plus-sign button. Pressing the plus sign button then creates a new row containing an EditText and a minus-button which will remove the row.
I then created a method for this component called getTexts() which returns a List that has all the non-empty Text values from all the components. Testing it in a dummy app, it seemed to work fine.
I think I need to make tweaks to make it more robust (like the ability to add rows programatically, listeners to alert other components when a row is added/removed, and the ability to set the individual EditText values, for instance), but this functions as I imagined.