From 74d31ef2b2c42b54fa1f7cf94ea955ea67ab69a0 Mon Sep 17 00:00:00 2001 From: Eric Fischer Date: Thu, 5 Aug 2010 15:29:36 -0700 Subject: [PATCH] Fix a bug where paragraph styles could leak onto an adjacent empty line. When the text ends with an empty line, the getSpans() call to retrieve styles for it would also retrieve the styles of the preceding line. Add a special case to detect and prevent this. Change-Id: I888131cacce6bf45e68c53c931ebe8d58db0b7a9 --- core/java/android/text/Layout.java | 50 +++++++++++++++++++----- core/java/android/text/StaticLayout.java | 6 +-- 2 files changed, 44 insertions(+), 12 deletions(-) diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index f533944ed60c0..0466c6993a7ed 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -234,7 +234,7 @@ public abstract class Layout { LineBackgroundSpan.class); // All LineBackgroundSpans on a line contribute to its // background. - spans = sp.getSpans(start, end, LineBackgroundSpan.class); + spans = getParagraphSpans(sp, start, end, LineBackgroundSpan.class); } for (int n = 0; n < spans.length; n++) { @@ -309,7 +309,7 @@ public abstract class Layout { if (start >= spanEnd && (i == first || isFirstParaLine)) { spanEnd = sp.nextSpanTransition(start, textLength, ParagraphStyle.class); - spans = sp.getSpans(start, spanEnd, ParagraphStyle.class); + spans = getParagraphSpans(sp, start, spanEnd, ParagraphStyle.class); align = mAlignment; for (int n = spans.length-1; n >= 0; n--) { @@ -425,7 +425,7 @@ public abstract class Layout { int start = getLineStart(line); int spanEnd = spanned.nextSpanTransition(start, spanned.length(), TabStopSpan.class); - TabStopSpan[] tabSpans = spanned.getSpans(start, spanEnd, TabStopSpan.class); + TabStopSpan[] tabSpans = getParagraphSpans(spanned, start, spanEnd, TabStopSpan.class); if (tabSpans.length > 0) { tabStops = new TabStops(TAB_INCREMENT, tabSpans); } @@ -713,7 +713,7 @@ public abstract class Layout { if (hasTabOrEmoji && mText instanceof Spanned) { // Just checking this line should be good enough, tabs should be // consistent across all lines in a paragraph. - TabStopSpan[] tabs = ((Spanned) mText).getSpans(start, end, TabStopSpan.class); + TabStopSpan[] tabs = getParagraphSpans((Spanned) mText, start, end, TabStopSpan.class); if (tabs.length > 0) { tabStops = new TabStops(TAB_INCREMENT, tabs); // XXX should reuse } @@ -820,7 +820,7 @@ public abstract class Layout { if (hasTabsOrEmoji && mText instanceof Spanned) { // Just checking this line should be good enough, tabs should be // consistent across all lines in a paragraph. - TabStopSpan[] tabs = ((Spanned) mText).getSpans(start, end, TabStopSpan.class); + TabStopSpan[] tabs = getParagraphSpans((Spanned) mText, start, end, TabStopSpan.class); if (tabs.length > 0) { tabStops = new TabStops(TAB_INCREMENT, tabs); // XXX should reuse } @@ -1308,7 +1308,7 @@ public abstract class Layout { if (mSpannedText) { Spanned sp = (Spanned) mText; - AlignmentSpan[] spans = sp.getSpans(getLineStart(line), + AlignmentSpan[] spans = getParagraphSpans(sp, getLineStart(line), getLineEnd(line), AlignmentSpan.class); @@ -1361,7 +1361,7 @@ public abstract class Layout { int lineEnd = getLineEnd(line); int spanEnd = spanned.nextSpanTransition(lineStart, lineEnd, LeadingMarginSpan.class); - LeadingMarginSpan[] spans = spanned.getSpans(lineStart, spanEnd, + LeadingMarginSpan[] spans = getParagraphSpans(spanned, lineStart, spanEnd, LeadingMarginSpan.class); if (spans.length == 0) { return 0; // no leading margin span; @@ -1416,7 +1416,7 @@ public abstract class Layout { Spanned spanned = (Spanned) text; int spanEnd = spanned.nextSpanTransition(start, end, TabStopSpan.class); - TabStopSpan[] spans = spanned.getSpans(start, spanEnd, + TabStopSpan[] spans = getParagraphSpans(spanned, start, spanEnd, TabStopSpan.class); if (spans.length > 0) { tabStops = new TabStops(TAB_INCREMENT, spans); @@ -1513,7 +1513,7 @@ public abstract class Layout { if (text instanceof Spanned) { if (tabs == null) { - tabs = ((Spanned) text).getSpans(start, end, TabStopSpan.class); + tabs = getParagraphSpans((Spanned) text, start, end, TabStopSpan.class); alltabs = true; } @@ -1540,6 +1540,38 @@ public abstract class Layout { return mSpannedText; } + /** + * Returns the same as text.getSpans(), except where + * start and end are the same and are not + * at the very beginning of the text, in which case an empty array + * is returned instead. + *

+ * This is needed because of the special case that getSpans() + * on an empty range returns the spans adjacent to that range, which is + * primarily for the sake of TextWatchers so they will get + * notifications when text goes from empty to non-empty. But it also + * has the unfortunate side effect that if the text ends with an empty + * paragraph, that paragraph accidentally picks up the styles of the + * preceding paragraph (even though those styles will not be picked up + * by new text that is inserted into the empty paragraph). + *

+ * The reason it just checks whether start and end + * is the same is that the only time a line can contain 0 characters + * is if it is the final paragraph of the Layout; otherwise any line will + * contain at least one printing or newline character. The reason for the + * additional check if start is greater than 0 is that + * if the empty paragraph is the entire content of the buffer, paragraph + * styles that are already applied to the buffer will apply to text that + * is inserted into it. + */ + /* package */ static T[] getParagraphSpans(Spanned text, int start, int end, Class type) { + if (start == end && start > 0) { + return (T[]) ArrayUtils.emptyArray(type); + } + + return text.getSpans(start, end, type); + } + private void ellipsize(int start, int end, int line, char[] dest, int destoff) { int ellipsisCount = getEllipsisCount(line); diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index 44157de670f91..cc969cb9f50c7 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -155,7 +155,7 @@ extends Layout LineHeightSpan[] chooseht = null; if (spanned != null) { - LeadingMarginSpan[] sp = spanned.getSpans(paraStart, paraEnd, + LeadingMarginSpan[] sp = getParagraphSpans(spanned, paraStart, paraEnd, LeadingMarginSpan.class); for (int i = 0; i < sp.length; i++) { LeadingMarginSpan lms = sp[i]; @@ -174,7 +174,7 @@ extends Layout } } - chooseht = spanned.getSpans(paraStart, paraEnd, LineHeightSpan.class); + chooseht = getParagraphSpans(spanned, paraStart, paraEnd, LineHeightSpan.class); if (chooseht.length != 0) { if (choosehtv == null || @@ -267,7 +267,7 @@ extends Layout hasTabOrEmoji = true; if (spanned != null) { // First tab this para, check for tabstops - TabStopSpan[] spans = spanned.getSpans(paraStart, + TabStopSpan[] spans = getParagraphSpans(spanned, paraStart, paraEnd, TabStopSpan.class); if (spans.length > 0) { tabStops = new TabStops(TAB_INCREMENT, spans);