Merge "AutoSize TextView (part 1) - minimal end-to-end"
This commit is contained in:
committed by
Android (Google) Code Review
commit
595e83395d
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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. -->
|
||||
|
||||
@@ -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">
|
||||
|
||||
Reference in New Issue
Block a user