Android8之前版本 Spannable无效问题

作者: iceIC | 来源:发表于2019-11-01 16:20 被阅读0次

    最近发现在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无效问题

        本文链接:https://www.haomeiwen.com/subject/fhvfbctx.html