很久以前做的表情输入及显示,用的系统的SpannableString,以前都好端端的没问题,最近突然报出个棘手的bug,在4.1和4.1.1的手机上显示某位用户的评论时,程序直接挂掉,
异常信息:
9-17 16:38:27.429: E/AndroidRuntime(10425): FATAL EXCEPTION: main 09-17 16:38:27.429: E/AndroidRuntime(10425): java.lang.ArrayIndexOutOfBoundsException: length=116; index=125 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.text.MeasuredText.addStyleRun(MeasuredText.java:168) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.text.MeasuredText.addStyleRun(MeasuredText.java:204) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.text.StaticLayout.generate(StaticLayout.java:297) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.text.StaticLayout.<init>(StaticLayout.java:156) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.text.StaticLayout.<init>(StaticLayout.java:96) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.text.StaticLayout.<init>(StaticLayout.java:75) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.widget.TextView.makeSingleLayout(TextView.java:5942) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.widget.TextView.makeNewLayout(TextView.java:5782) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.widget.TextView.onMeasure(TextView.java:6139) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.view.View.measure(View.java:15264) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:4918) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1390) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.widget.LinearLayout.measureVertical(LinearLayout.java:681) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.widget.LinearLayout.onMeasure(LinearLayout.java:574) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.view.View.measure(View.java:15264) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:4918) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.widget.FrameLayout.onMeasure(FrameLayout.java:310) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.view.View.measure(View.java:15264) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.widget.ListView.setupChild(ListView.java:1893) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.widget.ListView.makeAndAddView(ListView.java:1803) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.widget.ListView.fillDown(ListView.java:681) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.widget.ListView.fillFromTop(ListView.java:742) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.widget.ListView.layoutChildren(ListView.java:1629) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.widget.AbsListView.onLayout(AbsListView.java:2224) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.view.View.layout(View.java:13846) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.view.ViewGroup.layout(ViewGroup.java:4466) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1649) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1507) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.widget.LinearLayout.onLayout(LinearLayout.java:1420) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.view.View.layout(View.java:13846) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.view.ViewGroup.layout(ViewGroup.java:4466) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1649) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1507) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.widget.LinearLayout.onLayout(LinearLayout.java:1420) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.view.View.layout(View.java:13846) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.view.ViewGroup.layout(ViewGroup.java:4466) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.widget.FrameLayout.onLayout(FrameLayout.java:448) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.view.View.layout(View.java:13846) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.view.ViewGroup.layout(ViewGroup.java:4466) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1649) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1507) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.widget.LinearLayout.onLayout(LinearLayout.java:1420) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.view.View.layout(View.java:13846) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.view.ViewGroup.layout(ViewGroup.java:4466) 09-17 16:38:27.429: E/AndroidRuntime(10425): at com.component.SinglePreviewContainer.onLayout(SinglePreviewContainer.java:124) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.view.View.layout(View.java:13846) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.view.ViewGroup.layout(ViewGroup.java:4466) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1649) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1507) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.widget.LinearLayout.onLayout(LinearLayout.java:1420) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.view.View.layout(View.java:13846) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.view.ViewGroup.layout(ViewGroup.java:4466) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1649) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.widget.LinearLayout.layoutHorizontal(LinearLayout.java:1638) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.widget.LinearLayout.onLayout(LinearLayout.java:1422) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.view.View.layout(View.java:13846) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.view.ViewGroup.layout(ViewGroup.java:4466) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.widget.RelativeLayout.onLayout(RelativeLayout.java:948) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.view.View.layout(View.java:13846) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.view.ViewGroup.layout(ViewGroup.java:4466) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.widget.FrameLayout.onLayout(FrameLayout.java:448) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.view.View.layout(View.java:13846) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.view.ViewGroup.layout(ViewGroup.java:4466) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1649) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1507) 09-17 16:38:27.429: E/AndroidRuntime(10425): at android.widget.Linear
原因:当TextView显示的表情恰好要被换行符截断的时候,会报异常(目前只在4.1和4.1.1上出现)
解决方式:
重写TextView 在onMeasure()中捕获异常
public class PatchedTextView extends TextView { public PatchedTextView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public PatchedTextView(Context context, AttributeSet attrs) { super(context, attrs); } public PatchedTextView(Context context) { super(context); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { try{ super.onMeasure(widthMeasureSpec, heightMeasureSpec); }catch (ArrayIndexOutOfBoundsException e){ setText(getText().toString()); super.onMeasure(widthMeasureSpec, heightMeasureSpec); } } @Override public void setGravity(int gravity){ try{ super.setGravity(gravity); }catch (ArrayIndexOutOfBoundsException e){ setText(getText().toString()); super.setGravity(gravity); } } @Override public void setText(CharSequence text, BufferType type) { try{ super.setText(text, type); }catch (ArrayIndexOutOfBoundsException e){ setText(text.toString()); } } }
引用牛人分析:
Here's some more details on the bug and how to work around it. I've attempted accuracy but please call out any mistakes. Crash Manifestation: When Android is rendering long lines of text to the screen, it needs to figure out where to perform a line-wrap. A crash will occur if all of the following are true: 1. The text contains spans that are MetricAffectingSpans (ex: StyleSpan is one such subclass). Other types of spans do not invoke the crash (ex: URLSpan). 2. There is a word where a MetricAffectingSpans starts or stops in the middle of that word (ex: the first half of a word is bolded while the second half is not). 3. A line-wrap is needed in the non-spanned part of the word. 4. The code is running on Android 4.1 or 4.1.1. What makes this bug show up on some devices and not others depends on several factors such as screen pixel dimensions, dpi, font size, the text and spans themselves, and others. This shows up both in custom layouts and in simple dialogs (ex: dialog's built by AlertDialogBuilder). So, what exactly is meant by a "word" in this case? A word is a sequence of characters that can not be split across a line-wrap. So then, where can line-wraps occurs? It has already been pointed out that spaces allow line-wraps to occur. However, there are other characters that also allow line-wraps. 1. Spaces, Tabs, and Newline characters always allow line-wraps (i.e. ' ', '\t', and '\n'). 2. The characters '.' ',' ';' and ':' allow line-wraps IF there is not a digit immedidately before or after them (as defined by Character.isDigit()). When these 4 characters separate two words, these characters are considered part of the first of the two words. 3. The characters '/' and '-' allow line-wraps IF there is not a digit immediately after them (as defined by Character.isDigit()). As above, when these 2 characters separate two words, these characters are considered part of the first of the two words. 4. Ideographs allow line-wraps if they are adjacent, except for non-starters while only wrap after the non-starter. See Android's 4.1.1 StaticLayout.java file for details. Here are some example strings that were put through textView.setText( Html.fromHtml(TEXT) ): "hello <b>world</b>" Safe "hello.<b>world</b>" Safe "hello.<b>1orld</b>" May crash "hell7.<b>world</b>" May crash "hello-<b>world</b>" Safe "hello-<b>1orld</b>" May crash "<b>hello</b> world" Safe "<b>hello.</b>world" Safe "<b>hello</b>.world" May crash "<b>hell7.</b>world" May crash "<b>hello.</b>1orld" May crash "<b>hello-</b>world" Safe "<b>hello</b>-world" May crash "<b>hell7-</b>world" Safe "<b>hello-</b>1orld" May crash The crashes are limited to any span that is or derived from MetricAffectingSpans. This means that the following spans are SAFE to use because they are not derived from MetricAffectingSpans: MaskFilterSpan, RasterizerSpan, clickableSpan, URLSpan, BackgroundColorSpan, ForegroundColorSpan, StrikethroughSpan, SuggestionSpan, UnderlineSpan There are two ways for detecting if the crash can even occur: 1. Look for Build.VERSION to be equal to "4.1" or "4.1.1". This check is easy, but your code might implement a work around on such device's where the crash wouldn't show up (ex: screen size meant that line-wraps didn't occur in any of the bad places). 2. Wrap the call to setText() for the View with a try{ ... } catch( IndexOutOfBoundsException e ){ ... }. Do this either around the call to setText or create a subclass that overrides setText() and calls super.setText(). Work-arounds if detected (from simplist to complex): 0. If you have complete control over the text, re-write the text to fit the "characters that allow line-wraps" rules listed above. 1. Remove all the spans from the text (ex: text.toString()). This is easy but it removes ALL spans. 2. Remove just the MetricAffectingSpans. Call text.getSpans(0, text.length(), MetricAffectingSpans.class) and then text.removeSpan() for each one returned. 3. Find all the MetricAffectingSpans and detect whether each span has a space/tab/newline before and after them. If not, then insert a space/tab/newline before and/or after each span. 4. Find all the MetricAffectingSpans and do the following: If the span is a StyleSpan for bold, replace the span with a MaskFilterSpan( new BlurMaskFilter((float) 0.5, BlurMaskFilter.Blur.SOLID ) ) If the span is a StyleSpan for italic, make the so-so replacement with ForegroundColorSpan(0xFF808080) If the span is something else, remove the span entirely. 5. Find all the MetricAffectingSpans and detect whether each span allows a line-wrap immediately before and after them according to the "characters that allow line-wraps" rules listed above. Modify the string accordingly. 6. Other work-arounds are possible.
链接 https://code.google.com/p/android/issues/detail?id=35466
相关推荐
android TextView android TextView
android TextView 支持CSS样式
一个自定义的android的翻转textview,效果很不错的
最近在项目中使用textview时发现在使用Android:layout_height=”wrap_content”这个属性设置后,textview会有默认的padding,也就是fontpadding。这样就会造成textview和其他view中间的间距会比自己的设置的大。那么...
Android的TextView中显示图片和图像的方法
本文实例讲述了Android TextView实现垂直滚动效果的方法。分享给大家供大家参考,具体如下: 在TextView中,如果文本很长,可能需要实现垂直滚动显示文本的效果。这里需要在XML布局文件中为TextView设置如下几个属性...
android实现TextView文字部分渐变色;LinearGradientFontSpan SpannableString
我们在浏览网页时,网页的文本可以放大缩小,android两点手势,两点距离靠近时缩小,两点距离远离时候是放大。那么若果不用android的WebView控件,单纯的TextView能否做到放大缩小呢,其实也是可以的。只要响应和...
有时候看到原型图上要求图片和文字对齐,但是直接使用TextView的话总是有一段空白,这里直接继承TextView重写onDraw方法去掉上下边距,但是简单粗暴,导致只能显示文字,没有TextView其他的功能了。
在Android中用textview实现简单的小说分页,字体可调节,未对字符串进行任何操作,分段和控制格式自行对字符串操作
TagTextView:包含标签的TextView
这是参考别人的显示gif的textview而写出来的,可同时显示gif和png还有文字,类似于QQ聊天时输入动态表情, 不足的是,由于各gif的频率不一致,在输入多个不同gif时,有些gif可能看起来会慢一些 参考:...
这是一个关于TextView上下滚动的效果,详情请前往博客浏览http://blog.csdn.net/u014452224/article/details/53101507
Android中TextView的xml属性介绍,水平有限,仅供参考
Android 为textView增加边框效果,这又是一个初级的Android文字布局示例,今天是为TextView文字框增加一个外边框,可实现在画布上画边框的功能,在填充方面,你还可使用不透明的9.png和透明的9.png,带边框的...
通过textview设置状态选择
解决TextView的排版问题-自动换行
支持TextView默认支持的标签 支持自定义标签,接口类似Html.TagHandler 1 自定义标签: /** * 解析哈哈哈 */ public class SpanTagHandler implements HtmlTagHandler.TagHandler { private String fontColor =...
android TextView解决自动换行问题,设置行数后超过用省略号