-
Notifications
You must be signed in to change notification settings - Fork 686
Added height fitting option (by KonradJanica) #69
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 2 commits
108edd3
61020f1
3366f58
32a1e95
9a51582
47e92b4
a0f9e0a
f6ef8d7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -61,6 +61,7 @@ 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 heightToFit = false; | ||
| if (attrs != null) { | ||
| Context context = view.getContext(); | ||
| int minTextSize = (int) helper.getMinTextSize(); | ||
|
|
@@ -72,14 +73,16 @@ public static AutofitHelper create(TextView view, AttributeSet attrs, int defSty | |
| defStyle, | ||
| 0); | ||
| sizeToFit = ta.getBoolean(R.styleable.AutofitTextView_sizeToFit, sizeToFit); | ||
| heightToFit = ta.getBoolean(R.styleable.AutofitTextView_heightToFit, heightToFit); | ||
| 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.setHeightFitting(heightToFit); | ||
| helper.setEnabled(sizeToFit); | ||
|
|
||
| return helper; | ||
|
|
@@ -89,7 +92,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) { | ||
| if (maxLines <= 0 || maxLines == Integer.MAX_VALUE) { | ||
| // Don't auto-size since there's no limit on lines. | ||
| return; | ||
|
|
@@ -128,19 +131,49 @@ private static void autofit(TextView view, TextPaint paint, float minTextSize, f | |
| displayMetrics); | ||
| } | ||
|
|
||
| if (size < minTextSize) { | ||
| size = minTextSize; | ||
| if (mIsHeightFitting) { | ||
| int targetHeight = view.getHeight() - view.getPaddingTop() - view.getPaddingBottom(); | ||
| if (targetHeight <= 0) { | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it seems like we could invert this conditional so that we don't have to duplicate minTextSize check and setTextSize calls |
||
| if (size < minTextSize) { | ||
| size = minTextSize; | ||
| } | ||
| view.setTextSize(TypedValue.COMPLEX_UNIT_PX, size); | ||
| return; | ||
| } | ||
|
|
||
| float textHeight = getTextHeight(text, paint, targetWidth, size); | ||
| float heightRatio = targetHeight / textHeight; | ||
| float newSize = size * heightRatio; | ||
| if (newSize < size) { | ||
| size = newSize; | ||
| } | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this method of calculating a fitting textSize to match the height is interesting and different than how we calculate fitting textSize to match the width... are we guaranteed that text height changes proportionally with textSize? if we are, could we use a similar method for width? if not, should we use a similar procedure?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm actually not super familiar with StaticLayout; that's just what KonradJanica used to calculate height. However, I think this article on The Mathematics of Golden Ratio Typography sheds some light on the mathematics between text height, text width, and text size. |
||
|
|
||
| if (size < minTextSize) { | ||
| size = minTextSize; | ||
| } | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we need to pop this conditional out of it's nested conditional or else we'll break minTextSize for non-height-fitting modes |
||
| } | ||
|
|
||
| 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) { | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: revert |
||
| float mid = (low + high) / 2.0f; | ||
| int lineCount = 1; | ||
| StaticLayout layout = null; | ||
|
|
@@ -196,7 +229,7 @@ else if (lineCount < maxLines) { | |
| } | ||
|
|
||
| private static int getLineCount(CharSequence text, TextPaint paint, float size, float width, | ||
| DisplayMetrics displayMetrics) { | ||
| DisplayMetrics displayMetrics) { | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: revert |
||
| paint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, size, | ||
| displayMetrics)); | ||
| StaticLayout layout = new StaticLayout(text, paint, (int)width, | ||
|
|
@@ -234,6 +267,7 @@ else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { | |
|
|
||
| private boolean mEnabled; | ||
| private boolean mIsAutofitting; | ||
| private static boolean mIsHeightFitting; | ||
|
|
||
| private ArrayList<OnTextSizeChangeListener> mListeners; | ||
|
|
||
|
|
@@ -418,7 +452,8 @@ 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; | ||
|
|
@@ -446,6 +481,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 isHeightFitting() { | ||
| return mIsHeightFitting; | ||
| } | ||
|
|
||
| /** | ||
| * Sets the state of automatically resizing by text fitting in height. | ||
| * Calls an autofit if it is already enabled. | ||
| * @param enabled The state to update the height fitting member | ||
| */ | ||
| public AutofitHelper setHeightFitting(boolean enabled) { | ||
| mIsHeightFitting = enabled; | ||
| // Fit if required | ||
| setEnabled(mEnabled); | ||
| return this; | ||
| } | ||
|
|
||
| /** | ||
| * Returns the original text size of the View. | ||
| * | ||
|
|
@@ -535,7 +591,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) { | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: revert |
||
| autofit(); | ||
| } | ||
| } | ||
|
|
@@ -551,4 +607,4 @@ public interface OnTextSizeChangeListener { | |
| */ | ||
| public void onTextSizeChange(float textSize, float oldTextSize); | ||
| } | ||
| } | ||
| } | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: revert |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,6 +22,7 @@ | |
| public class AutofitLayout extends FrameLayout { | ||
|
|
||
| private boolean mEnabled; | ||
| private boolean mIsHeightFitting; | ||
| private float mMinTextSize; | ||
| private float mPrecision; | ||
| private WeakHashMap<View, AutofitHelper> mHelpers = new WeakHashMap<View, AutofitHelper>(); | ||
|
|
@@ -43,6 +44,7 @@ public AutofitLayout(Context context, AttributeSet attrs, int defStyle) { | |
|
|
||
| private void init(Context context, AttributeSet attrs, int defStyle) { | ||
| boolean sizeToFit = true; | ||
| boolean heightToFit = false; | ||
| int minTextSize = -1; | ||
| float precision = -1; | ||
|
|
||
|
|
@@ -53,13 +55,15 @@ private void init(Context context, AttributeSet attrs, int defStyle) { | |
| defStyle, | ||
| 0); | ||
| sizeToFit = ta.getBoolean(R.styleable.AutofitTextView_sizeToFit, sizeToFit); | ||
| heightToFit = ta.getBoolean(R.styleable.AutofitTextView_heightToFit, heightToFit); | ||
| minTextSize = ta.getDimensionPixelSize(R.styleable.AutofitTextView_minTextSize, | ||
| minTextSize); | ||
| precision = ta.getFloat(R.styleable.AutofitTextView_precision, precision); | ||
| ta.recycle(); | ||
| } | ||
|
|
||
| mEnabled = sizeToFit; | ||
| mIsHeightFitting = heightToFit; | ||
| mMinTextSize = minTextSize; | ||
| mPrecision = precision; | ||
| } | ||
|
|
@@ -68,8 +72,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.setHeightFitting(mIsHeightFitting); | ||
| helper.setEnabled(mEnabled); | ||
| if (mPrecision > 0) { | ||
| helper.setPrecision(mPrecision); | ||
| } | ||
|
|
@@ -92,4 +97,4 @@ public AutofitHelper getAutofitHelper(TextView textView) { | |
| public AutofitHelper getAutofitHelper(int index) { | ||
| return mHelpers.get(getChildAt(index)); | ||
| } | ||
| } | ||
| } | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: revert |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -103,6 +103,29 @@ public void setSizeToFit(boolean sizeToFit) { | |
| mHelper.setEnabled(sizeToFit); | ||
| } | ||
|
|
||
| /** | ||
| * Returns whether or not the text will be automatically re-sized to fit its height. | ||
| */ | ||
| public boolean isHeightFitting() { | ||
| return mHelper.isHeightFitting(); | ||
| } | ||
|
|
||
| /** | ||
| * Sets the property of this field (isHeightFitting), to automatically resize the text to fit | ||
| * its height. | ||
| */ | ||
| public void setHeightFitting() { | ||
| setHeightFitting(true); | ||
| } | ||
|
|
||
| /** | ||
| * Enables automatic text resizing to fit the textview height | ||
| * @param isEnabled If true, the text will automatically be re-sized to fit its height | ||
| */ | ||
| public void setHeightFitting(boolean isEnabled) { | ||
| mHelper.setHeightFitting(isEnabled); | ||
| } | ||
|
|
||
| /** | ||
| * Returns the maximum size (in pixels) of the text in this View. | ||
| */ | ||
|
|
@@ -189,4 +212,4 @@ public void setPrecision(float precision) { | |
| public void onTextSizeChange(float textSize, float oldTextSize) { | ||
| // do nothing | ||
| } | ||
| } | ||
| } | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: revert |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: revert