Android8之前版本 Spannable无效问题

最近发现在Android8.0之前的手机上设置Spannable无效,后来调研发现问题是因为textAllCaps属性和Spannable冲突问题,把textAllCaps属性设置为false就好了。

下面是具体原因:

textAllCaps属性会作用在 AllCapsTransformationMethod类上
这个类是用来实现将文字变大写的,具体来说是其中的getTransformation方法

8.0之前

该方法在8.0以前的源码是:

    @Override
    public CharSequence getTransformation(CharSequence source, View view) {
        //省略若干无用代码
        return source.toString().toUpperCase();
    }

这个方法的入参其实是个Spannable对象,通过toString().toUpperCase()等操作,返回对象变成了String丢掉了很多我们所期望的特效(删除线、背景色、图片等)

8.0之后

在8.0以后的源码中该方法改为返回Spannable

以8.1版本的源码为例

可以直接略过下面两块代码看结论

    @Override
    public CharSequence getTransformation(@Nullable CharSequence source, View view) {
        if (!mEnabled) {
            Log.w(TAG, "Caller did not enable length changes; not transforming text");
            return source;
        }

        if (source == null) {
            return null;
        }

        Locale locale = null;
        if (view instanceof TextView) {
            locale = ((TextView)view).getTextLocale();
        }
        if (locale == null) {
            locale = mLocale;
        }
        final boolean copySpans = source instanceof Spanned;
        return TextUtils.toUpperCase(locale, source, copySpans);
    }

TextUtils的toUpperCase方法源码如下:

    public static CharSequence toUpperCase(@Nullable Locale locale, @NonNull CharSequence source,
            boolean copySpans) {
        final Edits edits = new Edits();
        if (!copySpans) { // No spans. Just uppercase the characters.
            final StringBuilder result = CaseMap.toUpper().apply(
                    locale, source, new StringBuilder(), edits);
            return edits.hasChanges() ? result : source;
        }

        final SpannableStringBuilder result = CaseMap.toUpper().apply(
                locale, source, new SpannableStringBuilder(), edits);
        if (!edits.hasChanges()) {
            // No changes happened while capitalizing. We can return the source as it was.
            return source;
        }

        final Edits.Iterator iterator = edits.getFineIterator();
        final int sourceLength = source.length();
        final Spanned spanned = (Spanned) source;
        final Object[] spans = spanned.getSpans(0, sourceLength, Object.class);
        for (Object span : spans) {
            final int sourceStart = spanned.getSpanStart(span);
            final int sourceEnd = spanned.getSpanEnd(span);
            final int flags = spanned.getSpanFlags(span);
            // Make sure the indices are not at the end of the string, since in that case
            // iterator.findSourceIndex() would fail.
            final int destStart = sourceStart == sourceLength ? result.length() :
                    toUpperMapToDest(iterator, sourceStart);
            final int destEnd = sourceEnd == sourceLength ? result.length() :
                    toUpperMapToDest(iterator, sourceEnd);
            result.setSpan(span, destStart, destEnd, flags);
        }
        return result;
    }

发现没!在8.1版本中不再是简单的toString、toUpperCase,它会将之前的Span信息存取下来,返回的时候也会一并返回回去,这样下来TextView就可以根据这些返回的Span信息来给文本设置各种特效了。

友情提示

刚刚看了下androidx的包,发现也是简单的toString、toUpperCase,所以使用的时候需要注意。

你可能感兴趣的:(Android8之前版本 Spannable无效问题)