很久以前做的表情输入及显示,用的系统的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是用于显示单行或多行文本的视图组件,它在界面设计中扮演着重要的角色。为了使TextView更具交互性,我们常常需要根据用户操作或特定条件来改变其显示状态,比如选中、聚焦或者按下等。在...
在Android开发中,实现TextView的上下滚动效果可以让信息在有限的空间内持续展示,提升用户体验。以下将详细讲解如何实现这一功能。 首先,我们要明白TextView是Android SDK中的一个基础组件,用于显示单行或多行...
在Android开发中,`TextView` 是一个非常基础且重要的组件,用于显示文本。然而,在实际应用中,我们可能会遇到`TextView`默认带有上下边距的问题,这可能不符合我们的设计需求或者影响界面布局的美观性。标题"安卓...
本文将深入探讨如何使用TextView在Android环境中实现这一功能,同时考虑到字体大小的调整以及字符串处理的灵活性。 首先,TextView是Android SDK中一个核心组件,它用于显示单行或多行文本。在我们的场景中,...
在Android应用开发中,文本输入和显示是常见且重要的任务之一。`TextView`作为标准的UI组件,通常用于展示单行或多行文本。然而,当文本内容过长时,直接展示全部可能会占据大量屏幕空间,影响用户体验。为了解决这...
在Android开发中,当需要在一个TextView中展示大量文本,而屏幕空间有限时,可以使用TextView的垂直滚动功能来实现文本的连续显示。本篇文章将详细讲解如何在Android中实现TextView的垂直滚动效果。 首先,要开启...
在这个示例中,我们使用了`ScrollView`作为外部容器,并设置了`android:fadeScrollbars="false"`和`android:scrollbarAlwaysDrawVerticalTrack="true"`来保证滚动条始终可见。内部使用了`LinearLayout`和`TextView`...
在Android开发过程中,为了增强界面的表现力和可维护性,开发者常常需要利用到HTML和CSS的技术来丰富`TextView`的内容展示。虽然原生的Android SDK并没有直接支持完整的CSS语法,但通过`TextView`的`setText`方法...
在Android开发中,自定义视图是提升用户体验和实现独特设计的重要手段。本文将深入探讨如何创建一个自定义的翻转TextView,名为AnimTextView,它能实现文本的动态翻转效果,为用户界面增添生动性。 首先,让我们...
通过以上分析,我们可以了解到如何在Android中自定义TextView以取消自动换行,并利用自定义属性来灵活控制这一行为。这不仅有助于实现独特的文本显示效果,还能提高代码的可复用性和可扩展性。在实际开发中,根据...
在Android开发中,`TextView` 是一个非常常用的控件,用于显示单行或多行文本。在某些场景下,我们可能希望限制`TextView`的显示行数,并在超出时使用省略号来表示文本被截断。这就涉及到`TextView`的自动换行与截断...
最近在项目中使用textview时发现在使用Android:layout_height=”wrap_content”这个属性设置后,textview会有默认的padding,也就是fontpadding。这样就会造成textview和其他view中间的间距会比自己的设置的大。那么...
在Android开发中,`TextView`是用于展示文本的常用组件,它可以显示单行或多行文本。在特定场景下,为了优化用户体验,我们可能希望只显示文本的一部分,如果文本内容超出了预设的显示范围,就用"点击查看更多"这样...
本文将详细介绍如何在Android中为TextView中的文本设置划线效果。 #### 一、理解TextView与Paint对象 在深入探讨之前,我们先来了解一下TextView的基本概念以及它与Paint对象的关系。TextView是Android中最常用的...
本文将详细介绍如何在Android的TextView中显示图片和图像。 首先,我们要理解Android系统提供的一些基本资源类型,包括Drawable资源,这是用来表示图像的主要方式。在Android工程中,图片通常存储在res/drawable...
在Android原生的TextView中,仅支持有限的HTML标签,如`<b>`(粗体)、`<i>`(斜体)和`<u>`(下划线),但无法直接处理复杂的HTML内容,如图像、链接或自定义样式。这就导致了开发者在需要展示富文本内容时面临困难...
在Android开发中,`TextView`是用于展示文本的视图组件,它提供了丰富的特性来定制文本的显示方式。本文将详细介绍`TextView`的一些关键XML属性,帮助开发者更好地理解和使用这个组件。 1. `android:autoLink` `...
在Android开发中,TextView是用于显示单行或多行文本的常用控件。然而,当文本内容过长时,为了适应屏幕大小,我们需要采取措施来处理这些超长的文本。本篇将详细介绍如何在TextView中实现文字过长时的省略显示和...
在Android应用开发中,文本输入和显示是界面设计的基础元素之一。`TagTextView`是一个专为Android平台设计的自定义视图,它扩展了标准的`TextView`类,增加了显示多个标签的功能。这个组件通常用于在界面上展示一组...
在Android开发中,有时我们需要在TextView中展示富文本内容,比如HTML格式的文本,这能够增强界面的表现力,包括插入图片、设置字体样式等。这个"Android TextView显示html的demo"就是专门为了实现这一功能而设计的...