From 5d04239b3bf713283c77e6d43d5cabf5ced1cbc0 Mon Sep 17 00:00:00 2001 From: Andrei Stingaceanu Date: Tue, 17 Jan 2017 14:00:41 +0000 Subject: [PATCH] AutoSize TextView (part 7) - APIs for configuring auto-size Contains: * getters/setters for all auto-size config attributes * verbose JavaDocs * isolated and reused the setup part of auto-size * some variables had to become instance fields * clarified some variable names In a few words, whenever a configuration setter is called, the value is assigned and setupAutoSizeTextXY() gets called. The latter tries to build all the intermediary values that it needs, attaches default values, etc. If it is successful then it makes a call to autoSizeText(), else it throws a runtime exception with an appropriate message. Bug: 32221168 Test: attached in the same topic Change-Id: I448454ef4490102c8cc2b19e0e16e998aeb45cd0 --- api/current.txt | 12 +- api/system-current.txt | 12 +- api/test-current.txt | 12 +- core/java/android/widget/EditText.java | 2 + core/java/android/widget/TextView.java | 279 ++++++++++++++++++++----- core/res/res/values/attrs.xml | 3 +- 6 files changed, 261 insertions(+), 59 deletions(-) diff --git a/api/current.txt b/api/current.txt index 889c05851ebef..c997057ed4c72 100644 --- a/api/current.txt +++ b/api/current.txt @@ -49271,6 +49271,10 @@ package android.widget { method public void endBatchEdit(); method public boolean extractText(android.view.inputmethod.ExtractedTextRequest, android.view.inputmethod.ExtractedText); method public final int getAutoLinkMask(); + method public int getAutoSizeMaxTextSize(); + method public int getAutoSizeMinTextSize(); + method public int getAutoSizeStepGranularity(); + method public int getAutoSizeTextType(); method public int getBreakStrategy(); method public int getCompoundDrawablePadding(); method public android.content.res.ColorStateList getCompoundDrawableTintList(); @@ -49380,6 +49384,10 @@ package android.widget { method public void removeTextChangedListener(android.text.TextWatcher); method public void setAllCaps(boolean); method public final void setAutoLinkMask(int); + method public void setAutoSizeMaxTextSize(int, float); + method public void setAutoSizeMinTextSize(int, float); + method public void setAutoSizeStepGranularity(int, float); + method public void setAutoSizeTextType(int); method public void setBreakStrategy(int); method public void setCompoundDrawablePadding(int); method public void setCompoundDrawableTintList(android.content.res.ColorStateList); @@ -49471,8 +49479,8 @@ package android.widget { method public void setTypeface(android.graphics.Typeface, int); method public void setTypeface(android.graphics.Typeface); method public void setWidth(int); - field public static final int AUTO_SIZE_TYPE_NONE = 0; // 0x0 - field public static final int AUTO_SIZE_TYPE_XY = 1; // 0x1 + field public static final int AUTO_SIZE_TEXT_TYPE_NONE = 0; // 0x0 + field public static final int AUTO_SIZE_TEXT_TYPE_XY = 1; // 0x1 } public static final class TextView.BufferType extends java.lang.Enum { diff --git a/api/system-current.txt b/api/system-current.txt index d9c9372c6109b..1b08faa14966d 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -52887,6 +52887,10 @@ package android.widget { method public void endBatchEdit(); method public boolean extractText(android.view.inputmethod.ExtractedTextRequest, android.view.inputmethod.ExtractedText); method public final int getAutoLinkMask(); + method public int getAutoSizeMaxTextSize(); + method public int getAutoSizeMinTextSize(); + method public int getAutoSizeStepGranularity(); + method public int getAutoSizeTextType(); method public int getBreakStrategy(); method public int getCompoundDrawablePadding(); method public android.content.res.ColorStateList getCompoundDrawableTintList(); @@ -52996,6 +53000,10 @@ package android.widget { method public void removeTextChangedListener(android.text.TextWatcher); method public void setAllCaps(boolean); method public final void setAutoLinkMask(int); + method public void setAutoSizeMaxTextSize(int, float); + method public void setAutoSizeMinTextSize(int, float); + method public void setAutoSizeStepGranularity(int, float); + method public void setAutoSizeTextType(int); method public void setBreakStrategy(int); method public void setCompoundDrawablePadding(int); method public void setCompoundDrawableTintList(android.content.res.ColorStateList); @@ -53087,8 +53095,8 @@ package android.widget { method public void setTypeface(android.graphics.Typeface, int); method public void setTypeface(android.graphics.Typeface); method public void setWidth(int); - field public static final int AUTO_SIZE_TYPE_NONE = 0; // 0x0 - field public static final int AUTO_SIZE_TYPE_XY = 1; // 0x1 + field public static final int AUTO_SIZE_TEXT_TYPE_NONE = 0; // 0x0 + field public static final int AUTO_SIZE_TEXT_TYPE_XY = 1; // 0x1 } public static final class TextView.BufferType extends java.lang.Enum { diff --git a/api/test-current.txt b/api/test-current.txt index 7e84da391551a..40e28785581fa 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -49573,6 +49573,10 @@ package android.widget { method public void endBatchEdit(); method public boolean extractText(android.view.inputmethod.ExtractedTextRequest, android.view.inputmethod.ExtractedText); method public final int getAutoLinkMask(); + method public int getAutoSizeMaxTextSize(); + method public int getAutoSizeMinTextSize(); + method public int getAutoSizeStepGranularity(); + method public int getAutoSizeTextType(); method public int getBreakStrategy(); method public int getCompoundDrawablePadding(); method public android.content.res.ColorStateList getCompoundDrawableTintList(); @@ -49682,6 +49686,10 @@ package android.widget { method public void removeTextChangedListener(android.text.TextWatcher); method public void setAllCaps(boolean); method public final void setAutoLinkMask(int); + method public void setAutoSizeMaxTextSize(int, float); + method public void setAutoSizeMinTextSize(int, float); + method public void setAutoSizeStepGranularity(int, float); + method public void setAutoSizeTextType(int); method public void setBreakStrategy(int); method public void setCompoundDrawablePadding(int); method public void setCompoundDrawableTintList(android.content.res.ColorStateList); @@ -49773,8 +49781,8 @@ package android.widget { method public void setTypeface(android.graphics.Typeface, int); method public void setTypeface(android.graphics.Typeface); method public void setWidth(int); - field public static final int AUTO_SIZE_TYPE_NONE = 0; // 0x0 - field public static final int AUTO_SIZE_TYPE_XY = 1; // 0x1 + field public static final int AUTO_SIZE_TEXT_TYPE_NONE = 0; // 0x0 + field public static final int AUTO_SIZE_TEXT_TYPE_XY = 1; // 0x1 } public static final class TextView.BufferType extends java.lang.Enum { diff --git a/core/java/android/widget/EditText.java b/core/java/android/widget/EditText.java index af5c8426a44a5..faf0379648973 100644 --- a/core/java/android/widget/EditText.java +++ b/core/java/android/widget/EditText.java @@ -42,6 +42,8 @@ import android.view.autofill.AutoFillValue; *

See the Text Fields * guide.

*

+ * This widget does not support auto-sizing text. + *

* XML attributes *

* See {@link android.R.styleable#EditText EditText Attributes}, diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index b6693c715da2c..25038b2702470 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -22,6 +22,7 @@ import android.R; import android.annotation.ColorInt; import android.annotation.DrawableRes; import android.annotation.FloatRange; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Size; @@ -158,6 +159,8 @@ import com.android.internal.widget.EditableInputConnection; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Locale; @@ -252,6 +255,10 @@ import java.util.Locale; * @attr ref android.R.styleable#TextView_fontFeatureSettings * @attr ref android.R.styleable#TextView_breakStrategy * @attr ref android.R.styleable#TextView_hyphenationFrequency + * @attr ref android.R.styleable#TextView_autoSizeText + * @attr ref android.R.styleable#TextView_autoSizeMinTextSize + * @attr ref android.R.styleable#TextView_autoSizeMaxTextSize + * @attr ref android.R.styleable#TextView_autoSizeStepGranularity */ @RemoteView public class TextView extends View implements ViewTreeObserver.OnPreDrawListener { @@ -670,17 +677,31 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener */ private int mDeviceProvisionedState = DEVICE_PROVISIONED_UNKNOWN; - // The TextView does not auto-size text. - public static final int AUTO_SIZE_TYPE_NONE = 0; + // The TextView does not auto-size text (default). + public static final int AUTO_SIZE_TEXT_TYPE_NONE = 0; // The TextView performs uniform horizontal and vertical text size scaling to fit within the // container. - public static final int AUTO_SIZE_TYPE_XY = 1; - // Auto-size type. - private int mAutoSizeType = AUTO_SIZE_TYPE_NONE; - // Specify if auto-size is needed. - private boolean mNeedsAutoSize = false; + public static final int AUTO_SIZE_TEXT_TYPE_XY = 1; + /** @hide */ + @IntDef({AUTO_SIZE_TEXT_TYPE_NONE, AUTO_SIZE_TEXT_TYPE_XY}) + @Retention(RetentionPolicy.SOURCE) + public @interface AutoSizeTextType {} + // Default minimum size for auto-sizing text in scaled pixels. {@see #setAutoSizeMinTextSize}. + private static final int DEFAULT_AUTO_SIZE_MIN_TEXT_SIZE_IN_SP = 12; + // Default maximum size for auto-sizing text in scaled pixels. {@see #setAutoSizeMaxTextSize}. + private static final int DEFAULT_AUTO_SIZE_MAX_TEXT_SIZE_IN_SP = 112; // Default value for the step size in pixels. private static final int DEFAULT_AUTO_SIZE_GRANULARITY_IN_PX = 1; + // Auto-size text type. + private int mAutoSizeTextType = AUTO_SIZE_TEXT_TYPE_NONE; + // Specify if auto-size text is needed. + private boolean mNeedsAutoSizeText = false; + // Step size for auto-sizing in pixels. + private int mAutoSizeStepGranularityInPx = 0; + // Minimum text size for auto-sizing in pixels. + private int mAutoSizeMinTextSizeInPx = 0; + // Maximum text size for auto-sizing in pixels. + private int mAutoSizeMaxTextSizeInPx = 0; // Contains the sorted set of desired text sizes in pixels to pick from when auto-sizing text. private int[] mAutoSizeTextSizesInPx; @@ -887,13 +908,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener CharSequence hint = null; boolean password = false; int inputType = EditorInfo.TYPE_NULL; - int autoSizeStepGranularityInPx = DEFAULT_AUTO_SIZE_GRANULARITY_IN_PX; - int autoSizeMinTextSize = (int) TypedValue.applyDimension( - TypedValue.COMPLEX_UNIT_SP, 12, getResources().getDisplayMetrics()); - int autoSizeMaxTextSize = (int) TypedValue.applyDimension( - TypedValue.COMPLEX_UNIT_SP, 112, getResources().getDisplayMetrics()); - - a = theme.obtainStyledAttributes( attrs, com.android.internal.R.styleable.TextView, defStyleAttr, defStyleRes); @@ -1249,20 +1263,19 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener break; case com.android.internal.R.styleable.TextView_autoSizeText: - mAutoSizeType = a.getInt(attr, AUTO_SIZE_TYPE_NONE); + mAutoSizeTextType = a.getInt(attr, AUTO_SIZE_TEXT_TYPE_NONE); break; case com.android.internal.R.styleable.TextView_autoSizeStepGranularity: - autoSizeStepGranularityInPx = a.getDimensionPixelSize( - attr, DEFAULT_AUTO_SIZE_GRANULARITY_IN_PX); + mAutoSizeStepGranularityInPx = a.getDimensionPixelSize(attr, 0); break; case com.android.internal.R.styleable.TextView_autoSizeMinTextSize: - autoSizeMinTextSize = a.getDimensionPixelSize(attr, autoSizeMinTextSize); + mAutoSizeMinTextSizeInPx = a.getDimensionPixelSize(attr, 0); break; case com.android.internal.R.styleable.TextView_autoSizeMaxTextSize: - autoSizeMaxTextSize = a.getDimensionPixelSize(attr, autoSizeMaxTextSize); + mAutoSizeMaxTextSizeInPx = a.getDimensionPixelSize(attr, 0); break; } } @@ -1542,39 +1555,204 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); } - // Setup auto-size. + setupAutoSizeTextXY(); + } + + /** + * Specify whether this widget should automatically scale the text to try to perfectly fit + * within the layout bounds by taking into account the auto-size configuration. + * + * @param autoSizeTextType the type of auto-size. Must be one of + * {@link TextView#AUTO_SIZE_TEXT_TYPE_NONE} or + * {@link TextView#AUTO_SIZE_TEXT_TYPE_XY} + * + * @attr ref android.R.styleable#TextView_autoSizeText + * + * @see #getAutoSizeTextType() + */ + public void setAutoSizeTextType(@AutoSizeTextType int autoSizeTextType) { if (supportsAutoSizeText()) { - switch (mAutoSizeType) { - case AUTO_SIZE_TYPE_NONE: - // Nothing to do. + switch (autoSizeTextType) { + case AUTO_SIZE_TEXT_TYPE_NONE: + if (mAutoSizeTextType != AUTO_SIZE_TEXT_TYPE_NONE) { + // Clear all auto-size configuration + mAutoSizeTextType = AUTO_SIZE_TEXT_TYPE_NONE; + mAutoSizeMinTextSizeInPx = 0; + mAutoSizeMaxTextSizeInPx = 0; + mAutoSizeStepGranularityInPx = 0; + mAutoSizeTextSizesInPx = null; + mNeedsAutoSizeText = false; + } break; - case AUTO_SIZE_TYPE_XY: - if (autoSizeMaxTextSize <= autoSizeMinTextSize) { - throw new IllegalStateException("Maximum text size is less then minimum " - + "text size"); + case AUTO_SIZE_TEXT_TYPE_XY: + if (mAutoSizeTextType != AUTO_SIZE_TEXT_TYPE_XY) { + mAutoSizeTextType = AUTO_SIZE_TEXT_TYPE_XY; + setupAutoSizeTextXY(); } - - if (autoSizeStepGranularityInPx <= 0) { - throw new IllegalStateException("Unexpected zero or negative value for auto" - + " size step granularity in pixels"); - } - - final int autoSizeValuesLength = (autoSizeMaxTextSize - autoSizeMinTextSize) - / autoSizeStepGranularityInPx; - mAutoSizeTextSizesInPx = new int[autoSizeValuesLength]; - int sizeToAdd = autoSizeMinTextSize; - for (int i = 0; i < autoSizeValuesLength; i++) { - mAutoSizeTextSizesInPx[i] = sizeToAdd; - sizeToAdd += autoSizeStepGranularityInPx; - } - - mNeedsAutoSize = true; break; default: throw new IllegalArgumentException( - "Unknown autoSizeText type: " + mAutoSizeType); + "Unknown auto-size text type: " + autoSizeTextType); + } + } + } + + /** + * Returns the type of auto-size set for this widget. + * + * @return an {@code int} corresponding to one of the auto-size types: + * {@link TextView#AUTO_SIZE_TEXT_TYPE_NONE} or + * {@link TextView#AUTO_SIZE_TEXT_TYPE_XY} + * + * @attr ref android.R.styleable#TextView_autoSizeText + * + * @see #setAutoSizeTextType(int) + */ + @AutoSizeTextType + public int getAutoSizeTextType() { + return mAutoSizeTextType; + } + + /** + * Sets the auto-size step granularity. It is used in conjunction with auto-size minimum + * and maximum text size in order to build the set of text sizes the system uses to choose + * from when auto-sizing. + * + * @param unit the desired dimension unit. See {@link TypedValue} for the possible + * dimension units + * @param size the desired size in the given units + * + * @attr ref android.R.styleable#TextView_autoSizeStepGranularity + * + * @see #getAutoSizeStepGranularity() + * @see #setAutoSizeMinTextSize(int, float) + * @see #setAutoSizeMaxTextSize(int, float) + */ + public void setAutoSizeStepGranularity(int unit, float size) { + if (supportsAutoSizeText()) { + mAutoSizeStepGranularityInPx = (int) TypedValue.applyDimension( + unit, size, getResources().getDisplayMetrics()); + setupAutoSizeTextXY(); + } + } + + /** + * @return the current auto-size step granularity in pixels. + * + * @see #setAutoSizeStepGranularity(int, float) + */ + public int getAutoSizeStepGranularity() { + return mAutoSizeStepGranularityInPx; + } + + /** + * Sets the minimum text size to be used in conjunction with auto-size maximum text size and + * auto-size step granularity in order to build the set of text sizes the system uses to choose + * from when auto-sizing. + * + * @param unit the desired dimension unit. See {@link TypedValue} for the possible + * dimension units + * @param size the desired size in the given units + * + * @attr ref android.R.styleable#TextView_autoSizeMinTextSize + * + * @see #getAutoSizeMinTextSize() + * @see #setAutoSizeMaxTextSize(int, float) + * @see #setAutoSizeStepGranularity(int, float) + */ + public void setAutoSizeMinTextSize(int unit, float size) { + if (supportsAutoSizeText()) { + mAutoSizeMinTextSizeInPx = (int) TypedValue.applyDimension( + unit, size, getResources().getDisplayMetrics()); + setupAutoSizeTextXY(); + } + } + + /** + * @return the current auto-size minimum text size in pixels (the default is 12sp). Note that + * if auto-size has not been configured this function returns {@code 0}. + * + * @see #setAutoSizeMinTextSize(int, float) + */ + public int getAutoSizeMinTextSize() { + return mAutoSizeMinTextSizeInPx; + } + + /** + * Sets the maximum text size to be used in conjunction with auto-size minimum text size and + * auto-size step granularity in order to build the set of text sizes the system uses to choose + * from when auto-sizing. + * + * @param unit the desired dimension unit. See {@link TypedValue} for the possible + * dimension units + * @param size the desired size in the given units + * + * @attr ref android.R.styleable#TextView_autoSizeMaxTextSize + * + * @see #getAutoSizeMaxTextSize() + * @see #setAutoSizeMinTextSize(int, float) + * @see #setAutoSizeStepGranularity(int, float) + */ + public void setAutoSizeMaxTextSize(int unit, float size) { + if (supportsAutoSizeText()) { + mAutoSizeMaxTextSizeInPx = (int) TypedValue.applyDimension( + unit, size, getResources().getDisplayMetrics()); + setupAutoSizeTextXY(); + } + } + + /** + * @return the current auto-size maximum text size in pixels (the default is 112sp). Note that + * if auto-size has not been configured this function returns {@code 0}. + * + * @see #setAutoSizeMaxTextSize(int, float) + */ + public int getAutoSizeMaxTextSize() { + return mAutoSizeMaxTextSizeInPx; + } + + private void setupAutoSizeTextXY() { + if (supportsAutoSizeText() && mAutoSizeTextType == AUTO_SIZE_TEXT_TYPE_XY) { + // Set valid defaults. + if (mAutoSizeMinTextSizeInPx <= 0) { + mAutoSizeMinTextSizeInPx = (int) TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_SP, + DEFAULT_AUTO_SIZE_MIN_TEXT_SIZE_IN_SP, + getResources().getDisplayMetrics()); } + if (mAutoSizeMaxTextSizeInPx <= 0) { + mAutoSizeMaxTextSizeInPx = (int) TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_SP, + DEFAULT_AUTO_SIZE_MAX_TEXT_SIZE_IN_SP, + getResources().getDisplayMetrics()); + } + + if (mAutoSizeStepGranularityInPx <= 0) { + mAutoSizeStepGranularityInPx = DEFAULT_AUTO_SIZE_GRANULARITY_IN_PX; + } + + // Validate. + if (mAutoSizeMaxTextSizeInPx <= mAutoSizeMinTextSizeInPx) { + throw new IllegalStateException("Maximum auto-size text size (" + + mAutoSizeMaxTextSizeInPx + "px) is less or equal to minimum auto-size " + + "text size (" + mAutoSizeMinTextSizeInPx + "px)"); + } + + // Calculate sizes to choose from based on the current auto-size configuration. + final int autoSizeValuesLength = (int) Math.ceil( + (mAutoSizeMaxTextSizeInPx - mAutoSizeMinTextSizeInPx) + / mAutoSizeStepGranularityInPx); + + mAutoSizeTextSizesInPx = new int[autoSizeValuesLength]; + int sizeToAdd = mAutoSizeMinTextSizeInPx; + for (int i = 0; i < autoSizeValuesLength; i++) { + mAutoSizeTextSizesInPx[i] = sizeToAdd; + sizeToAdd += mAutoSizeStepGranularityInPx; + } + + mNeedsAutoSizeText = true; + autoSizeText(); } } @@ -3030,9 +3208,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener /** * @return the size (in pixels) of the default text size in this TextView. - * - *

Note: if this TextView has mAutoSizeType set to {@link TextView#AUTO_SIZE_TYPE_XY} than - * this function returns the maximum text size for auto-sizing. */ @ViewDebug.ExportedProperty(category = "text") public float getTextSize() { @@ -3077,7 +3252,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** - * Set the default text size to a given unit and value. See {@link + * Set the default text size to a given unit and value. See {@link * TypedValue} for the possible dimension units. * *

Note: if this TextView has the auto-size feature enabled than this function is no-op. @@ -3113,7 +3288,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (mLayout != null) { // Do not auto-size right after setting the text size. - mNeedsAutoSize = false; + mNeedsAutoSizeText = false; nullLayouts(); requestLayout(); invalidate(); @@ -7612,13 +7787,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } if (isAutoSizeEnabled()) { - if (mNeedsAutoSize) { + if (mNeedsAutoSizeText) { // Call auto-size after the width and height have been calculated. autoSizeText(); } // Always try to auto-size if enabled. Functions that do not want to trigger auto-sizing // after the next measuring round should set this to false. - mNeedsAutoSize = true; + mNeedsAutoSizeText = true; } setMeasuredDimension(width, height); @@ -9377,7 +9552,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * auto-size. */ private boolean isAutoSizeEnabled() { - return supportsAutoSizeText() && mAutoSizeType != AUTO_SIZE_TYPE_NONE; + return supportsAutoSizeText() && mAutoSizeTextType != AUTO_SIZE_TEXT_TYPE_NONE; } /** diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index a5c1a94da47e2..dbca3b95cb825 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -4707,7 +4707,8 @@ screens with limited space for text. --> - +