Android8之前版本 Spannable无效问题
2019-11-01 本文已影响0人
iceIC
最近发现在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,所以使用的时候需要注意。