这个问题是由于系统原因引发,目前发现在android4.1、4.1.1的系统版本上,测试机器Samsung galaxy note 2(模拟器)。
目前网上讨论的方案大多是直接捕捉异常重新设置setText(text.toString());
代码如下:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
try {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
} catch (IndexOutOfBoundsException e) {
setText(getText().toString());
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
@Override
public void setGravity(int gravity) {
try {
super.setGravity(gravity);
} catch (IndexOutOfBoundsException e) {
setText(getText().toString());
super.setGravity(gravity);
}
}
@Override
public void setText(CharSequence text, BufferType type) {
try {
super.setText(text, type);
} catch (IndexOutOfBoundsException e) {
setText(text.toString());
}
}
}
这种方式可以解决崩溃的问题,但是imagespan却展示不出来。
第二种方案:删除引发问题的span。
代码如下:
@Override
public void setText(CharSequence text, BufferType type) {
try {
super.setText(text, type);
} catch (IndexOutOfBoundsException e) {
if (text instanceof Spanned) {
Spanned spanned = (Spanned) text;
SpannableStringBuilder builder = new SpannableStringBuilder(spanned);
MetricAffectingSpan[] spans = spanned.getSpans(0, spanned.length(), MetricAffectingSpan.class);
if (spans != null && spans.length > 0) {
for (MetricAffectingSpan span : spans) {
if (span instanceof ImageSpan) {
//do nothing
} else {
builder.removeSpan(span);
}
}
}
super.setText(builder, type);
}
}
}
这种方案可以防止崩溃,而且可以使imagespan继续展示,只是删除了其他span。
第三种方案:在引发问题span的前后加入空格符。
代码如下:
@Override
public void setText(CharSequence text, BufferType type) {
try {
super.setText(text, type);
} catch (IndexOutOfBoundsException e) {
if (text instanceof Spanned) {
Spanned spanned = (Spanned) text;
SpannableStringBuilder builder = new SpannableStringBuilder(spanned);
MetricAffectingSpan[] spans = spanned.getSpans(0, spanned.length(), MetricAffectingSpan.class);
if (spans != null && spans.length > 0) {
for (MetricAffectingSpan span : spans) {
int spanStart = builder.getSpanStart(span);
if (isNotSpace(builder, spanStart - 1)) {
builder.insert(spanStart, " ");
}
int spanEnd = builder.getSpanEnd(span);
if (isNotSpace(builder, spanEnd)) {
builder.insert(spanEnd, " ");
}
}
}
super.setText(builder, type);
}
}
}
private boolean isNotSpace(CharSequence text, int where) {
return where < 0 || where >= text.length() || text.charAt(where) != ' ';
}
这种方案相对于1,2相对完美,并不会去除span,内容风格可以保留。
在此感谢国外大神提供思路(issues 35466的第25楼):
https://code.google.com/p/android/issues/detail?id=35466
不能完全解决问题的同学可以参考该issues 上的其他解决方案。