Merge "AutoSize TextView (part 1) - minimal end-to-end"

This commit is contained in:
Andrei Stingaceanu
2016-11-24 14:06:08 +00:00
committed by Android (Google) Code Review
6 changed files with 215 additions and 0 deletions

View File

@@ -289,6 +289,10 @@ package android {
field public static final int autoLink = 16842928; // 0x10100b0
field public static final int autoMirrored = 16843754; // 0x10103ea
field public static final int autoRemoveFromRecents = 16843847; // 0x1010447
field public static final int autoSizeMinTextSize = 16844088; // 0x1010538
field public static final int autoSizeStepGranularity = 16844086; // 0x1010536
field public static final int autoSizeStepSizeSet = 16844087; // 0x1010537
field public static final int autoSizeText = 16844085; // 0x1010535
field public static final int autoStart = 16843445; // 0x10102b5
field public static final deprecated int autoText = 16843114; // 0x101016a
field public static final int autoUrlDetect = 16843404; // 0x101028c
@@ -48746,6 +48750,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
}
public static final class TextView.BufferType extends java.lang.Enum {

View File

@@ -396,6 +396,10 @@ package android {
field public static final int autoLink = 16842928; // 0x10100b0
field public static final int autoMirrored = 16843754; // 0x10103ea
field public static final int autoRemoveFromRecents = 16843847; // 0x1010447
field public static final int autoSizeMinTextSize = 16844088; // 0x1010538
field public static final int autoSizeStepGranularity = 16844086; // 0x1010536
field public static final int autoSizeStepSizeSet = 16844087; // 0x1010537
field public static final int autoSizeText = 16844085; // 0x1010535
field public static final int autoStart = 16843445; // 0x10102b5
field public static final deprecated int autoText = 16843114; // 0x101016a
field public static final int autoUrlDetect = 16843404; // 0x101028c
@@ -52267,6 +52271,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
}
public static final class TextView.BufferType extends java.lang.Enum {

View File

@@ -289,6 +289,10 @@ package android {
field public static final int autoLink = 16842928; // 0x10100b0
field public static final int autoMirrored = 16843754; // 0x10103ea
field public static final int autoRemoveFromRecents = 16843847; // 0x1010447
field public static final int autoSizeMinTextSize = 16844088; // 0x1010538
field public static final int autoSizeStepGranularity = 16844086; // 0x1010536
field public static final int autoSizeStepSizeSet = 16844087; // 0x1010537
field public static final int autoSizeText = 16844085; // 0x1010535
field public static final int autoStart = 16843445; // 0x10102b5
field public static final deprecated int autoText = 16843114; // 0x101016a
field public static final int autoUrlDetect = 16843404; // 0x101028c
@@ -49004,6 +49008,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
}
public static final class TextView.BufferType extends java.lang.Enum {

View File

@@ -159,6 +159,7 @@ import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Locale;
/**
@@ -617,6 +618,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private Rect mTempRect;
private long mLastScroll;
private Scroller mScroller;
private TextPaint mTempTextPaint;
private BoringLayout.Metrics mBoring, mHintBoring;
private BoringLayout mSavedLayout, mSavedHintLayout;
@@ -667,6 +669,20 @@ 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 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;
// Default value for the step size in pixels.
private static final int DEFAULT_AUTO_SIZE_GRANULARITY_IN_PX = 1;
// Contains the sorted set of desired text sizes in pixels to pick from when auto-sizing text.
private int[] mAutoSizeTextSizesInPx;
// Specifies if the current TextView needs to be auto-sized.
private boolean mNeedsTextAutoResize = false;
/**
* Kick-start the font cache for the zygote process (to pay the cost of
* initializing freetype for our default font only once).
@@ -869,6 +885,9 @@ 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());
a = theme.obtainStyledAttributes(
attrs, com.android.internal.R.styleable.TextView, defStyleAttr, defStyleRes);
@@ -1223,6 +1242,19 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
case com.android.internal.R.styleable.TextView_hyphenationFrequency:
mHyphenationFrequency = a.getInt(attr, Layout.HYPHENATION_FREQUENCY_NONE);
break;
case com.android.internal.R.styleable.TextView_autoSizeText:
mAutoSizeType = a.getInt(attr, AUTO_SIZE_TYPE_NONE);
break;
case com.android.internal.R.styleable.TextView_autoSizeStepGranularity:
autoSizeStepGranularityInPx = a.getDimensionPixelSize(
attr, DEFAULT_AUTO_SIZE_GRANULARITY_IN_PX);
break;
case com.android.internal.R.styleable.TextView_autoSizeMinTextSize:
autoSizeMinTextSize = a.getDimensionPixelSize(attr, autoSizeMinTextSize);
break;
}
}
a.recycle();
@@ -1500,6 +1532,43 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if (getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
}
// Setup auto-size.
if (mEditor == null) {
switch (mAutoSizeType) {
case AUTO_SIZE_TYPE_NONE:
// Nothing to do.
break;
case AUTO_SIZE_TYPE_XY:
// getTextSize() represents the maximum text size.
if (getTextSize() <= autoSizeMinTextSize) {
throw new IllegalStateException("Maximum text size is less then minimum "
+ "text size");
}
if (autoSizeStepGranularityInPx <= 0) {
throw new IllegalStateException("Unexpected zero or negative value for auto"
+ " size step granularity in pixels");
}
final int autoSizeValuesLength = (int) ((getTextSize() - autoSizeMinTextSize)
/ autoSizeStepGranularityInPx);
mAutoSizeTextSizesInPx = new int[autoSizeValuesLength];
int sizeToAdd = autoSizeMinTextSize;
for (int i = 0; i < autoSizeValuesLength; i++) {
mAutoSizeTextSizesInPx[i] = sizeToAdd;
sizeToAdd += autoSizeStepGranularityInPx;
}
Arrays.sort(mAutoSizeTextSizesInPx);
mNeedsTextAutoResize = true;
break;
default:
throw new IllegalArgumentException(
"Unknown autoSizeText type: " + mAutoSizeType);
}
}
}
private int[] parseDimensionArray(TypedArray dimens) {
@@ -2954,6 +3023,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
/**
* @return the size (in pixels) of the default text size in this TextView.
*
* <p>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() {
@@ -2986,6 +3058,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* pixel" units. This size is adjusted based on the current density and
* user font size preference.
*
* <p>Note: if this TextView has mAutoSizeType set to {@link TextView#AUTO_SIZE_TYPE_XY} than
* this function sets the maximum text size for auto-sizing.
*
* @param size The scaled pixel size.
*
* @attr ref android.R.styleable#TextView_textSize
@@ -2999,6 +3074,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* Set the default text size to a given unit and value. See {@link
* TypedValue} for the possible dimension units.
*
* <p>Note: if this TextView has mAutoSizeType set to {@link TextView#AUTO_SIZE_TYPE_XY} than
* this function sets the maximum text size for auto-sizing.
*
* @param unit The desired dimension unit.
* @param size The desired size in the given units.
*
@@ -7446,9 +7524,107 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
scrollTo(0, 0);
}
if (mNeedsTextAutoResize) {
// Call auto-size after the width and height have been calculated.
autoSizeText();
}
setMeasuredDimension(width, height);
}
/**
* Automatically computes and sets the text size.
*/
private void autoSizeText() {
synchronized (TEMP_RECTF) {
TEMP_RECTF.setEmpty();
final int maxWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
final int maxHeight = getMeasuredHeight() - getPaddingBottom() - getPaddingTop();
if (maxWidth <= 0 || maxHeight <= 0) {
return;
}
TEMP_RECTF.right = maxWidth;
TEMP_RECTF.bottom = maxHeight;
final float textSize = findLargestTextSizeWhichFits(TEMP_RECTF);
setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
mNeedsTextAutoResize = false;
}
}
/**
* Performs a binary search to find the largest text size that will still fit within the size
* available to this view.
*/
private int findLargestTextSizeWhichFits(RectF availableSpace) {
final int sizesCount = mAutoSizeTextSizesInPx.length;
if (sizesCount == 0) {
throw new IllegalStateException("No available text sizes to choose from.");
}
int bestSizeIndex = 0;
int lowIndex = bestSizeIndex + 1;
int highIndex = sizesCount - 1;
int sizeToTryIndex;
while (lowIndex <= highIndex) {
sizeToTryIndex = (lowIndex + highIndex) / 2;
if (suggestedSizeFitsInSpace(mAutoSizeTextSizesInPx[sizeToTryIndex], availableSpace)) {
bestSizeIndex = lowIndex;
lowIndex = sizeToTryIndex + 1;
} else {
highIndex = sizeToTryIndex - 1;
bestSizeIndex = highIndex;
}
}
return mAutoSizeTextSizesInPx[bestSizeIndex];
}
private boolean suggestedSizeFitsInSpace(int suggestedSizeInPx, RectF availableSpace) {
final CharSequence text = getText();
final int maxLines = getMaxLines();
if (mTempTextPaint == null) {
mTempTextPaint = new TextPaint();
} else {
mTempTextPaint.reset();
}
mTempTextPaint.set(getPaint());
mTempTextPaint.setTextSize(suggestedSizeInPx);
if ((mLayout instanceof BoringLayout) && BoringLayout.isBoring(
text, mTempTextPaint, getTextDirectionHeuristic(), mBoring) != null) {
return mTempTextPaint.getFontSpacing() + getPaddingTop() + getPaddingBottom()
<= availableSpace.bottom
&& mTempTextPaint.measureText(text, 0, text.length())
+ getPaddingLeft() + getPaddingRight() <= availableSpace.right;
} else {
StaticLayout.Builder layoutBuilder = StaticLayout.Builder.obtain(text, 0, text.length(),
mTempTextPaint, getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
layoutBuilder.setAlignment(getLayoutAlignment());
layoutBuilder.setLineSpacing(getLineSpacingExtra(), getLineSpacingMultiplier());
layoutBuilder.setIncludePad(true);
StaticLayout layout = layoutBuilder.build();
// Lines overflow.
if (maxLines != -1 && layout.getLineCount() > maxLines) {
return false;
}
// Width overflow.
if (layout.getWidth() > availableSpace.right) {
return false;
}
// Height overflow.
if (layout.getHeight() > availableSpace.bottom) {
return false;
}
}
return true;
}
private int getDesiredHeight() {
return Math.max(
getDesiredHeight(mLayout, true),

View File

@@ -4624,6 +4624,23 @@
screens with limited space for text. -->
<enum name="full" value="2" />
</attr>
<!-- Specify the type of auto-size. -->
<attr name="autoSizeText" format="enum">
<!-- No auto-sizing (default). -->
<enum name="none" value="0" />
<!-- Uniform horizontal and vertical scaling. -->
<enum name="xy" value="1" />
</attr>
<!-- Specify the auto-size step size if <code>autoSizeText</code> is set to
<code>xy</code>. The default is 1px. Overwrites
<code>autoSizeStepSizeSet</code> if set. -->
<attr name="autoSizeStepGranularity" format="dimension" />
<!-- Array of dimensions to be used in conjunction with
<code>autoSizeText</code> set to <code>xy</code>. Overwrites
<code>autoSizeStepGranularity</code> if set. -->
<attr name="autoSizeStepSizeSet"/>
<!-- The minimum text size constraint to be used when auto-sizing text -->
<attr name="autoSizeMinTextSize" format="dimension" />
</declare-styleable>
<declare-styleable name="TextViewAppearance">
<!-- Base text color, typeface, size, and style. -->

View File

@@ -2760,6 +2760,10 @@
<public name="font" />
<public name="fontWeight" />
<public name="tooltip" />
<public name="autoSizeText" />
<public name="autoSizeStepGranularity" />
<public name="autoSizeStepSizeSet" />
<public name="autoSizeMinTextSize" />
</public-group>
<public-group type="style" first-id="0x010302e0">