解决SpannableString在Android组件间传递时显示失效的问题
问题:在a activity中传递一个spannablestring到b activity中,并最终传递到b activity中的textview中,但是没有展示出span效果。
解决:阅读textview.settext()方法
// if suggestions are not enabled, remove the suggestion spans from the text if (!issuggestionsenabled()) { text = removesuggestionspans(text); } ... if (type == buffertype.editable || getkeylistener() != null || neededitablefornotification) { //略 } else if (precomputed != null) { //略 } else if (type == buffertype.spannable || mmovement != null) { text = mspannablefactory.newspannable(text); } else if (!(text instanceof charwrapper)) { text = textutils.stringorspannedstring(text); }
看到会根据buffertype对传入的text重新赋值,于是回溯找到传入buffertype的地方:
public void settext(charsequence text, buffertype type) { settext(text, type, true, 0); if (mcharwrapper != null) { mcharwrapper.mchars = null; } }
公有方法,传入buffertype,查看buffertype:
/** * type of the text buffer that defines the characteristics of the text such as static, * styleable, or editable. */ public enum buffertype { normal, spannable, editable }
可以看到buffertype是枚举类型,有三种类型,spannablestring实现了spannable接口,那么这里选择spannable,尝试后还是没有span效果,又注意到settext方法中mspannablefactory.newspannable会重新生成一个spannablestring:
public spannablestring(charsequence source) { this(source, false /* ignorenocopyspan */); } public spannablestring(charsequence source, boolean ignorenocopyspan) { super(source, 0, source.length(), ignorenocopyspan); }
可以看到,默认将整个source作为一个span,这显然不是我们想要的。
重新阅读settext源码,发现:
// if suggestions are not enabled, remove the suggestion spans from the text if (!issuggestionsenabled()) { text = removesuggestionspans(text); }
如果没有开启suggestions,传递进去的text将被移除自身已有的span,看下 issuggestionsenabled()方法:
public boolean issuggestionsenabled() { if (meditor == null) return false; if ((meditor.minputtype & inputtype.type_mask_class) != inputtype.type_class_text) { return false; } if ((meditor.minputtype & inputtype.type_text_flag_no_suggestions) > 0) return false; final int variation = meditor.minputtype & editorinfo.type_mask_variation; return (variation == editorinfo.type_text_variation_normal || variation == editorinfo.type_text_variation_email_subject || variation == editorinfo.type_text_variation_long_message || variation == editorinfo.type_text_variation_short_message || variation == editorinfo.type_text_variation_web_edit_text); }
可以看到该方法的返回值都与meditor有关,再看下meditor:
/** * {@link edittext} specific data, created on demand when one of the editor fields is used. * see {@link #createeditorifneeded()}. */ private editor meditor;
meditor是特定数据,在使用编辑器字段之一时按需创建,再看下注释中meditor的创建方法:
private void createeditorifneeded() { if (meditor == null) { meditor = new editor(this); } }
啊哦,创建meditor的唯一方法是私有方法,也就是说没法通过改变issuggestionsenabled()返回值来取消移除已有的span。
回过头看spannablestring源码,发现spannablestring没有实现任何序列化接口,而我是把spannablestring作为charsequence通过intent来传递的,它将作为普通的charsequence实现类对象传递到textview.settext()中,所以,解决方法有两种:
1)在settext()需要传递spannablestring的地方,重新创建一个spannablestring;
2)重写spannablestring,继承自spannablestring并实现序列化接口,将自定义的spannablestring作为对象通过intent来传递;
总结:在android组件间进行数据传递时,如果是传递对象,通常都会考虑到数据是否实现了序列化接口,但在这种情况下,试图将spannablestring作为charsequence的实现类在组件之间进行传递,在接收端获取到的charsequence将不再是传递之前的实现类对象,同时也容易忽略掉我们真正需要的是传递一个对象,而通过intent传递对象是需要实现序列化接口的。