diff --git a/library/src/main/java/me/grantland/widget/AutofitHelper.java b/library/src/main/java/me/grantland/widget/AutofitHelper.java index 90e0b0e..75f2ec9 100644 --- a/library/src/main/java/me/grantland/widget/AutofitHelper.java +++ b/library/src/main/java/me/grantland/widget/AutofitHelper.java @@ -24,7 +24,8 @@ * A helper class to enable automatically resizing {@link TextView}`s {@code textSize} to fit * within its bounds. * - * @attr ref R.styleable.AutofitTextView_sizeToFit + * @attr ref R.styleable.AutofitTextView_autofitWidthEnabled + * @attr ref R.styleable.AutofitTextView_autofitHeightEnabled * @attr ref R.styleable.AutofitTextView_minTextSize * @attr ref R.styleable.AutofitTextView_precision */ @@ -60,7 +61,8 @@ public static AutofitHelper create(TextView view, AttributeSet attrs) { */ public static AutofitHelper create(TextView view, AttributeSet attrs, int defStyle) { AutofitHelper helper = new AutofitHelper(view); - boolean sizeToFit = true; + boolean autofitWidthEnabled = true; + boolean autofitHeightEnabled = false; if (attrs != null) { Context context = view.getContext(); int minTextSize = (int) helper.getMinTextSize(); @@ -71,16 +73,20 @@ public static AutofitHelper create(TextView view, AttributeSet attrs, int defSty R.styleable.AutofitTextView, defStyle, 0); - sizeToFit = ta.getBoolean(R.styleable.AutofitTextView_sizeToFit, sizeToFit); + autofitWidthEnabled = ta.getBoolean(R.styleable.AutofitTextView_autofitWidthEnabled, + autofitWidthEnabled); + autofitHeightEnabled = ta.getBoolean(R.styleable.AutofitTextView_autofitHeightEnabled, + autofitHeightEnabled); minTextSize = ta.getDimensionPixelSize(R.styleable.AutofitTextView_minTextSize, minTextSize); precision = ta.getFloat(R.styleable.AutofitTextView_precision, precision); ta.recycle(); helper.setMinTextSize(TypedValue.COMPLEX_UNIT_PX, minTextSize) - .setPrecision(precision); + .setPrecision(precision); } - helper.setEnabled(sizeToFit); + helper.setAutofitWidthEnabled(autofitWidthEnabled); + helper.setAutofitHeightEnabled(autofitHeightEnabled); return helper; } @@ -89,7 +95,7 @@ public static AutofitHelper create(TextView view, AttributeSet attrs, int defSty * Re-sizes the textSize of the TextView so that the text fits within the bounds of the View. */ private static void autofit(TextView view, TextPaint paint, float minTextSize, float maxTextSize, - int maxLines, float precision) { + int maxLines, float precision, boolean isAutofitWidthEnabled, boolean isAutofitHeightEnabled) { if (maxLines <= 0 || maxLines == Integer.MAX_VALUE) { // Don't auto-size since there's no limit on lines. return; @@ -128,6 +134,18 @@ private static void autofit(TextView view, TextPaint paint, float minTextSize, f displayMetrics); } + if (isAutofitHeightEnabled) { + int targetHeight = view.getHeight() - view.getPaddingTop() - view.getPaddingBottom(); + if (targetHeight > 0) { + float textHeight = getTextHeight(text, paint, targetWidth, size); + float heightRatio = targetHeight / textHeight; + float newSize = size * heightRatio; + if (newSize < size) { + size = newSize; + } + } + } + if (size < minTextSize) { size = minTextSize; } @@ -135,12 +153,24 @@ private static void autofit(TextView view, TextPaint paint, float minTextSize, f view.setTextSize(TypedValue.COMPLEX_UNIT_PX, size); } + /** + * Try to fit the text with current size to a static layout to calculate height needed + * by that text size. + * @note Can be put in a loop where text size is gradually decreased etc. + * @return float The height size required by the text. + */ + private static float getTextHeight(CharSequence text, TextPaint paint, int width, float textSize) { + StaticLayout textHeightAdjuster = new StaticLayout(text, paint, width, + Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, true); + return textHeightAdjuster.getHeight(); + } + /** * Recursive binary search to find the best size for the text. */ private static float getAutofitTextSize(CharSequence text, TextPaint paint, - float targetWidth, int maxLines, float low, float high, float precision, - DisplayMetrics displayMetrics) { + float targetWidth, int maxLines, float low, float high, float precision, + DisplayMetrics displayMetrics) { float mid = (low + high) / 2.0f; int lineCount = 1; StaticLayout layout = null; @@ -196,7 +226,7 @@ else if (lineCount < maxLines) { } private static int getLineCount(CharSequence text, TextPaint paint, float size, float width, - DisplayMetrics displayMetrics) { + DisplayMetrics displayMetrics) { paint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, size, displayMetrics)); StaticLayout layout = new StaticLayout(text, paint, (int)width, @@ -232,8 +262,9 @@ else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { private float mMaxTextSize; private float mPrecision; - private boolean mEnabled; + private boolean mIsAutofitWidthEnabled; private boolean mIsAutofitting; + private boolean mIsAutofitHeightEnabled; private ArrayList mListeners; @@ -418,20 +449,21 @@ public AutofitHelper setMaxLines(int lines) { } /** - * Returns whether or not automatically resizing text is enabled. + * Returns whether or not automatically resizing text + * by width and number of lines is enabled. */ - public boolean isEnabled() { - return mEnabled; + public boolean isAutofitWidthEnabled() { + return mIsAutofitWidthEnabled; } /** * Set the enabled state of automatically resizing text. */ - public AutofitHelper setEnabled(boolean enabled) { - if (mEnabled != enabled) { - mEnabled = enabled; + public AutofitHelper setAutofitWidthEnabled(boolean autofitWidthEnabled) { + if (mIsAutofitWidthEnabled != autofitWidthEnabled) { + mIsAutofitWidthEnabled = autofitWidthEnabled; - if (enabled) { + if (autofitWidthEnabled) { mTextView.addTextChangedListener(mTextWatcher); mTextView.addOnLayoutChangeListener(mOnLayoutChangeListener); @@ -446,6 +478,27 @@ public AutofitHelper setEnabled(boolean enabled) { return this; } + /** + * Returns whether or not automatically resizing text + * by height is enabled. + * @return boolean True when height scaling is on. + */ + public boolean isAutofitHeightEnabled() { + return mIsAutofitHeightEnabled; + } + + /** + * Sets the state of automatically resizing by text fitting in height. + * Calls an autofit if it is already enabled. + * @param autofitHeightEnabled The state to update the height fitting member + */ + public AutofitHelper setAutofitHeightEnabled(boolean autofitHeightEnabled) { + mIsAutofitHeightEnabled = autofitHeightEnabled; + // Fit if required + setAutofitWidthEnabled(mIsAutofitWidthEnabled); + return this; + } + /** * Returns the original text size of the View. * @@ -496,7 +549,7 @@ private void autofit() { float textSize; mIsAutofitting = true; - autofit(mTextView, mPaint, mMinTextSize, mMaxTextSize, mMaxLines, mPrecision); + autofit(mTextView, mPaint, mMinTextSize, mMaxTextSize, mMaxLines, mPrecision, mIsAutofitWidthEnabled, mIsAutofitHeightEnabled); mIsAutofitting = false; textSize = mTextView.getTextSize(); @@ -535,7 +588,7 @@ public void afterTextChanged(Editable editable) { private class AutofitOnLayoutChangeListener implements View.OnLayoutChangeListener { @Override public void onLayoutChange(View view, int left, int top, int right, int bottom, - int oldLeft, int oldTop, int oldRight, int oldBottom) { + int oldLeft, int oldTop, int oldRight, int oldBottom) { autofit(); } } @@ -551,4 +604,4 @@ public interface OnTextSizeChangeListener { */ public void onTextSizeChange(float textSize, float oldTextSize); } -} +} \ No newline at end of file diff --git a/library/src/main/java/me/grantland/widget/AutofitLayout.java b/library/src/main/java/me/grantland/widget/AutofitLayout.java index 7620e70..094d18a 100644 --- a/library/src/main/java/me/grantland/widget/AutofitLayout.java +++ b/library/src/main/java/me/grantland/widget/AutofitLayout.java @@ -15,13 +15,15 @@ * A {@link ViewGroup} that re-sizes the text of it's children to be no larger than the width of the * view. * - * @attr ref R.styleable.AutofitTextView_sizeToFit + * @attr ref R.styleable.AutofitTextView_autofitWidthEnabled + * @attr ref R.styleable.AutofitTextView_autofitHeightEnabled * @attr ref R.styleable.AutofitTextView_minTextSize * @attr ref R.styleable.AutofitTextView_precision */ public class AutofitLayout extends FrameLayout { - private boolean mEnabled; + private boolean mIsAutofitWidthEnabled; + private boolean mIsAutofitHeightEnabled; private float mMinTextSize; private float mPrecision; private WeakHashMap mHelpers = new WeakHashMap(); @@ -42,7 +44,8 @@ public AutofitLayout(Context context, AttributeSet attrs, int defStyle) { } private void init(Context context, AttributeSet attrs, int defStyle) { - boolean sizeToFit = true; + boolean autofitWidthEnabled = true; + boolean autofitHeightEnabled = false; int minTextSize = -1; float precision = -1; @@ -52,14 +55,18 @@ private void init(Context context, AttributeSet attrs, int defStyle) { R.styleable.AutofitTextView, defStyle, 0); - sizeToFit = ta.getBoolean(R.styleable.AutofitTextView_sizeToFit, sizeToFit); + autofitWidthEnabled = ta.getBoolean(R.styleable.AutofitTextView_autofitWidthEnabled, + autofitWidthEnabled); + autofitHeightEnabled = ta.getBoolean(R.styleable.AutofitTextView_autofitHeightEnabled, + autofitHeightEnabled); minTextSize = ta.getDimensionPixelSize(R.styleable.AutofitTextView_minTextSize, minTextSize); precision = ta.getFloat(R.styleable.AutofitTextView_precision, precision); ta.recycle(); } - mEnabled = sizeToFit; + mIsAutofitWidthEnabled = autofitWidthEnabled; + mIsAutofitHeightEnabled = autofitHeightEnabled; mMinTextSize = minTextSize; mPrecision = precision; } @@ -68,8 +75,9 @@ private void init(Context context, AttributeSet attrs, int defStyle) { public void addView(View child, int index, ViewGroup.LayoutParams params) { super.addView(child, index, params); TextView textView = (TextView) child; - AutofitHelper helper = AutofitHelper.create(textView) - .setEnabled(mEnabled); + AutofitHelper helper = AutofitHelper.create(textView); + helper.setAutofitWidthEnabled(mIsAutofitWidthEnabled); + helper.setAutofitHeightEnabled(mIsAutofitHeightEnabled); if (mPrecision > 0) { helper.setPrecision(mPrecision); } @@ -92,4 +100,4 @@ public AutofitHelper getAutofitHelper(TextView textView) { public AutofitHelper getAutofitHelper(int index) { return mHelpers.get(getChildAt(index)); } -} +} \ No newline at end of file diff --git a/library/src/main/java/me/grantland/widget/AutofitTextView.java b/library/src/main/java/me/grantland/widget/AutofitTextView.java index f5cd9ba..1a23e17 100644 --- a/library/src/main/java/me/grantland/widget/AutofitTextView.java +++ b/library/src/main/java/me/grantland/widget/AutofitTextView.java @@ -8,7 +8,8 @@ /** * A {@link TextView} that re-sizes its text to be no larger than the width of the view. * - * @attr ref R.styleable.AutofitTextView_sizeToFit + * @attr ref R.styleable.AutofitTextView_autofitWidthEnabled + * @attr ref R.styleable.AutofitTextView_autofitHeightEnabled * @attr ref R.styleable.AutofitTextView_minTextSize * @attr ref R.styleable.AutofitTextView_precision */ @@ -81,26 +82,49 @@ public AutofitHelper getAutofitHelper() { /** * Returns whether or not the text will be automatically re-sized to fit its constraints. */ - public boolean isSizeToFit() { - return mHelper.isEnabled(); + public boolean isAutofitWidthEnabled() { + return mHelper.isAutofitWidthEnabled(); } /** - * Sets the property of this field (sizeToFit), to automatically resize the text to fit its + * Sets the property of this field (autofitWidthEnabled), to automatically resize the text to fit its * constraints. */ - public void setSizeToFit() { - setSizeToFit(true); + public void setAutofitWidthEnabled() { + setAutofitWidthEnabled(true); } /** * If true, the text will automatically be re-sized to fit its constraints; if false, it will * act like a normal TextView. * - * @param sizeToFit + * @param autofitWidthEnabled */ - public void setSizeToFit(boolean sizeToFit) { - mHelper.setEnabled(sizeToFit); + public void setAutofitWidthEnabled(boolean autofitWidthEnabled) { + mHelper.setAutofitWidthEnabled(autofitWidthEnabled); + } + + /** + * Returns whether or not the text will be automatically re-sized to fit its height. + */ + public boolean isAutofitHeightEnabled() { + return mHelper.isAutofitHeightEnabled(); + } + + /** + * Sets the property of this field (autofitHeightEnabled), to automatically resize the text to fit + * its height. + */ + public void setAutofitHeightEnabled() { + setAutofitHeightEnabled(true); + } + + /** + * Enables automatic text resizing to fit the textview height + * @param autofitHeightEnabled If true, the text will automatically be re-sized to fit its height + */ + public void setAutofitHeightEnabled(boolean autofitHeightEnabled) { + mHelper.setAutofitHeightEnabled(autofitHeightEnabled); } /** @@ -189,4 +213,4 @@ public void setPrecision(float precision) { public void onTextSizeChange(float textSize, float oldTextSize) { // do nothing } -} +} \ No newline at end of file diff --git a/library/src/main/res/values/attrs.xml b/library/src/main/res/values/attrs.xml index 15e90fe..e432549 100644 --- a/library/src/main/res/values/attrs.xml +++ b/library/src/main/res/values/attrs.xml @@ -1,13 +1,14 @@ - - - - - - + + + + + + + \ No newline at end of file