Odd behavior: Class type of the object retuned by the getText() method of TextView changes after creating AccessibilityNodeInfo of TextView

StackOverflow https://stackoverflow.com/questions/23610393

Question

I have a TextView with text "Hello World!" defined in layout xml

TextView textView = (TextView)findViewById(R.id.textView);

TextView's getText() method returns object of Class java.lang.String

//Returns object of String class
Toast.makeText(getApplicationContext(), textView.getText().getClass().getName(), Toast.LENGTH_LONG).show();

If the same is called after creating AccessibilityNodeInfo, it returns object of android.text.SpannableString

//Creating AccessibilityNodeInfo
AccessibilityNodeInfo info = textView.createAccessibilityNodeInfo();

//Returns object of SpannableString
Toast.makeText(getApplicationContext(), "After creating AccessibilityNodeInfo: " + textView.getText().getClass().getName(), Toast.LENGTH_LONG).show();

How is creating AccessibilityNodeInfo relevant to the object returned by the getText() method?

Note: This only happens in Android 4.3 and above

Was it helpful?

Solution

It is due to new features like select, cut, copy, paste features added to the AccessibilityNodeInfo. It was introduced in Android 4.3 and is documented here.

Select text and copy/paste

The AccessibilityNodeInfo now provides APIs that allow an AccessibilityService to select, cut, copy, and paste text in a node.

To specify the selection of text to cut or copy, your accessibility service can use the new action, ACTION_SET_SELECTION, passing with it the selection start and end position with ACTION_ARGUMENT_SELECTION_START_INT and ACTION_ARGUMENT_SELECTION_END_INT. Alternatively you can select text by manipulating the cursor position using the existing action, ACTION_NEXT_AT_MOVEMENT_GRANULARITY (previously only for moving the cursor position), and adding the argument ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN.

You can then cut or copy with ACTION_CUT, ACTION_COPY, then later paste with ACTION_PASTE.

Note: These new APIs are also available for previous versions of Android through the Android Support Library, with the AccessibilityNodeInfoCompat class.

To implement the select feature, the styleable / markup objects need to be attached to the underlying text. Hence the text type is changed from String to SpannableString.

Here is the code of View.java which introduced this feature. Following code changes the type to SpannableString.

@Override
public CharSequence getIterableTextForAccessibility() {
    if (!(mText instanceof Spannable)) {
        setText(mText, BufferType.SPANNABLE);
    }
    return mText;
}

OTHER TIPS

From looking at the source of View and TextView (at least for API level 19), it can be determined that the "culprit" is TextView.getIterableTextForAccessibility(). In the TextView class, it's defined as:

/**
 * @hide
 */
@Override
public CharSequence getIterableTextForAccessibility() {
    if (!(mText instanceof Spannable)) {
        setText(mText, BufferType.SPANNABLE);
    }
    return mText;
}

This method is called as part of the createAccessibilityNodeInfo() sequence, in particular from View.onInitializeAccessibilityNodeInfoInternal().

Since it's not documented, it's hard to know exactly why (or even if it was intended or just a side effect), but it's clear that the text will necessarily end up being a Spannable, even if it wasn't one before.

From the history of TextView.java in the GitHub mirror of the AOSP repository, it would seem that the commit that introduced this logic was made by Svetoslav Ganov. Short of asking him, I'm not sure what else you could do to learn more. :)

As for the "why would you need to know this" side of the whole affair (apart from curiosity), the exact class of the TextView's text shouldn't be relevant... I assume the legacy code was doing something like String x = (String)textView.getText(). If you need a String instead of a CharSequence, replacing those calls by getText().toString() should be enough.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top