summaryrefslogtreecommitdiff
path: root/core/java/android/text/TextUtils.java
diff options
context:
space:
mode:
authorRoozbeh Pournader <roozbeh@google.com>2016-06-17 15:03:56 -0700
committerRoozbeh Pournader <roozbeh@google.com>2016-06-24 16:45:21 -0700
commit3bfce33993a3aea981cf60e1a19757dea0cc2374 (patch)
tree543f5efe127b7084f0113267df86bcec594834d3 /core/java/android/text/TextUtils.java
parent42e62608e55d65109310d1596486e295fb82a2f5 (diff)
Introduce listEllipsize() and deprecate commaEllipsize()
The commaEllipsize() method was beyond repair. It couldn't support several needed internationalization features, and its API depended on splitting a string and the language having only one plural form. Bug: 24097278 Change-Id: I85217e4eadac971c58bb001b62ba386fb1297a24
Diffstat (limited to 'core/java/android/text/TextUtils.java')
-rw-r--r--core/java/android/text/TextUtils.java95
1 files changed, 94 insertions, 1 deletions
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index d7e786a6f2c5..23b22d1dcf96 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -16,7 +16,11 @@
package android.text;
+import android.annotation.FloatRange;
+import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.PluralsRes;
+import android.content.Context;
import android.content.res.Resources;
import android.icu.util.ULocale;
import android.os.Parcel;
@@ -60,6 +64,7 @@ import libcore.icu.ICU;
import java.lang.reflect.Array;
import java.util.Iterator;
+import java.util.List;
import java.util.Locale;
import java.util.regex.Pattern;
@@ -1191,16 +1196,103 @@ public class TextUtils {
}
/**
+ * Formats a list of CharSequences by repeatedly inserting the separator between them,
+ * but stopping when the resulting sequence is too wide for the specified width.
+ *
+ * This method actually tries to fit the maximum number of elements. So if {@code "A, 11 more"
+ * fits}, {@code "A, B, 10 more"} doesn't fit, but {@code "A, B, C, 9 more"} fits again (due to
+ * the glyphs for the digits being very wide, for example), it returns
+ * {@code "A, B, C, 9 more"}. Because of this, this method may be inefficient for very long
+ * lists.
+ *
+ * Note that the elements of the returned value, as well as the string for {@code moreId}, will
+ * be bidi-wrapped using {@link BidiFormatter#unicodeWrap} based on the locale of the input
+ * Context. If the input {@code Context} is null, the default BidiFormatter from
+ * {@link BidiFormatter#getInstance()} will be used.
+ *
+ * @param context the {@code Context} to get the {@code moreId} resource from. If {@code null},
+ * an ellipsis (U+2026) would be used for {@code moreId}.
+ * @param elements the list to format
+ * @param separator a separator, such as {@code ", "}
+ * @param paint the Paint with which to measure the text
+ * @param avail the horizontal width available for the text (in pixels)
+ * @param moreId the resource ID for the pluralized string to insert at the end of sequence when
+ * some of the elements don't fit.
+ *
+ * @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,
+ @PluralsRes int moreId) {
+ if (elements == null) {
+ return "";
+ }
+ final int totalLen = elements.size();
+ if (totalLen == 0) {
+ return "";
+ }
+
+ final Resources res;
+ final BidiFormatter bidiFormatter;
+ if (context == null) {
+ res = null;
+ bidiFormatter = BidiFormatter.getInstance();
+ } else {
+ res = context.getResources();
+ bidiFormatter = BidiFormatter.getInstance(res.getConfiguration().getLocales().get(0));
+ }
+
+ final SpannableStringBuilder output = new SpannableStringBuilder();
+ final int[] endIndexes = new int[totalLen];
+ for (int i = 0; i < totalLen; i++) {
+ output.append(bidiFormatter.unicodeWrap(elements.get(i)));
+ if (i != totalLen - 1) { // Insert a separator, except at the very end.
+ output.append(separator);
+ }
+ endIndexes[i] = output.length();
+ }
+
+ for (int i = totalLen - 1; i >= 0; i--) {
+ // Delete the tail of the string, cutting back to one less element.
+ output.delete(endIndexes[i], output.length());
+
+ final int remainingElements = totalLen - i - 1;
+ if (remainingElements > 0) {
+ CharSequence morePiece = (res == null) ?
+ ELLIPSIS_STRING :
+ res.getQuantityString(moreId, remainingElements, remainingElements);
+ morePiece = bidiFormatter.unicodeWrap(morePiece);
+ output.append(morePiece);
+ }
+
+ final float width = paint.measureText(output, 0, output.length());
+ if (width <= avail) { // The string fits.
+ return output;
+ }
+ }
+ return ""; // Nothing fits.
+ }
+
+ /**
* Converts a CharSequence of the comma-separated form "Andy, Bob,
* Charles, David" that is too wide to fit into the specified width
* into one like "Andy, Bob, 2 more".
*
* @param text the text to truncate
* @param p the Paint with which to measure the text
- * @param avail the horizontal width available for the text
+ * @param avail the horizontal width available for the text (in pixels)
* @param oneMore the string for "1 more" in the current locale
* @param more the string for "%d more" in the current locale
+ *
+ * @deprecated Do not use. This is not internationalized, and has known issues
+ * with right-to-left text, languages that have more than one plural form, languages
+ * that use a different character as a comma-like separator, etc.
+ * Use {@link #listEllipsize} instead.
*/
+ @Deprecated
public static CharSequence commaEllipsize(CharSequence text,
TextPaint p, float avail,
String oneMore,
@@ -1212,6 +1304,7 @@ public class TextUtils {
/**
* @hide
*/
+ @Deprecated
public static CharSequence commaEllipsize(CharSequence text, TextPaint p,
float avail, String oneMore, String more, TextDirectionHeuristic textDir) {