欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

解决SpannableString在Android组件间传递时显示失效的问题

程序员文章站 2022-10-17 23:25:53
问题:在A activity中传递一个SpannableString到B activity中,并最终传递到B activity中的TextView中,但是没有展示出Span效果。 解决:阅读TextView.setText()方法 看到会根据BufferType对传入的text重新赋值,于是回溯找到 ......

问题:在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传递对象是需要实现序列化接口的。