简体   繁体   中英

How to prevent android talkback to speak seekbar progress

I have a custom control extending SeekBar , in which I have overridden onInitializeAccessibilityNodeInfo as below:

@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
    super.onInitializeAccessibilityNodeInfo(info);
    info.setClassName(TextView.class.getName());
}

Then I set content description as "my control" to this control, but when accessibility focus comes to this control, talkback still speak "my control, %x percent". I don't want it to speak progress ie "x percent". Instead of that I want to override the talkback text so that instead of it announcing "x percent..." it announces custom text that I set eg "x value selected". How to do it?

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.

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.

For me worked on API level >= 28 in combination with ProgressBar (SeekBar should also work) the following code. Setting the state description to , will cause the accessibility service (tested with Talkback) to only read the view type.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
    // prevent "zero percent" talkback; null or empty string does not work
    pbProgress.setStateDescription("\u2800");
}

Null resets the state description to its default behaviour. Empty strings or spaces seem to be ignored.

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" ... />

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM