Revert "Make ellipsize retry if text doesn't fit"
This reverts commit e88b5df5b7.
Test: bit FrameworksCoreTests:android.text.StaticLayoutTest
Test: bit FrameworksCoreTests:android.text.DynamicLayoutTest
Test: bit FrameworksCoreTests:android.text.TextUtilsTest
Test: bit CtsTextTestCases:*
Test: atest FilesActivityUiTest
Bug: 71599479
Bug: 31537595
Bug: 64156587
Change-Id: I1afa77a4b90b9b822b409a3f4721c45d0c0a6664
This commit is contained in:
@@ -928,8 +928,6 @@ public class StaticLayout extends Layout {
|
||||
}
|
||||
}
|
||||
|
||||
// The parameters that are not changed in the method are marked as final to make the code
|
||||
// easier to understand.
|
||||
private int out(final CharSequence text, final int start, final int end, int above, int below,
|
||||
int top, int bottom, int v, final float spacingmult, final float spacingadd,
|
||||
final LineHeightSpan[] chooseHt, final int[] chooseHtv, final Paint.FontMetricsInt fm,
|
||||
@@ -958,21 +956,29 @@ public class StaticLayout extends Layout {
|
||||
mLineDirections = grow;
|
||||
}
|
||||
|
||||
lines[off + START] = start;
|
||||
lines[off + TOP] = v;
|
||||
if (chooseHt != null) {
|
||||
fm.ascent = above;
|
||||
fm.descent = below;
|
||||
fm.top = top;
|
||||
fm.bottom = bottom;
|
||||
|
||||
// Information about hyphenation, tabs, and directions are needed for determining
|
||||
// ellipsization, so the values should be assigned before ellipsization.
|
||||
for (int i = 0; i < chooseHt.length; i++) {
|
||||
if (chooseHt[i] instanceof LineHeightSpan.WithDensity) {
|
||||
((LineHeightSpan.WithDensity) chooseHt[i])
|
||||
.chooseHeight(text, start, end, chooseHtv[i], v, fm, paint);
|
||||
} else {
|
||||
chooseHt[i].chooseHeight(text, start, end, chooseHtv[i], v, fm);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: could move TAB to share same column as HYPHEN, simplifying this code and gaining
|
||||
// one bit for start field
|
||||
lines[off + TAB] |= flags & TAB_MASK;
|
||||
lines[off + HYPHEN] = flags;
|
||||
lines[off + DIR] |= dir << DIR_SHIFT;
|
||||
mLineDirections[j] = measured.getDirections(start - widthStart, end - widthStart);
|
||||
above = fm.ascent;
|
||||
below = fm.descent;
|
||||
top = fm.top;
|
||||
bottom = fm.bottom;
|
||||
}
|
||||
|
||||
final boolean firstLine = (j == 0);
|
||||
final boolean currentLineIsTheLastVisibleOne = (j + 1 == mMaximumVisibleLineCount);
|
||||
boolean firstLine = (j == 0);
|
||||
boolean currentLineIsTheLastVisibleOne = (j + 1 == mMaximumVisibleLineCount);
|
||||
|
||||
if (ellipsize != null) {
|
||||
// If there is only one line, then do any type of ellipsis except when it is MARQUEE
|
||||
@@ -985,9 +991,9 @@ public class StaticLayout extends Layout {
|
||||
(!firstLine && (currentLineIsTheLastVisibleOne || !moreChars) &&
|
||||
ellipsize == TextUtils.TruncateAt.END);
|
||||
if (doEllipsis) {
|
||||
calculateEllipsis(text, start, end, widths, widthStart,
|
||||
ellipsisWidth - getTotalInsets(j), ellipsize, j,
|
||||
textWidth, paint, forceEllipsis, dir);
|
||||
calculateEllipsis(start, end, widths, widthStart,
|
||||
ellipsisWidth, ellipsize, j,
|
||||
textWidth, paint, forceEllipsis);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1006,28 +1012,6 @@ public class StaticLayout extends Layout {
|
||||
}
|
||||
}
|
||||
|
||||
if (chooseHt != null) {
|
||||
fm.ascent = above;
|
||||
fm.descent = below;
|
||||
fm.top = top;
|
||||
fm.bottom = bottom;
|
||||
|
||||
for (int i = 0; i < chooseHt.length; i++) {
|
||||
if (chooseHt[i] instanceof LineHeightSpan.WithDensity) {
|
||||
((LineHeightSpan.WithDensity) chooseHt[i])
|
||||
.chooseHeight(text, start, end, chooseHtv[i], v, fm, paint);
|
||||
|
||||
} else {
|
||||
chooseHt[i].chooseHeight(text, start, end, chooseHtv[i], v, fm);
|
||||
}
|
||||
}
|
||||
|
||||
above = fm.ascent;
|
||||
below = fm.descent;
|
||||
top = fm.top;
|
||||
bottom = fm.bottom;
|
||||
}
|
||||
|
||||
if (firstLine) {
|
||||
if (trackPad) {
|
||||
mTopPadding = top - above;
|
||||
@@ -1038,6 +1022,8 @@ public class StaticLayout extends Layout {
|
||||
}
|
||||
}
|
||||
|
||||
int extra;
|
||||
|
||||
if (lastLine) {
|
||||
if (trackPad) {
|
||||
mBottomPadding = bottom - below;
|
||||
@@ -1048,9 +1034,8 @@ public class StaticLayout extends Layout {
|
||||
}
|
||||
}
|
||||
|
||||
final int extra;
|
||||
if (needMultiply && (addLastLineLineSpacing || !lastLine)) {
|
||||
final double ex = (below - above) * (spacingmult - 1) + spacingadd;
|
||||
double ex = (below - above) * (spacingmult - 1) + spacingadd;
|
||||
if (ex >= 0) {
|
||||
extra = (int)(ex + EXTRA_ROUNDING);
|
||||
} else {
|
||||
@@ -1060,6 +1045,8 @@ public class StaticLayout extends Layout {
|
||||
extra = 0;
|
||||
}
|
||||
|
||||
lines[off + START] = start;
|
||||
lines[off + TOP] = v;
|
||||
lines[off + DESCENT] = below + extra;
|
||||
lines[off + EXTRA] = extra;
|
||||
|
||||
@@ -1067,7 +1054,7 @@ public class StaticLayout extends Layout {
|
||||
// store the height as if it was ellipsized
|
||||
if (!mEllipsized && currentLineIsTheLastVisibleOne) {
|
||||
// below calculation as if it was the last line
|
||||
final int maxLineBelow = includePad ? bottom : below;
|
||||
int maxLineBelow = includePad ? bottom : below;
|
||||
// similar to the calculation of v below, without the extra.
|
||||
mMaxLineHeight = v + (maxLineBelow - above);
|
||||
}
|
||||
@@ -1076,13 +1063,23 @@ public class StaticLayout extends Layout {
|
||||
lines[off + mColumns + START] = end;
|
||||
lines[off + mColumns + TOP] = v;
|
||||
|
||||
// TODO: could move TAB to share same column as HYPHEN, simplifying this code and gaining
|
||||
// one bit for start field
|
||||
lines[off + TAB] |= flags & TAB_MASK;
|
||||
lines[off + HYPHEN] = flags;
|
||||
lines[off + DIR] |= dir << DIR_SHIFT;
|
||||
mLineDirections[j] = measured.getDirections(start - widthStart, end - widthStart);
|
||||
|
||||
mLineCount++;
|
||||
return v;
|
||||
}
|
||||
|
||||
private void calculateEllipsis(CharSequence text, int lineStart, int lineEnd, float[] widths,
|
||||
int widthStart, float avail, TextUtils.TruncateAt where, int line, float textWidth,
|
||||
TextPaint paint, boolean forceEllipsis, int dir) {
|
||||
private void calculateEllipsis(int lineStart, int lineEnd,
|
||||
float[] widths, int widthStart,
|
||||
float avail, TextUtils.TruncateAt where,
|
||||
int line, float textWidth, TextPaint paint,
|
||||
boolean forceEllipsis) {
|
||||
avail -= getTotalInsets(line);
|
||||
if (textWidth <= avail && !forceEllipsis) {
|
||||
// Everything fits!
|
||||
mLines[mColumns * line + ELLIPSIS_START] = 0;
|
||||
@@ -1090,53 +1087,11 @@ public class StaticLayout extends Layout {
|
||||
return;
|
||||
}
|
||||
|
||||
float tempAvail = avail;
|
||||
int numberOfTries = 0;
|
||||
boolean lineFits = false;
|
||||
mWorkPaint.set(paint);
|
||||
do {
|
||||
final float ellipsizedWidth = guessEllipsis(text, lineStart, lineEnd, widths,
|
||||
widthStart, tempAvail, where, line, mWorkPaint, forceEllipsis, dir);
|
||||
if (ellipsizedWidth <= avail) {
|
||||
lineFits = true;
|
||||
} else {
|
||||
numberOfTries++;
|
||||
if (numberOfTries > 10) {
|
||||
// If the text still doesn't fit after ten tries, assume it will never fit and
|
||||
// ellipsize it all.
|
||||
mLines[mColumns * line + ELLIPSIS_START] = 0;
|
||||
mLines[mColumns * line + ELLIPSIS_COUNT] = lineEnd - lineStart;
|
||||
lineFits = true;
|
||||
} else {
|
||||
// Some side effect of ellipsization has caused the text to go over the
|
||||
// available width. Let's make the available width shorter by exactly that
|
||||
// amount and retry.
|
||||
tempAvail -= ellipsizedWidth - avail;
|
||||
}
|
||||
}
|
||||
} while (!lineFits);
|
||||
mEllipsized = true;
|
||||
}
|
||||
float ellipsisWidth = paint.measureText(TextUtils.getEllipsisString(where));
|
||||
int ellipsisStart = 0;
|
||||
int ellipsisCount = 0;
|
||||
int len = lineEnd - lineStart;
|
||||
|
||||
// Returns the width of the ellipsized line which in some rare cases can actually be larger
|
||||
// than 'avail' (due to kerning or other context-based effect of replacement of text by
|
||||
// ellipsis). If all the line needs to ellipsized away, or it's an invalud hyphenation mode,
|
||||
// returns 0 so the caller can stop iterating.
|
||||
//
|
||||
// This method temporarily modifies the TextPaint passed to it, so the TextPaint passed to it
|
||||
// should not be accessed while the method is running.
|
||||
private float guessEllipsis(CharSequence text, int lineStart, int lineEnd, float[] widths,
|
||||
int widthStart, float avail, TextUtils.TruncateAt where, int line,
|
||||
TextPaint paint, boolean forceEllipsis, int dir) {
|
||||
final int savedHyphenEdit = paint.getHyphenEdit();
|
||||
paint.setHyphenEdit(0);
|
||||
final float ellipsisWidth = paint.measureText(TextUtils.getEllipsisString(where));
|
||||
final int ellipsisStart;
|
||||
final int ellipsisCount;
|
||||
final int len = lineEnd - lineStart;
|
||||
final int offset = lineStart - widthStart;
|
||||
|
||||
int hyphen = getHyphen(line);
|
||||
// We only support start ellipsis on a single line
|
||||
if (where == TextUtils.TruncateAt.START) {
|
||||
if (mMaximumVisibleLineCount == 1) {
|
||||
@@ -1144,9 +1099,9 @@ public class StaticLayout extends Layout {
|
||||
int i;
|
||||
|
||||
for (i = len; i > 0; i--) {
|
||||
final float w = widths[i - 1 + offset];
|
||||
float w = widths[i - 1 + lineStart - widthStart];
|
||||
if (w + sum + ellipsisWidth > avail) {
|
||||
while (i < len && widths[i + offset] == 0.0f) {
|
||||
while (i < len && widths[i + lineStart - widthStart] == 0.0f) {
|
||||
i++;
|
||||
}
|
||||
break;
|
||||
@@ -1157,13 +1112,9 @@ public class StaticLayout extends Layout {
|
||||
|
||||
ellipsisStart = 0;
|
||||
ellipsisCount = i;
|
||||
// Strip the potential hyphenation at beginning of line.
|
||||
hyphen &= ~Paint.HYPHENEDIT_MASK_START_OF_LINE;
|
||||
} else {
|
||||
ellipsisStart = 0;
|
||||
ellipsisCount = 0;
|
||||
if (Log.isLoggable(TAG, Log.WARN)) {
|
||||
Log.w(TAG, "Start ellipsis only supported with one line");
|
||||
Log.w(TAG, "Start Ellipsis only supported with one line");
|
||||
}
|
||||
}
|
||||
} else if (where == TextUtils.TruncateAt.END || where == TextUtils.TruncateAt.MARQUEE ||
|
||||
@@ -1172,7 +1123,7 @@ public class StaticLayout extends Layout {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
final float w = widths[i + offset];
|
||||
float w = widths[i + lineStart - widthStart];
|
||||
|
||||
if (w + sum + ellipsisWidth > avail) {
|
||||
break;
|
||||
@@ -1181,27 +1132,24 @@ public class StaticLayout extends Layout {
|
||||
sum += w;
|
||||
}
|
||||
|
||||
if (forceEllipsis && i == len && len > 0) {
|
||||
ellipsisStart = i;
|
||||
ellipsisCount = len - i;
|
||||
if (forceEllipsis && ellipsisCount == 0 && len > 0) {
|
||||
ellipsisStart = len - 1;
|
||||
ellipsisCount = 1;
|
||||
} else {
|
||||
ellipsisStart = i;
|
||||
ellipsisCount = len - i;
|
||||
}
|
||||
// Strip the potential hyphenation at end of line.
|
||||
hyphen &= ~Paint.HYPHENEDIT_MASK_END_OF_LINE;
|
||||
} else { // where = TextUtils.TruncateAt.MIDDLE
|
||||
// We only support middle ellipsis on a single line.
|
||||
} else {
|
||||
// where = TextUtils.TruncateAt.MIDDLE We only support middle ellipsis on a single line
|
||||
if (mMaximumVisibleLineCount == 1) {
|
||||
float lsum = 0, rsum = 0;
|
||||
int left = 0, right = len;
|
||||
|
||||
final float ravail = (avail - ellipsisWidth) / 2;
|
||||
float ravail = (avail - ellipsisWidth) / 2;
|
||||
for (right = len; right > 0; right--) {
|
||||
final float w = widths[right - 1 + offset];
|
||||
float w = widths[right - 1 + lineStart - widthStart];
|
||||
|
||||
if (w + rsum > ravail) {
|
||||
while (right < len && widths[right + offset] == 0.0f) {
|
||||
while (right < len && widths[right + lineStart - widthStart] == 0.0f) {
|
||||
right++;
|
||||
}
|
||||
break;
|
||||
@@ -1209,9 +1157,9 @@ public class StaticLayout extends Layout {
|
||||
rsum += w;
|
||||
}
|
||||
|
||||
final float lavail = avail - ellipsisWidth - rsum;
|
||||
float lavail = avail - ellipsisWidth - rsum;
|
||||
for (left = 0; left < right; left++) {
|
||||
final float w = widths[left + offset];
|
||||
float w = widths[left + lineStart - widthStart];
|
||||
|
||||
if (w + lsum > lavail) {
|
||||
break;
|
||||
@@ -1223,53 +1171,14 @@ public class StaticLayout extends Layout {
|
||||
ellipsisStart = left;
|
||||
ellipsisCount = right - left;
|
||||
} else {
|
||||
ellipsisStart = 0;
|
||||
ellipsisCount = 0;
|
||||
if (Log.isLoggable(TAG, Log.WARN)) {
|
||||
Log.w(TAG, "Middle ellipsis only supported with one line");
|
||||
Log.w(TAG, "Middle Ellipsis only supported with one line");
|
||||
}
|
||||
}
|
||||
}
|
||||
mEllipsized = true;
|
||||
mLines[mColumns * line + ELLIPSIS_START] = ellipsisStart;
|
||||
mLines[mColumns * line + ELLIPSIS_COUNT] = ellipsisCount;
|
||||
|
||||
if (ellipsisStart == 0 && (ellipsisCount == 0 || ellipsisCount == len)) {
|
||||
// Unsupported ellipsization mode or all text is ellipsized away. Return 0.
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
final boolean isSpanned = text instanceof Spanned;
|
||||
final Ellipsizer ellipsizedText = isSpanned
|
||||
? new SpannedEllipsizer(text)
|
||||
: new Ellipsizer(text);
|
||||
ellipsizedText.mLayout = this;
|
||||
ellipsizedText.mMethod = where;
|
||||
|
||||
final boolean hasTabs = getLineContainsTab(line);
|
||||
final TabStops tabStops;
|
||||
if (hasTabs && isSpanned) {
|
||||
final TabStopSpan[] tabs = getParagraphSpans((Spanned) ellipsizedText, lineStart,
|
||||
lineEnd, TabStopSpan.class);
|
||||
if (tabs.length == 0) {
|
||||
tabStops = null;
|
||||
} else {
|
||||
tabStops = new TabStops(TAB_INCREMENT, tabs);
|
||||
}
|
||||
} else {
|
||||
tabStops = null;
|
||||
}
|
||||
paint.setHyphenEdit(hyphen);
|
||||
final TextLine textline = TextLine.obtain();
|
||||
textline.set(paint, ellipsizedText, lineStart, lineEnd, dir, getLineDirections(line),
|
||||
hasTabs, tabStops);
|
||||
// Since TextLine.metric() returns negative values for RTL text, multiplication by dir
|
||||
// converts it to an actual width. Note that we don't want to use the absolute value,
|
||||
// since we may actually have glyphs with negative advances, which by definition always
|
||||
// fit.
|
||||
final float ellipsizedWidth = textline.metrics(null) * dir;
|
||||
TextLine.recycle(textline);
|
||||
paint.setHyphenEdit(savedHyphenEdit);
|
||||
return ellipsizedWidth;
|
||||
}
|
||||
|
||||
private float getTotalInsets(int line) {
|
||||
@@ -1509,8 +1418,6 @@ public class StaticLayout extends Layout {
|
||||
*/
|
||||
private int mMaxLineHeight = DEFAULT_MAX_LINE_HEIGHT;
|
||||
|
||||
private TextPaint mWorkPaint = new TextPaint();
|
||||
|
||||
private static final int COLUMNS_NORMAL = 5;
|
||||
private static final int COLUMNS_ELLIPSIZE = 7;
|
||||
private static final int START = 0;
|
||||
|
||||
@@ -88,8 +88,8 @@ public class TextUtils {
|
||||
|
||||
/** {@hide} */
|
||||
@NonNull
|
||||
public static String getEllipsisString(@NonNull TruncateAt method) {
|
||||
return (method == TruncateAt.END_SMALL) ? ELLIPSIS_TWO_DOTS : ELLIPSIS_NORMAL;
|
||||
public static String getEllipsisString(@NonNull TextUtils.TruncateAt method) {
|
||||
return (method == TextUtils.TruncateAt.END_SMALL) ? ELLIPSIS_TWO_DOTS : ELLIPSIS_NORMAL;
|
||||
}
|
||||
|
||||
|
||||
@@ -1194,11 +1194,9 @@ public class TextUtils {
|
||||
* or, if it does not fit, a truncated
|
||||
* copy with ellipsis character added at the specified edge or center.
|
||||
*/
|
||||
@NonNull
|
||||
public static CharSequence ellipsize(@NonNull CharSequence text,
|
||||
@NonNull TextPaint p,
|
||||
@FloatRange(from = 0.0) float avail,
|
||||
@NonNull TruncateAt where) {
|
||||
public static CharSequence ellipsize(CharSequence text,
|
||||
TextPaint p,
|
||||
float avail, TruncateAt where) {
|
||||
return ellipsize(text, p, avail, where, false, null);
|
||||
}
|
||||
|
||||
@@ -1214,11 +1212,9 @@ public class TextUtils {
|
||||
* report the start and end of the ellipsized range. TextDirection
|
||||
* is determined by the first strong directional character.
|
||||
*/
|
||||
@NonNull
|
||||
public static CharSequence ellipsize(@NonNull CharSequence text,
|
||||
@NonNull TextPaint paint,
|
||||
@FloatRange(from = 0.0) float avail,
|
||||
@NonNull TruncateAt where,
|
||||
public static CharSequence ellipsize(CharSequence text,
|
||||
TextPaint paint,
|
||||
float avail, TruncateAt where,
|
||||
boolean preserveLength,
|
||||
@Nullable EllipsizeCallback callback) {
|
||||
return ellipsize(text, paint, avail, where, preserveLength, callback,
|
||||
@@ -1239,19 +1235,16 @@ public class TextUtils {
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
public static CharSequence ellipsize(@NonNull CharSequence text,
|
||||
@NonNull TextPaint paint,
|
||||
@FloatRange(from = 0.0) float avail,
|
||||
@NonNull TruncateAt where,
|
||||
public static CharSequence ellipsize(CharSequence text,
|
||||
TextPaint paint,
|
||||
float avail, TruncateAt where,
|
||||
boolean preserveLength,
|
||||
@Nullable EllipsizeCallback callback,
|
||||
@NonNull TextDirectionHeuristic textDir,
|
||||
@NonNull String ellipsis) {
|
||||
TextDirectionHeuristic textDir, String ellipsis) {
|
||||
|
||||
int len = text.length();
|
||||
|
||||
final int len = text.length();
|
||||
MeasuredParagraph mt = null;
|
||||
MeasuredParagraph resultMt = null;
|
||||
try {
|
||||
mt = MeasuredParagraph.buildForMeasurement(paint, text, 0, text.length(), textDir, mt);
|
||||
float width = mt.getWholeWidth();
|
||||
@@ -1260,110 +1253,76 @@ public class TextUtils {
|
||||
if (callback != null) {
|
||||
callback.ellipsized(0, 0);
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
// First estimate of effective width of ellipsis.
|
||||
float ellipsisWidth = paint.measureText(ellipsis);
|
||||
int numberOfTries = 0;
|
||||
boolean textFits = false;
|
||||
int start, end;
|
||||
CharSequence result;
|
||||
do {
|
||||
if (avail < ellipsisWidth) {
|
||||
// Even the ellipsis can't fit. So it all goes.
|
||||
start = 0;
|
||||
end = len;
|
||||
} else {
|
||||
final float remainingWidth = avail - ellipsisWidth;
|
||||
if (where == TruncateAt.START) {
|
||||
start = 0;
|
||||
end = len - mt.breakText(len, false /* backwards */, remainingWidth);
|
||||
} else if (where == TruncateAt.END || where == TruncateAt.END_SMALL) {
|
||||
start = mt.breakText(len, true /* forwards */, remainingWidth);
|
||||
end = len;
|
||||
} else {
|
||||
end = len - mt.breakText(len, false /* backwards */, remainingWidth / 2);
|
||||
start = mt.breakText(end, true /* forwards */,
|
||||
remainingWidth - mt.measure(end, len));
|
||||
}
|
||||
}
|
||||
// XXX assumes ellipsis string does not require shaping and
|
||||
// is unaffected by style
|
||||
float ellipsiswid = paint.measureText(ellipsis);
|
||||
avail -= ellipsiswid;
|
||||
|
||||
final char[] buf = mt.getChars();
|
||||
final Spanned sp = text instanceof Spanned ? (Spanned) text : null;
|
||||
|
||||
final int removed = end - start;
|
||||
final int remaining = len - removed;
|
||||
if (preserveLength) {
|
||||
int pos = start;
|
||||
if (remaining > 0 && removed >= ellipsis.length()) {
|
||||
ellipsis.getChars(0, ellipsis.length(), buf, start);
|
||||
pos += ellipsis.length();
|
||||
} // else eliminate the ellipsis
|
||||
while (pos < end) {
|
||||
buf[pos++] = ELLIPSIS_FILLER;
|
||||
}
|
||||
final String s = new String(buf, 0, len);
|
||||
if (sp == null) {
|
||||
result = s;
|
||||
} else {
|
||||
final SpannableString ss = new SpannableString(s);
|
||||
copySpansFrom(sp, 0, len, Object.class, ss, 0);
|
||||
result = ss;
|
||||
}
|
||||
} else {
|
||||
if (remaining == 0) {
|
||||
result = "";
|
||||
} else if (sp == null) {
|
||||
final StringBuilder sb = new StringBuilder(remaining + ellipsis.length());
|
||||
sb.append(buf, 0, start);
|
||||
sb.append(ellipsis);
|
||||
sb.append(buf, end, len - end);
|
||||
result = sb.toString();
|
||||
} else {
|
||||
final SpannableStringBuilder ssb = new SpannableStringBuilder();
|
||||
ssb.append(text, 0, start);
|
||||
ssb.append(ellipsis);
|
||||
ssb.append(text, end, len);
|
||||
result = ssb;
|
||||
}
|
||||
}
|
||||
|
||||
if (remaining == 0) { // All text is gone.
|
||||
textFits = true;
|
||||
} else {
|
||||
resultMt = MeasuredParagraph.buildForMeasurement(
|
||||
paint, result, 0, result.length(), textDir, resultMt);
|
||||
width = resultMt.getWholeWidth();
|
||||
if (width <= avail) {
|
||||
textFits = true;
|
||||
} else {
|
||||
numberOfTries++;
|
||||
if (numberOfTries > 10) {
|
||||
// If the text still doesn't fit after ten tries, assume it will never
|
||||
// fit and ellipsize it all. We do this by setting the width of the
|
||||
// ellipsis to be positive infinity, so we get to empty text in the next
|
||||
// round.
|
||||
ellipsisWidth = Float.POSITIVE_INFINITY;
|
||||
} else {
|
||||
// Adjust the width of the ellipsis by adding the amount 'width' is
|
||||
// still over.
|
||||
ellipsisWidth += width - avail;
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (!textFits);
|
||||
if (callback != null) {
|
||||
callback.ellipsized(start, end);
|
||||
int left = 0;
|
||||
int right = len;
|
||||
if (avail < 0) {
|
||||
// it all goes
|
||||
} else if (where == TruncateAt.START) {
|
||||
right = len - mt.breakText(len, false, avail);
|
||||
} else if (where == TruncateAt.END || where == TruncateAt.END_SMALL) {
|
||||
left = mt.breakText(len, true, avail);
|
||||
} else {
|
||||
right = len - mt.breakText(len, false, avail / 2);
|
||||
avail -= mt.measure(right, len);
|
||||
left = mt.breakText(right, true, avail);
|
||||
}
|
||||
return result;
|
||||
|
||||
if (callback != null) {
|
||||
callback.ellipsized(left, right);
|
||||
}
|
||||
|
||||
final char[] buf = mt.getChars();
|
||||
Spanned sp = text instanceof Spanned ? (Spanned) text : null;
|
||||
|
||||
final int removed = right - left;
|
||||
final int remaining = len - removed;
|
||||
if (preserveLength) {
|
||||
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] = ELLIPSIS_FILLER;
|
||||
}
|
||||
String s = new String(buf, 0, len);
|
||||
if (sp == null) {
|
||||
return s;
|
||||
}
|
||||
SpannableString ss = new SpannableString(s);
|
||||
copySpansFrom(sp, 0, len, Object.class, ss, 0);
|
||||
return ss;
|
||||
}
|
||||
|
||||
if (remaining == 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (sp == null) {
|
||||
StringBuilder sb = new StringBuilder(remaining + ellipsis.length());
|
||||
sb.append(buf, 0, left);
|
||||
sb.append(ellipsis);
|
||||
sb.append(buf, right, len - right);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
SpannableStringBuilder ssb = new SpannableStringBuilder();
|
||||
ssb.append(text, 0, left);
|
||||
ssb.append(ellipsis);
|
||||
ssb.append(text, right, len);
|
||||
return ssb;
|
||||
} finally {
|
||||
if (mt != null) {
|
||||
mt.recycle();
|
||||
}
|
||||
if (resultMt != null) {
|
||||
resultMt.recycle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1394,6 +1353,7 @@ public class TextUtils {
|
||||
* @return the formatted CharSequence. If even the shortest sequence (e.g. {@code "A, 11 more"})
|
||||
* doesn't fit, it will return an empty string.
|
||||
*/
|
||||
|
||||
public static CharSequence listEllipsize(@Nullable Context context,
|
||||
@Nullable List<CharSequence> elements, @NonNull String separator,
|
||||
@NonNull TextPaint paint, @FloatRange(from=0.0,fromInclusive=false) float avail,
|
||||
|
||||
@@ -2742,7 +2742,7 @@ public class Paint {
|
||||
* @param offset index of caret position
|
||||
* @return width measurement between start and offset
|
||||
*/
|
||||
public float getRunAdvance(@NonNull CharSequence text, int start, int end, int contextStart,
|
||||
public float getRunAdvance(CharSequence text, int start, int end, int contextStart,
|
||||
int contextEnd, boolean isRtl, int offset) {
|
||||
if (text == null) {
|
||||
throw new IllegalArgumentException("text cannot be null");
|
||||
|
||||
Reference in New Issue
Block a user