I have a custom control extending SeekBar
, in which I have overridden onInitializeAccessibilityNodeInfo
as below:
@Override
public
Thanks to @nick-peppers answer https://stackoverflow.com/a/58434362/2713403 I had started to understand better how AccessibilityDelegate works, so I made a different solution because I wanted to select the seekbar. By removing the super call from OnInitializeAccessibilityEvent method, it wasn't possible to keep the selection on the seek bar if the user clicks, so I made this (Tested on Android 11, 10 and 9)
...
seekbar.setAccessibilityDelegate(new customSeekBarDelegate());
...
/**
* Create a custom delegate to modify what Talkback is going to read out loud
*/
private class customSeekBarDelegate extends View.AccessibilityDelegate
{
/**
* If the selected view is the slider, populate the text to read by talkback.
* On Android 10 and 9, the view got selected from the beginning without touch the slider,
* so the TYPE_VIEW_SELECTED event is controlled.
* The text should be overwritten to trigger what Talkback do need to read.
*
* @param host The view selected
* @param event The event to initialise
*/
@Override public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event)
{
super.onInitializeAccessibilityEvent(host, event);
if (event.getEventType() != AccessibilityEvent.TYPE_VIEW_SELECTED)
{
seekbarText.setText(seekbarText.getText());
}
}
/**
* Send all accessibility events except the focused accessibility event
* because it reads the percentage, so it needs to be changed to no focused to read
* the sliderText.
*
* @param host the view selected
* @param eventType The accessibility event to send
*/
@Override public void sendAccessibilityEvent(View host, int eventType)
{
if (eventType == AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED)
{
eventType = AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED;
}
super.sendAccessibilityEvent(host, eventType);
}
/**
* If the slider changes, it won't send the AccessibilityEvent TYPE_WINDOW_CONTENT_CHANGED
* because it reads the percentages, so in that way it will read the sliderText.
* On Android 10 and 9, the view got selected when it changes, so the TYPE_VIEW_SELECTED
* event is controlled.
*
* @param host the view selected
* @param event the accessibility event to send
*/
@Override public void sendAccessibilityEventUnchecked(View host, AccessibilityEvent event)
{
if (event.getEventType() != AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
&& event.getEventType() != AccessibilityEvent.TYPE_VIEW_SELECTED)
{
super.sendAccessibilityEventUnchecked(host, event);
}
}
}
So in that way, I could have selected the seekbar, and read out loud without percentages. Don't forguet to attach the event SeekBar.OnSeekBarChangeListener to the seekbar to update the seekbar text when it changes.
There are two ways to achieve this:
Programmatically you can say:
view.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
In your XML file, set the importance to "no".
<SeekBar
android:importantForAccessibility="no"
... />
I did something similar in Xamarin.Android in C#, but the code should be easy enough to port back to Java. Also, keep in mind these changes will override the initial talkback when the control is highlighted.
First on the textView that is keeping track of the value set:
textView.AccessibilityLiveRegion = AndroidViews.AccessibilityLiveRegion.Assertive;
Then I had to create my own custom AccessibilityDelegate
:
public class CustomSeekbarDelegate : Android.Views.View.AccessibilityDelegate
{
public override void OnInitializeAccessibilityEvent(AndroidViews.View host, AccessibilityEvent e)
{
//NOTE: Don't call base to prevent seekbar talkback percentage
}
}
seekBar.SetAccessibilityDelegate(new CustomSeekbarDelegate());
There's probably a way to keep both the initial spoken control accessibility when first highlighted, but there's not much online about it and this was good enough for our needs. Plus, I didn't have additional time to try and get that working as well.