Refactor ellipsis attributes and methods
Mostly changed to avoid repetition of code and remove the assumptions about ellipsis being one code unit. The code for multi-code unit ellipsis is not triggered yet, but is done in preparation for potential future locale-dependent cases. Test: bit CtsTextTestCases:* Test: bit CtsWidgetTestCases:android.widget.cts.TextViewTest Test: bit CtsWidgetTestCases:android.widget.cts.EditTextTest Test: bit CtsWidgetTestCases:android.widget.cts.CheckedTextViewTest Test: bit CtsWidgetTestCases:android.widget.cts.AutoCompleteTextViewTest Test: bit CtsWidgetTestCases:android.widget.cts.MultiAutoCompleteTextViewTest Test: adb shell am instrument -w -e package android.text com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner Change-Id: Id1dfdc503f87fabed2447d55ab2107eee0eccd08
This commit is contained in:
@@ -2036,35 +2036,27 @@ public abstract class Layout {
|
||||
}
|
||||
}
|
||||
|
||||
private char getEllipsisChar(TextUtils.TruncateAt method) {
|
||||
return (method == TextUtils.TruncateAt.END_SMALL) ?
|
||||
TextUtils.ELLIPSIS_TWO_DOTS[0] :
|
||||
TextUtils.ELLIPSIS_NORMAL[0];
|
||||
}
|
||||
|
||||
private void ellipsize(int start, int end, int line,
|
||||
char[] dest, int destoff, TextUtils.TruncateAt method) {
|
||||
int ellipsisCount = getEllipsisCount(line);
|
||||
|
||||
final int ellipsisCount = getEllipsisCount(line);
|
||||
if (ellipsisCount == 0) {
|
||||
return;
|
||||
}
|
||||
final int ellipsisStart = getEllipsisStart(line);
|
||||
final int lineStart = getLineStart(line);
|
||||
|
||||
int ellipsisStart = getEllipsisStart(line);
|
||||
int linestart = getLineStart(line);
|
||||
|
||||
for (int i = ellipsisStart; i < ellipsisStart + ellipsisCount; i++) {
|
||||
char c;
|
||||
|
||||
if (i == ellipsisStart) {
|
||||
c = getEllipsisChar(method); // ellipsis
|
||||
final String ellipsisString = TextUtils.getEllipsisString(method);
|
||||
final int ellipsisStringLen = ellipsisString.length();
|
||||
for (int i = 0; i < ellipsisCount; i++) {
|
||||
final char c;
|
||||
if (i < ellipsisStringLen && ellipsisCount <= ellipsisStringLen) {
|
||||
c = ellipsisString.charAt(i);
|
||||
} else {
|
||||
c = '\uFEFF'; // 0-width space
|
||||
c = TextUtils.ELLIPSIS_FILLER;
|
||||
}
|
||||
|
||||
int a = i + linestart;
|
||||
|
||||
if (a >= start && a < end) {
|
||||
final int a = i + ellipsisStart + lineStart;
|
||||
if (start <= a && a < end) {
|
||||
dest[destoff + a - start] = c;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -781,8 +781,8 @@ public class StaticLayout extends Layout {
|
||||
&& (ellipsize == TextUtils.TruncateAt.END
|
||||
|| (mMaximumVisibleLineCount == 1
|
||||
&& ellipsize != TextUtils.TruncateAt.MARQUEE));
|
||||
if (remainingLineCount > 0 && remainingLineCount < breakCount &&
|
||||
ellipsisMayBeApplied) {
|
||||
if (0 < remainingLineCount && remainingLineCount < breakCount
|
||||
&& ellipsisMayBeApplied) {
|
||||
// Calculate width and flag.
|
||||
float width = 0;
|
||||
int flag = 0;
|
||||
@@ -1053,9 +1053,7 @@ public class StaticLayout extends Layout {
|
||||
return;
|
||||
}
|
||||
|
||||
float ellipsisWidth = paint.measureText(
|
||||
(where == TextUtils.TruncateAt.END_SMALL) ?
|
||||
TextUtils.ELLIPSIS_TWO_DOTS : TextUtils.ELLIPSIS_NORMAL, 0, 1);
|
||||
float ellipsisWidth = paint.measureText(TextUtils.getEllipsisString(where));
|
||||
int ellipsisStart = 0;
|
||||
int ellipsisCount = 0;
|
||||
int len = lineEnd - lineStart;
|
||||
|
||||
@@ -77,12 +77,21 @@ import java.util.regex.Pattern;
|
||||
public class TextUtils {
|
||||
private static final String TAG = "TextUtils";
|
||||
|
||||
/* package */ static final char[] ELLIPSIS_NORMAL = { '\u2026' }; // this is "..."
|
||||
/** {@hide} */
|
||||
public static final String ELLIPSIS_STRING = new String(ELLIPSIS_NORMAL);
|
||||
// Zero-width character used to fill ellipsized strings when codepoint lenght must be preserved.
|
||||
/* package */ static final char ELLIPSIS_FILLER = '\uFEFF'; // ZERO WIDTH NO-BREAK SPACE
|
||||
|
||||
// TODO: Based on CLDR data, these need to be localized for Dzongkha (dz) and perhaps
|
||||
// Hong Kong Traditional Chinese (zh-Hant-HK), but that may need to depend on the actual word
|
||||
// being ellipsized and not the locale.
|
||||
private static final String ELLIPSIS_NORMAL = "\u2026"; // HORIZONTAL ELLIPSIS (…)
|
||||
private static final String ELLIPSIS_TWO_DOTS = "\u2025"; // TWO DOT LEADER (‥)
|
||||
|
||||
/** {@hide} */
|
||||
@NonNull
|
||||
public static String getEllipsisString(@NonNull TextUtils.TruncateAt method) {
|
||||
return (method == TextUtils.TruncateAt.END_SMALL) ? ELLIPSIS_TWO_DOTS : ELLIPSIS_NORMAL;
|
||||
}
|
||||
|
||||
/* package */ static final char[] ELLIPSIS_TWO_DOTS = { '\u2025' }; // this is ".."
|
||||
private static final String ELLIPSIS_TWO_DOTS_STRING = new String(ELLIPSIS_TWO_DOTS);
|
||||
|
||||
private TextUtils() { /* cannot be instantiated */ }
|
||||
|
||||
@@ -1190,10 +1199,10 @@ public class TextUtils {
|
||||
TextPaint paint,
|
||||
float avail, TruncateAt where,
|
||||
boolean preserveLength,
|
||||
EllipsizeCallback callback) {
|
||||
@Nullable EllipsizeCallback callback) {
|
||||
return ellipsize(text, paint, avail, where, preserveLength, callback,
|
||||
TextDirectionHeuristics.FIRSTSTRONG_LTR,
|
||||
(where == TruncateAt.END_SMALL) ? ELLIPSIS_TWO_DOTS_STRING : ELLIPSIS_STRING);
|
||||
getEllipsisString(where));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1213,7 +1222,7 @@ public class TextUtils {
|
||||
TextPaint paint,
|
||||
float avail, TruncateAt where,
|
||||
boolean preserveLength,
|
||||
EllipsizeCallback callback,
|
||||
@Nullable EllipsizeCallback callback,
|
||||
TextDirectionHeuristic textDir, String ellipsis) {
|
||||
|
||||
int len = text.length();
|
||||
@@ -1256,13 +1265,15 @@ public class TextUtils {
|
||||
char[] buf = mt.mChars;
|
||||
Spanned sp = text instanceof Spanned ? (Spanned) text : null;
|
||||
|
||||
int remaining = len - (right - left);
|
||||
final int removed = right - left;
|
||||
final int remaining = len - removed;
|
||||
if (preserveLength) {
|
||||
if (remaining > 0) { // else eliminate the ellipsis too
|
||||
buf[left++] = ellipsis.charAt(0);
|
||||
}
|
||||
if (remaining > 0 && removed >= ellipsis.length()) {
|
||||
ellipsis.getChars(0, ellipsis.length(), buf, left);
|
||||
left += ellipsis.length();
|
||||
} // else skip the ellipsis
|
||||
for (int i = left; i < right; i++) {
|
||||
buf[i] = ZWNBS_CHAR;
|
||||
buf[i] = ELLIPSIS_FILLER;
|
||||
}
|
||||
String s = new String(buf, 0, len);
|
||||
if (sp == null) {
|
||||
@@ -1362,7 +1373,7 @@ public class TextUtils {
|
||||
final int remainingElements = totalLen - i - 1;
|
||||
if (remainingElements > 0) {
|
||||
CharSequence morePiece = (res == null) ?
|
||||
ELLIPSIS_STRING :
|
||||
ELLIPSIS_NORMAL :
|
||||
res.getQuantityString(moreId, remainingElements, remainingElements);
|
||||
morePiece = bidiFormatter.unicodeWrap(morePiece);
|
||||
output.append(morePiece);
|
||||
@@ -2029,6 +2040,4 @@ public class TextUtils {
|
||||
private static char[] sTemp = null;
|
||||
|
||||
private static String[] EMPTY_STRING_ARRAY = new String[]{};
|
||||
|
||||
private static final char ZWNBS_CHAR = '\uFEFF';
|
||||
}
|
||||
|
||||
@@ -181,7 +181,7 @@ public class LocaleHelper {
|
||||
// Hong Kong Traditional Chinese (zh_Hant_HK) and Dzongkha (dz). But that has two
|
||||
// problems: it's expensive to extract it, and in case the output string becomes
|
||||
// automatically ellipsized, it can result in weird output.
|
||||
localeNames[maxLocales] = TextUtils.ELLIPSIS_STRING;
|
||||
localeNames[maxLocales] = TextUtils.getEllipsisString(TextUtils.TruncateAt.END);
|
||||
}
|
||||
|
||||
ListFormatter lfn = ListFormatter.getInstance(dispLocale);
|
||||
|
||||
@@ -340,6 +340,66 @@ public class TextUtilsTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEllipsize_multiCodepoint() {
|
||||
final TextPaint paint = new TextPaint();
|
||||
final float wordWidth = paint.measureText("MMMM");
|
||||
|
||||
// Establish the ground rules first, for single-codepoint cases.
|
||||
final String ellipsis = "."; // one full stop character
|
||||
assertEquals(
|
||||
"MM.\uFEFF",
|
||||
TextUtils.ellipsize("MMMM", paint, 0.7f * wordWidth,
|
||||
TextUtils.TruncateAt.END, true /* preserve length */,
|
||||
null /* no callback */, TextDirectionHeuristics.LTR,
|
||||
ellipsis));
|
||||
assertEquals(
|
||||
"MM.",
|
||||
TextUtils.ellipsize("MMMM", paint, 0.7f * wordWidth,
|
||||
TextUtils.TruncateAt.END, false /* preserve length */,
|
||||
null /* no callback */, TextDirectionHeuristics.LTR,
|
||||
ellipsis));
|
||||
assertEquals(
|
||||
"M.",
|
||||
TextUtils.ellipsize("MM", paint, 0.45f * wordWidth,
|
||||
TextUtils.TruncateAt.END, true /* preserve length */,
|
||||
null /* no callback */, TextDirectionHeuristics.LTR,
|
||||
ellipsis));
|
||||
assertEquals(
|
||||
"M.",
|
||||
TextUtils.ellipsize("MM", paint, 0.45f * wordWidth,
|
||||
TextUtils.TruncateAt.END, false /* preserve length */,
|
||||
null /* no callback */, TextDirectionHeuristics.LTR,
|
||||
ellipsis));
|
||||
|
||||
// Now check the differences for multi-codepoint ellipsis.
|
||||
final String longEllipsis = ".."; // two full stop characters
|
||||
assertEquals(
|
||||
"MM..",
|
||||
TextUtils.ellipsize("MMMM", paint, 0.7f * wordWidth,
|
||||
TextUtils.TruncateAt.END, true /* preserve length */,
|
||||
null /* no callback */, TextDirectionHeuristics.LTR,
|
||||
longEllipsis));
|
||||
assertEquals(
|
||||
"MM..",
|
||||
TextUtils.ellipsize("MMMM", paint, 0.7f * wordWidth,
|
||||
TextUtils.TruncateAt.END, false /* preserve length */,
|
||||
null /* no callback */, TextDirectionHeuristics.LTR,
|
||||
longEllipsis));
|
||||
assertEquals(
|
||||
"M\uFEFF",
|
||||
TextUtils.ellipsize("MM", paint, 0.45f * wordWidth,
|
||||
TextUtils.TruncateAt.END, true /* preserve length */,
|
||||
null /* no callback */, TextDirectionHeuristics.LTR,
|
||||
longEllipsis));
|
||||
assertEquals(
|
||||
"M..",
|
||||
TextUtils.ellipsize("MM", paint, 0.45f * wordWidth,
|
||||
TextUtils.TruncateAt.END, false /* preserve length */,
|
||||
null /* no callback */, TextDirectionHeuristics.LTR,
|
||||
longEllipsis));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDelimitedStringContains() {
|
||||
assertFalse(TextUtils.delimitedStringContains("", ',', null));
|
||||
|
||||
Reference in New Issue
Block a user