問題:在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傳遞對象是需要實現序列化介面的。