Skip to content

Commit

Permalink
Fixed grantland#69
Browse files Browse the repository at this point in the history
  • Loading branch information
konifar committed Apr 30, 2018
1 parent 5c565aa commit 63622fa
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 36 deletions.
79 changes: 67 additions & 12 deletions library/src/main/java/me/grantland/widget/AutofitHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
* 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
*/
Expand Down Expand Up @@ -61,6 +63,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();
Expand All @@ -71,7 +75,10 @@ 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);
Expand All @@ -80,7 +87,8 @@ public static AutofitHelper create(TextView view, AttributeSet attrs, int defSty
helper.setMinTextSize(TypedValue.COMPLEX_UNIT_PX, minTextSize)
.setPrecision(precision);
}
helper.setEnabled(sizeToFit);
helper.setAutofitWidthEnabled(autofitWidthEnabled);
helper.setAutofitHeightEnabled(autofitHeightEnabled);

return helper;
}
Expand All @@ -89,7 +97,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;
Expand Down Expand Up @@ -128,13 +136,37 @@ 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;
}

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.
*/
Expand Down Expand Up @@ -232,8 +264,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<OnTextSizeChangeListener> mListeners;

Expand Down Expand Up @@ -418,20 +451,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);

Expand All @@ -446,6 +480,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.
*
Expand Down Expand Up @@ -496,7 +551,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();
Expand Down
22 changes: 15 additions & 7 deletions library/src/main/java/me/grantland/widget/AutofitLayout.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<View, AutofitHelper> mHelpers = new WeakHashMap<View, AutofitHelper>();
Expand All @@ -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;

Expand All @@ -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;
}
Expand All @@ -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);
}
Expand Down
45 changes: 32 additions & 13 deletions library/src/main/java/me/grantland/widget/AutofitTextView.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,28 +81,51 @@ 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);
}

/**
* Returns the maximum size (in pixels) of the text in this View.
*/
Expand All @@ -115,7 +138,6 @@ public float getMaxTextSize() {
* is adjusted based on the current density and user font size preference.
*
* @param size The scaled pixel size.
*
* @attr ref android.R.styleable#TextView_textSize
*/
public void setMaxTextSize(float size) {
Expand All @@ -128,7 +150,6 @@ public void setMaxTextSize(float size) {
*
* @param unit The desired dimension unit.
* @param size The desired size in the given units.
*
* @attr ref android.R.styleable#TextView_textSize
*/
public void setMaxTextSize(int unit, float size) {
Expand All @@ -147,7 +168,6 @@ public float getMinTextSize() {
* is adjusted based on the current density and user font size preference.
*
* @param minSize The scaled pixel size.
*
* @attr ref me.grantland.R.styleable#AutofitTextView_minTextSize
*/
public void setMinTextSize(int minSize) {
Expand All @@ -158,9 +178,8 @@ public void setMinTextSize(int minSize) {
* Set the minimum text size to a given unit and value. See TypedValue for the possible
* dimension units.
*
* @param unit The desired dimension unit.
* @param unit The desired dimension unit.
* @param minSize The desired size in the given units.
*
* @attr ref me.grantland.R.styleable#AutofitTextView_minTextSize
*/
public void setMinTextSize(int unit, float minSize) {
Expand Down
10 changes: 6 additions & 4 deletions library/src/main/res/values/attrs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
<declare-styleable name="AutofitTextView">
<!-- Minimum size of the text. -->
<attr name="minTextSize" format="dimension" />
<!-- Amount of precision used to calculate the correct text size to fit within its
bounds. Lower precision is more precise and takes more time. -->
<!-- Amount of precision used to calculate the correct text size to fit within its bounds.
Lower precision is more precise and takes more time. -->
<attr name="precision" format="float" />
<!-- Defines whether to automatically resize text to fit to the view's bounds. -->
<attr name="sizeToFit" format="boolean" />
<!-- Defines whether to automatically resize text to fit to the view's width bounds. -->
<attr name="autofitWidthEnabled" format="boolean" />
<!-- Defines whether to automatically resize text to fit to the view's height bounds. -->
<attr name="autofitHeightEnabled" format="boolean" />
</declare-styleable>
</resources>

0 comments on commit 63622fa

Please sign in to comment.