diff options
author | 2017-01-27 15:31:19 +0000 | |
---|---|---|
committer | 2017-02-08 12:48:21 +0000 | |
commit | 9fe208fe6b0c0aef797a2d3757f50b88ecd86a0c (patch) | |
tree | bb9e5f807e2d2ed49a1c3f977c92cc9941208338 | |
parent | 155c3a88ac6e6b690fb3324054abfcc8095e9cc3 (diff) |
AutoSize TextView (part 8) - APIs for predefined sizes
* getter/setter for predefined sizes
* reads and configures from XML at construction time
* fix for an ugly bug where the sizes were missing an
entry in certain cases, e.g: min = 10; max = 20;
step = 2 would have produced [10, 12, 14, 16, 18]
instead of [10, 12, 14, 16, 18, 20]
* fix using getHeight()/getWidth() instead of
untrusted getMeasuredHeight()/getMeasuredWidth()
and move the auto-sizing triggering to
onLayout() instead of onMeasure() (while manually
testing discovered missing or extra pixels and
sometimes resizing being skipped - it's all fixed
now)
* fix using deceiving getTotalPaddingBottom()/...Top()
and replaced with getExtendedPaddingBottom()/..Top()
(getTotal... was removing the whitespace height but
auto-size needs to know about it so it can fill the
space)
Bug: 32221168
Test: attached in the same topic
run cts-dev -m CtsWidgetTestCases -t \
android.widget.cts.TextViewTest
Change-Id: Id5a31d0d32b2b4082af45b4bd65af8cb85bdc92e
-rw-r--r-- | api/current.txt | 2 | ||||
-rw-r--r-- | api/system-current.txt | 2 | ||||
-rw-r--r-- | api/test-current.txt | 2 | ||||
-rw-r--r-- | core/java/android/widget/TextView.java | 212 |
4 files changed, 168 insertions, 50 deletions
diff --git a/api/current.txt b/api/current.txt index d7721819e404..15a6ec7d7d32 100644 --- a/api/current.txt +++ b/api/current.txt @@ -50383,6 +50383,7 @@ package android.widget { method public int getAutoSizeMaxTextSize(); method public int getAutoSizeMinTextSize(); method public int getAutoSizeStepGranularity(); + method public int[] getAutoSizeTextPresetSizes(); method public int getAutoSizeTextType(); method public int getBreakStrategy(); method public int getCompoundDrawablePadding(); @@ -50496,6 +50497,7 @@ package android.widget { method public void setAutoSizeMaxTextSize(int, float); method public void setAutoSizeMinTextSize(int, float); method public void setAutoSizeStepGranularity(int, float); + method public void setAutoSizeTextPresetSizes(int[]); method public void setAutoSizeTextType(int); method public void setBreakStrategy(int); method public void setCompoundDrawablePadding(int); diff --git a/api/system-current.txt b/api/system-current.txt index 6affa7fea40f..d24dc4212b7a 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -54154,6 +54154,7 @@ package android.widget { method public int getAutoSizeMaxTextSize(); method public int getAutoSizeMinTextSize(); method public int getAutoSizeStepGranularity(); + method public int[] getAutoSizeTextPresetSizes(); method public int getAutoSizeTextType(); method public int getBreakStrategy(); method public int getCompoundDrawablePadding(); @@ -54267,6 +54268,7 @@ package android.widget { method public void setAutoSizeMaxTextSize(int, float); method public void setAutoSizeMinTextSize(int, float); method public void setAutoSizeStepGranularity(int, float); + method public void setAutoSizeTextPresetSizes(int[]); method public void setAutoSizeTextType(int); method public void setBreakStrategy(int); method public void setCompoundDrawablePadding(int); diff --git a/api/test-current.txt b/api/test-current.txt index bbb7f898d201..0ac218c3cb2a 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -50703,6 +50703,7 @@ package android.widget { method public int getAutoSizeMaxTextSize(); method public int getAutoSizeMinTextSize(); method public int getAutoSizeStepGranularity(); + method public int[] getAutoSizeTextPresetSizes(); method public int getAutoSizeTextType(); method public int getBreakStrategy(); method public int getCompoundDrawablePadding(); @@ -50816,6 +50817,7 @@ package android.widget { method public void setAutoSizeMaxTextSize(int, float); method public void setAutoSizeMinTextSize(int, float); method public void setAutoSizeStepGranularity(int, float); + method public void setAutoSizeTextPresetSizes(int[]); method public void setAutoSizeTextType(int); method public void setBreakStrategy(int); method public void setCompoundDrawablePadding(int); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index a5ff29119dc9..31bb6b65cb85 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -109,6 +109,7 @@ import android.text.style.URLSpan; import android.text.style.UpdateAppearance; import android.text.util.Linkify; import android.util.AttributeSet; +import android.util.IntArray; import android.util.Log; import android.util.TypedValue; import android.view.AccessibilityIterators.TextSegmentIterator; @@ -159,6 +160,8 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.util.FastMath; import com.android.internal.widget.EditableInputConnection; +import libcore.util.EmptyArray; + import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; @@ -166,6 +169,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.Arrays; import java.util.Locale; /** @@ -262,6 +266,7 @@ import java.util.Locale; * @attr ref android.R.styleable#TextView_autoSizeMinTextSize * @attr ref android.R.styleable#TextView_autoSizeMaxTextSize * @attr ref android.R.styleable#TextView_autoSizeStepGranularity + * @attr ref android.R.styleable#TextView_autoSizeStepSizeSet */ @RemoteView public class TextView extends View implements ViewTreeObserver.OnPreDrawListener { @@ -708,8 +713,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener 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; + // Contains a (specified or computed) distinct sorted set of text sizes in pixels to pick from + // when auto-sizing text. + private int[] mAutoSizeTextSizesInPx = EmptyArray.INT; + // Specifies whether auto-size should use the provided auto size steps set or if it should + // build the steps set using mAutoSizeMinTextSizeInPx, mAutoSizeMaxTextSizeInPx and + // mAutoSizeStepGranularityInPx. + private boolean mHasPredefinedAutoSizeSet = false; // Watcher used to notify changes to auto-fill manager. private AutoFillChangeWatcher mAutoFillChangeWatcher; @@ -1301,8 +1311,19 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener case com.android.internal.R.styleable.TextView_autoSizeMaxTextSize: mAutoSizeMaxTextSizeInPx = a.getDimensionPixelSize(attr, 0); break; + + case com.android.internal.R.styleable.TextView_autoSizeStepSizeSet: + final int autoSizeStepSizeArrayResId = a.getResourceId(attr, 0); + if (autoSizeStepSizeArrayResId > 0) { + final TypedArray autoSizePreDefTextSizes = a.getResources() + .obtainTypedArray(autoSizeStepSizeArrayResId); + setupAutoSizeStepSizeSet(autoSizePreDefTextSizes); + autoSizePreDefTextSizes.recycle(); + } + break; } } + a.recycle(); BufferType bufferType = BufferType.EDITABLE; @@ -1599,7 +1620,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mAutoSizeMinTextSizeInPx = 0; mAutoSizeMaxTextSizeInPx = 0; mAutoSizeStepGranularityInPx = 0; - mAutoSizeTextSizesInPx = null; + mAutoSizeTextSizesInPx = EmptyArray.INT; mNeedsAutoSizeText = false; } break; @@ -1651,6 +1672,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (supportsAutoSizeText()) { mAutoSizeStepGranularityInPx = (int) TypedValue.applyDimension( unit, size, getResources().getDisplayMetrics()); + mHasPredefinedAutoSizeSet = false; setupAutoSizeTextXY(); } } @@ -1683,6 +1705,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (supportsAutoSizeText()) { mAutoSizeMinTextSizeInPx = (int) TypedValue.applyDimension( unit, size, getResources().getDisplayMetrics()); + mHasPredefinedAutoSizeSet = false; setupAutoSizeTextXY(); } } @@ -1716,6 +1739,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (supportsAutoSizeText()) { mAutoSizeMaxTextSizeInPx = (int) TypedValue.applyDimension( unit, size, getResources().getDisplayMetrics()); + mHasPredefinedAutoSizeSet = false; setupAutoSizeTextXY(); } } @@ -1730,44 +1754,137 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener 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()); + /** + * Sets a predefined array of sizes to be used when auto-sizing. + * + * <ul>Note: + * <li>when <code>presetSizes</code> is not empty then the auto-size algorithm will use the + * values provided here instead of calculating the values based on min, max and step size. Also + * the values will be de-duplicated, sorted and negative or zero values will be removed. + * <li>when <code>presetSizes</code> is empty then the auto-size algorithm will use the min, max + * and step size to build the set of available sizes to choose from. Note that if no values have + * been provided for any of min, max or step size then defaults will be used. + * </ul> + * + * @param presetSizes an {@code int} array of sizes in pixels + * + * @attr ref android.R.styleable#TextView_autoSizeStepSizeSet + * + * @see #getAutoSizeTextPresetSizes() + */ + public void setAutoSizeTextPresetSizes(@NonNull int[] presetSizes) { + if (supportsAutoSizeText()) { + if (presetSizes.length > 0) { + mAutoSizeTextSizesInPx = cleanupAutoSizePredefinedSizes(presetSizes); + final int sizesLength = mAutoSizeTextSizesInPx.length; + mHasPredefinedAutoSizeSet = sizesLength > 0; + if (mHasPredefinedAutoSizeSet) { + mAutoSizeMinTextSizeInPx = mAutoSizeTextSizesInPx[0]; + mAutoSizeMaxTextSizeInPx = mAutoSizeTextSizesInPx[sizesLength - 1]; + mAutoSizeStepGranularityInPx = 0; + } + } else { + mHasPredefinedAutoSizeSet = false; } + setupAutoSizeTextXY(); + } + } - if (mAutoSizeMaxTextSizeInPx <= 0) { - mAutoSizeMaxTextSizeInPx = (int) TypedValue.applyDimension( - TypedValue.COMPLEX_UNIT_SP, - DEFAULT_AUTO_SIZE_MAX_TEXT_SIZE_IN_SP, - getResources().getDisplayMetrics()); - } + /** + * @return the current auto-size {@code int} sizes array (in pixels). + * + * @see #setAutoSizeTextPresetSizes(int[]) + * @see #setAutoSizeMinTextSize(int, float) + * @see #setAutoSizeMaxTextSize(int, float) + * @see #setAutoSizeStepGranularity(int, float) + */ + public int[] getAutoSizeTextPresetSizes() { + return mAutoSizeTextSizesInPx; + } + + private void setupAutoSizeStepSizeSet(TypedArray textSizes) { + final int textSizesLength = textSizes.length(); + final int[] parsedSizes = new int[textSizesLength]; - if (mAutoSizeStepGranularityInPx <= 0) { - mAutoSizeStepGranularityInPx = DEFAULT_AUTO_SIZE_GRANULARITY_IN_PX; + if (textSizesLength > 0) { + for (int i = 0; i < textSizesLength; i++) { + parsedSizes[i] = textSizes.getDimensionPixelSize(i, -1); } + mAutoSizeTextSizesInPx = cleanupAutoSizePredefinedSizes(parsedSizes); + mHasPredefinedAutoSizeSet = mAutoSizeTextSizesInPx.length > 0; + } + } - // 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)"); + // Returns distinct sorted positive values. + private int[] cleanupAutoSizePredefinedSizes(int[] predefinedValues) { + final int predefinedValuesLength = predefinedValues.length; + if (predefinedValuesLength == 0) { + return predefinedValues; + } + Arrays.sort(predefinedValues); + + final IntArray uniqueValidSizes = new IntArray(); + for (int i = 0; i < predefinedValuesLength; i++) { + final int currentPredefinedValue = predefinedValues[i]; + + if (currentPredefinedValue > 0 + && uniqueValidSizes.binarySearch(currentPredefinedValue) < 0) { + uniqueValidSizes.add(currentPredefinedValue); } + } - // Calculate sizes to choose from based on the current auto-size configuration. - final int autoSizeValuesLength = (int) Math.ceil( - (mAutoSizeMaxTextSizeInPx - mAutoSizeMinTextSizeInPx) - / mAutoSizeStepGranularityInPx); + return predefinedValuesLength == uniqueValidSizes.size() + ? predefinedValues + : uniqueValidSizes.toArray(); + } + + private void setupAutoSizeTextXY() { + if (supportsAutoSizeText() && mAutoSizeTextType == AUTO_SIZE_TEXT_TYPE_XY) { + // Calculate the sizes set based on minimum size, maximum size and step size if we do + // not have a predefined set of sizes or if the current sizes array is empty. + if (!mHasPredefinedAutoSizeSet || mAutoSizeTextSizesInPx.length == 0) { + // 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; + } - mAutoSizeTextSizesInPx = new int[autoSizeValuesLength]; - int sizeToAdd = mAutoSizeMinTextSizeInPx; - for (int i = 0; i < autoSizeValuesLength; i++) { - mAutoSizeTextSizesInPx[i] = sizeToAdd; - sizeToAdd += mAutoSizeStepGranularityInPx; + // 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. + int autoSizeValuesLength = (int) Math.ceil( + (mAutoSizeMaxTextSizeInPx - mAutoSizeMinTextSizeInPx) + / (float) mAutoSizeStepGranularityInPx); + // Also reserve a slot for the max size if it fits. + if ((mAutoSizeMaxTextSizeInPx - mAutoSizeMinTextSizeInPx) + % mAutoSizeStepGranularityInPx == 0) { + autoSizeValuesLength++; + } + mAutoSizeTextSizesInPx = new int[autoSizeValuesLength]; + int sizeToAdd = mAutoSizeMinTextSizeInPx; + for (int i = 0; i < autoSizeValuesLength; i++) { + mAutoSizeTextSizesInPx[i] = sizeToAdd; + sizeToAdd += mAutoSizeStepGranularityInPx; + } } mNeedsAutoSizeText = true; @@ -7815,16 +7932,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener scrollTo(0, 0); } - if (isAutoSizeEnabled()) { - 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. - mNeedsAutoSizeText = true; - } - setMeasuredDimension(width, height); } @@ -7832,8 +7939,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * Automatically computes and sets the text size. */ private void autoSizeText() { - final int maxWidth = getMeasuredWidth() - getTotalPaddingLeft() - getTotalPaddingRight(); - final int maxHeight = getMeasuredHeight() - getTotalPaddingBottom() - getTotalPaddingTop(); + final int maxWidth = getWidth() - getTotalPaddingLeft() - getTotalPaddingRight(); + final int maxHeight = getHeight() - getExtendedPaddingBottom() - getExtendedPaddingTop(); if (maxWidth <= 0 || maxHeight <= 0) { return; @@ -7907,11 +8014,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return false; } - // Width overflow. - if (layout.getWidth() > availableSpace.right) { - return false; - } - // Height overflow. if (layout.getHeight() > availableSpace.bottom) { return false; @@ -8079,6 +8181,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mDeferScroll = -1; bringPointIntoView(Math.min(curs, mText.length())); } + + if (isAutoSizeEnabled()) { + 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 layout round should set this to false. + mNeedsAutoSizeText = true; + } } private boolean isShowingHint() { |