summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorRichard MacGregor <rmacgregor@cyngn.com>2016-05-18 08:44:34 -0700
committerRichard MacGregor <rmacgregor@cyngn.com>2016-05-20 09:20:02 -0700
commit746e8d40e4ada8d9a90c63c78c13b40fe63ccb3f (patch)
treec35b20c4369dd7991234f2cd74eaf7af1b30fe78 /src
parentdd9bd6b69af7be12375d4015a0585be2a5b8da67 (diff)
downloadandroid_packages_apps_Dialer-746e8d40e4ada8d9a90c63c78c13b40fe63ccb3f.tar.gz
android_packages_apps_Dialer-746e8d40e4ada8d9a90c63c78c13b40fe63ccb3f.tar.bz2
android_packages_apps_Dialer-746e8d40e4ada8d9a90c63c78c13b40fe63ccb3f.zip
Autosize hint text
Autosize hint text string to fit within the bounds of - 5 max lines - 16sp max font size - 12sp min font size This also removes a space between hint icon and description, which caused action text to hide behind t9 dialer in some languages. Ticket CD-631 Change-Id: I077da31789bba82535856f07d674b6dd98bc1f64
Diffstat (limited to 'src')
-rw-r--r--src/me/grantland/widget/AutofitHelper.java572
-rw-r--r--src/me/grantland/widget/AutofitLayout.java113
-rw-r--r--src/me/grantland/widget/AutofitTextView.java208
3 files changed, 893 insertions, 0 deletions
diff --git a/src/me/grantland/widget/AutofitHelper.java b/src/me/grantland/widget/AutofitHelper.java
new file mode 100644
index 000000000..6763cbe0c
--- /dev/null
+++ b/src/me/grantland/widget/AutofitHelper.java
@@ -0,0 +1,572 @@
+/*
+ * Copyright (C) 2014 Grantland Chew
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package me.grantland.widget;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.os.Build;
+import android.text.Editable;
+import android.text.Layout;
+import android.text.StaticLayout;
+import android.text.TextPaint;
+import android.text.TextWatcher;
+import android.text.method.SingleLineTransformationMethod;
+import android.text.method.TransformationMethod;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.View;
+import android.widget.TextView;
+
+import com.android.dialer.R;
+
+import java.util.ArrayList;
+
+/**
+ * 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_minTextSize
+ * @attr ref R.styleable.AutofitTextView_precision
+ */
+public class AutofitHelper {
+
+ private static final String TAG = "AutoFitTextHelper";
+ private static final boolean SPEW = false;
+
+ // Minimum size of the text in pixels
+ private static final int DEFAULT_MIN_TEXT_SIZE = 8; //sp
+ // How precise we want to be when reaching the target textWidth size
+ private static final float DEFAULT_PRECISION = 0.5f;
+
+ /**
+ * Creates a new instance of {@code AutofitHelper} that wraps a {@link TextView} and enables
+ * automatically sizing the text to fit.
+ */
+ public static AutofitHelper create(TextView view) {
+ return create(view, null, 0);
+ }
+
+ /**
+ * Creates a new instance of {@code AutofitHelper} that wraps a {@link TextView} and enables
+ * automatically sizing the text to fit.
+ */
+ public static AutofitHelper create(TextView view, AttributeSet attrs) {
+ return create(view, attrs, 0);
+ }
+
+ /**
+ * Creates a new instance of {@code AutofitHelper} that wraps a {@link TextView} and enables
+ * automatically sizing the text to fit.
+ */
+ public static AutofitHelper create(TextView view, AttributeSet attrs, int defStyle) {
+ AutofitHelper helper = new AutofitHelper(view);
+ boolean sizeToFit = true;
+ if (attrs != null) {
+ Context context = view.getContext();
+ int minTextSize = (int) helper.getMinTextSize();
+ float precision = helper.getPrecision();
+
+ TypedArray ta = context.obtainStyledAttributes(
+ attrs,
+ R.styleable.AutofitTextView,
+ defStyle,
+ 0);
+ sizeToFit = ta.getBoolean(R.styleable.AutofitTextView_sizeToFit, sizeToFit);
+ 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);
+ }
+ helper.setEnabled(sizeToFit);
+
+ return helper;
+ }
+
+ /**
+ * 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) {
+ if (maxLines <= 0 || maxLines == Integer.MAX_VALUE) {
+ // Don't auto-size since there's no limit on lines.
+ return;
+ }
+
+ int targetWidth = view.getWidth() - view.getPaddingLeft() - view.getPaddingRight();
+ if (targetWidth <= 0) {
+ return;
+ }
+
+ CharSequence text = view.getText();
+ TransformationMethod method = view.getTransformationMethod();
+ if (method != null) {
+ text = method.getTransformation(text, view);
+ }
+
+ Context context = view.getContext();
+ Resources r = Resources.getSystem();
+ DisplayMetrics displayMetrics;
+
+ float size = maxTextSize;
+ float high = size;
+ float low = 0;
+
+ if (context != null) {
+ r = context.getResources();
+ }
+ displayMetrics = r.getDisplayMetrics();
+
+ paint.set(view.getPaint());
+ paint.setTextSize(size);
+
+ if ((maxLines == 1 && paint.measureText(text, 0, text.length()) > targetWidth)
+ || getLineCount(text, paint, size, targetWidth, displayMetrics) > maxLines) {
+ size = getAutofitTextSize(text, paint, targetWidth, maxLines, low, high, precision,
+ displayMetrics);
+ }
+
+ if (size < minTextSize) {
+ size = minTextSize;
+ }
+
+ view.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
+ }
+
+ /**
+ * 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 mid = (low + high) / 2.0f;
+ int lineCount = 1;
+ StaticLayout layout = null;
+
+ paint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, mid,
+ displayMetrics));
+
+ if (maxLines != 1) {
+ layout = new StaticLayout(text, paint, (int)targetWidth, Layout.Alignment.ALIGN_NORMAL,
+ 1.0f, 0.0f, true);
+ lineCount = layout.getLineCount();
+ }
+
+ if (SPEW) Log.d(TAG, "low=" + low + " high=" + high + " mid=" + mid +
+ " target=" + targetWidth + " maxLines=" + maxLines + " lineCount=" + lineCount);
+
+ if (lineCount > maxLines) {
+ // For the case that `text` has more newline characters than `maxLines`.
+ if ((high - low) < precision) {
+ return low;
+ }
+ return getAutofitTextSize(text, paint, targetWidth, maxLines, low, mid, precision,
+ displayMetrics);
+ }
+ else if (lineCount < maxLines) {
+ return getAutofitTextSize(text, paint, targetWidth, maxLines, mid, high, precision,
+ displayMetrics);
+ }
+ else {
+ float maxLineWidth = 0;
+ if (maxLines == 1) {
+ maxLineWidth = paint.measureText(text, 0, text.length());
+ } else {
+ for (int i = 0; i < lineCount; i++) {
+ if (layout.getLineWidth(i) > maxLineWidth) {
+ maxLineWidth = layout.getLineWidth(i);
+ }
+ }
+ }
+
+ if ((high - low) < precision) {
+ return low;
+ } else if (maxLineWidth > targetWidth) {
+ return getAutofitTextSize(text, paint, targetWidth, maxLines, low, mid, precision,
+ displayMetrics);
+ } else if (maxLineWidth < targetWidth) {
+ return getAutofitTextSize(text, paint, targetWidth, maxLines, mid, high, precision,
+ displayMetrics);
+ } else {
+ return mid;
+ }
+ }
+ }
+
+ private static int getLineCount(CharSequence text, TextPaint paint, float size, float width,
+ DisplayMetrics displayMetrics) {
+ paint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, size,
+ displayMetrics));
+ StaticLayout layout = new StaticLayout(text, paint, (int)width,
+ Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, true);
+ return layout.getLineCount();
+ }
+
+ private static int getMaxLines(TextView view) {
+ int maxLines = -1; // No limit (Integer.MAX_VALUE also means no limit)
+
+ TransformationMethod method = view.getTransformationMethod();
+ if (method != null && method instanceof SingleLineTransformationMethod) {
+ maxLines = 1;
+ }
+ else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+ // setMaxLines() and getMaxLines() are only available on android-16+
+ maxLines = view.getMaxLines();
+ }
+
+ return maxLines;
+ }
+
+ // Attributes
+ private TextView mTextView;
+ private TextPaint mPaint;
+ /**
+ * Original textSize of the TextView.
+ */
+ private float mTextSize;
+
+ private int mMaxLines;
+ private float mMinTextSize;
+ private float mMaxTextSize;
+ private float mPrecision;
+
+ private boolean mEnabled;
+ private boolean mIsAutofitting;
+
+ private ArrayList<OnTextSizeChangeListener> mListeners;
+
+ private TextWatcher mTextWatcher = new AutofitTextWatcher();
+
+ private View.OnLayoutChangeListener mOnLayoutChangeListener =
+ new AutofitOnLayoutChangeListener();
+
+ private AutofitHelper(TextView view) {
+ final Context context = view.getContext();
+ float scaledDensity = context.getResources().getDisplayMetrics().scaledDensity;
+
+ mTextView = view;
+ mPaint = new TextPaint();
+ setRawTextSize(view.getTextSize());
+
+ mMaxLines = getMaxLines(view);
+ mMinTextSize = scaledDensity * DEFAULT_MIN_TEXT_SIZE;
+ mMaxTextSize = mTextSize;
+ mPrecision = DEFAULT_PRECISION;
+ }
+
+ /**
+ * Adds an {@link OnTextSizeChangeListener} to the list of those whose methods are called
+ * whenever the {@link TextView}'s {@code textSize} changes.
+ */
+ public AutofitHelper addOnTextSizeChangeListener(OnTextSizeChangeListener listener) {
+ if (mListeners == null) {
+ mListeners = new ArrayList<OnTextSizeChangeListener>();
+ }
+ mListeners.add(listener);
+ return this;
+ }
+
+ /**
+ * Removes the specified {@link OnTextSizeChangeListener} from the list of those whose methods
+ * are called whenever the {@link TextView}'s {@code textSize} changes.
+ */
+ public AutofitHelper removeOnTextSizeChangeListener(OnTextSizeChangeListener listener) {
+ if (mListeners != null) {
+ mListeners.remove(listener);
+ }
+ return this;
+ }
+
+ /**
+ * Returns the amount of precision used to calculate the correct text size to fit within its
+ * bounds.
+ */
+ public float getPrecision() {
+ return mPrecision;
+ }
+
+ /**
+ * Set the amount of precision used to calculate the correct text size to fit within its
+ * bounds. Lower precision is more precise and takes more time.
+ *
+ * @param precision The amount of precision.
+ */
+ public AutofitHelper setPrecision(float precision) {
+ if (mPrecision != precision) {
+ mPrecision = precision;
+
+ autofit();
+ }
+ return this;
+ }
+
+ /**
+ * Returns the minimum size (in pixels) of the text.
+ */
+ public float getMinTextSize() {
+ return mMinTextSize;
+ }
+
+ /**
+ * Set the minimum text size to the given value, interpreted as "scaled pixel" units. This size
+ * is adjusted based on the current density and user font size preference.
+ *
+ * @param size The scaled pixel size.
+ *
+ * @attr ref me.grantland.R.styleable#AutofitTextView_minTextSize
+ */
+ public AutofitHelper setMinTextSize(float size) {
+ return setMinTextSize(TypedValue.COMPLEX_UNIT_SP, size);
+ }
+
+ /**
+ * 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 size The desired size in the given units.
+ *
+ * @attr ref me.grantland.R.styleable#AutofitTextView_minTextSize
+ */
+ public AutofitHelper setMinTextSize(int unit, float size) {
+ Context context = mTextView.getContext();
+ Resources r = Resources.getSystem();
+
+ if (context != null) {
+ r = context.getResources();
+ }
+
+ setRawMinTextSize(TypedValue.applyDimension(unit, size, r.getDisplayMetrics()));
+ return this;
+ }
+
+ private void setRawMinTextSize(float size) {
+ if (size != mMinTextSize) {
+ mMinTextSize = size;
+
+ autofit();
+ }
+ }
+
+ /**
+ * Returns the maximum size (in pixels) of the text.
+ */
+ public float getMaxTextSize() {
+ return mMaxTextSize;
+ }
+
+ /**
+ * Set the maximum text size to the given value, interpreted as "scaled pixel" units. This size
+ * 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 AutofitHelper setMaxTextSize(float size) {
+ return setMaxTextSize(TypedValue.COMPLEX_UNIT_SP, size);
+ }
+
+ /**
+ * Set the maximum text size to a given unit and value. See TypedValue for the possible
+ * dimension units.
+ *
+ * @param unit The desired dimension unit.
+ * @param size The desired size in the given units.
+ *
+ * @attr ref android.R.styleable#TextView_textSize
+ */
+ public AutofitHelper setMaxTextSize(int unit, float size) {
+ Context context = mTextView.getContext();
+ Resources r = Resources.getSystem();
+
+ if (context != null) {
+ r = context.getResources();
+ }
+
+ setRawMaxTextSize(TypedValue.applyDimension(unit, size, r.getDisplayMetrics()));
+ return this;
+ }
+
+ private void setRawMaxTextSize(float size) {
+ if (size != mMaxTextSize) {
+ mMaxTextSize = size;
+
+ autofit();
+ }
+ }
+
+ /**
+ * @see TextView#getMaxLines()
+ */
+ public int getMaxLines() {
+ return mMaxLines;
+ }
+
+ /**
+ * @see TextView#setMaxLines(int)
+ */
+ public AutofitHelper setMaxLines(int lines) {
+ if (mMaxLines != lines) {
+ mMaxLines = lines;
+
+ autofit();
+ }
+ return this;
+ }
+
+ /**
+ * Returns whether or not automatically resizing text is enabled.
+ */
+ public boolean isEnabled() {
+ return mEnabled;
+ }
+
+ /**
+ * Set the enabled state of automatically resizing text.
+ */
+ public AutofitHelper setEnabled(boolean enabled) {
+ if (mEnabled != enabled) {
+ mEnabled = enabled;
+
+ if (enabled) {
+ mTextView.addTextChangedListener(mTextWatcher);
+ mTextView.addOnLayoutChangeListener(mOnLayoutChangeListener);
+
+ autofit();
+ } else {
+ mTextView.removeTextChangedListener(mTextWatcher);
+ mTextView.removeOnLayoutChangeListener(mOnLayoutChangeListener);
+
+ mTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSize);
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Returns the original text size of the View.
+ *
+ * @see TextView#getTextSize()
+ */
+ public float getTextSize() {
+ return mTextSize;
+ }
+
+ /**
+ * Set the original text size of the View.
+ *
+ * @see TextView#setTextSize(float)
+ */
+ public void setTextSize(float size) {
+ setTextSize(TypedValue.COMPLEX_UNIT_SP, size);
+ }
+
+ /**
+ * Set the original text size of the View.
+ *
+ * @see TextView#setTextSize(int, float)
+ */
+ public void setTextSize(int unit, float size) {
+ if (mIsAutofitting) {
+ // We don't want to update the TextView's actual textSize while we're autofitting
+ // since it'd get set to the autofitTextSize
+ return;
+ }
+ Context context = mTextView.getContext();
+ Resources r = Resources.getSystem();
+
+ if (context != null) {
+ r = context.getResources();
+ }
+
+ setRawTextSize(TypedValue.applyDimension(unit, size, r.getDisplayMetrics()));
+ }
+
+ private void setRawTextSize(float size) {
+ if (mTextSize != size) {
+ mTextSize = size;
+ }
+ }
+
+ private void autofit() {
+ float oldTextSize = mTextView.getTextSize();
+ float textSize;
+
+ mIsAutofitting = true;
+ autofit(mTextView, mPaint, mMinTextSize, mMaxTextSize, mMaxLines, mPrecision);
+ mIsAutofitting = false;
+
+ textSize = mTextView.getTextSize();
+ if (textSize != oldTextSize) {
+ sendTextSizeChange(textSize, oldTextSize);
+ }
+ }
+
+ private void sendTextSizeChange(float textSize, float oldTextSize) {
+ if (mListeners == null) {
+ return;
+ }
+
+ for (OnTextSizeChangeListener listener : mListeners) {
+ listener.onTextSizeChange(textSize, oldTextSize);
+ }
+ }
+
+ private class AutofitTextWatcher implements TextWatcher {
+ @Override
+ public void beforeTextChanged(CharSequence charSequence, int start, int count, int after) {
+ // do nothing
+ }
+
+ @Override
+ public void onTextChanged(CharSequence charSequence, int start, int before, int count) {
+ autofit();
+ }
+
+ @Override
+ public void afterTextChanged(Editable editable) {
+ // do nothing
+ }
+ }
+
+ 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) {
+ autofit();
+ }
+ }
+
+ /**
+ * When an object of a type is attached to an {@code AutofitHelper}, its methods will be called
+ * when the {@code textSize} is changed.
+ */
+ public interface OnTextSizeChangeListener {
+ /**
+ * This method is called to notify you that the size of the text has changed to
+ * {@code textSize} from {@code oldTextSize}.
+ */
+ public void onTextSizeChange(float textSize, float oldTextSize);
+ }
+}
diff --git a/src/me/grantland/widget/AutofitLayout.java b/src/me/grantland/widget/AutofitLayout.java
new file mode 100644
index 000000000..2be1d8809
--- /dev/null
+++ b/src/me/grantland/widget/AutofitLayout.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2014 Grantland Chew
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package me.grantland.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+
+import com.android.dialer.R;
+
+import java.util.WeakHashMap;
+
+/**
+ * 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_minTextSize
+ * @attr ref R.styleable.AutofitTextView_precision
+ */
+public class AutofitLayout extends FrameLayout {
+
+ private boolean mEnabled;
+ private float mMinTextSize;
+ private float mPrecision;
+ private WeakHashMap<View, AutofitHelper> mHelpers = new WeakHashMap<View, AutofitHelper>();
+
+ public AutofitLayout(Context context) {
+ super(context);
+ init(context, null, 0);
+ }
+
+ public AutofitLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(context, attrs, 0);
+ }
+
+ public AutofitLayout(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ init(context, attrs, defStyle);
+ }
+
+ private void init(Context context, AttributeSet attrs, int defStyle) {
+ boolean sizeToFit = true;
+ int minTextSize = -1;
+ float precision = -1;
+
+ if (attrs != null) {
+ TypedArray ta = context.obtainStyledAttributes(
+ attrs,
+ R.styleable.AutofitTextView,
+ defStyle,
+ 0);
+ sizeToFit = ta.getBoolean(R.styleable.AutofitTextView_sizeToFit, sizeToFit);
+ minTextSize = ta.getDimensionPixelSize(R.styleable.AutofitTextView_minTextSize,
+ minTextSize);
+ precision = ta.getFloat(R.styleable.AutofitTextView_precision, precision);
+ ta.recycle();
+ }
+
+ mEnabled = sizeToFit;
+ mMinTextSize = minTextSize;
+ mPrecision = precision;
+ }
+
+ @Override
+ 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);
+ if (mPrecision > 0) {
+ helper.setPrecision(mPrecision);
+ }
+ if (mMinTextSize > 0) {
+ helper.setMinTextSize(TypedValue.COMPLEX_UNIT_PX, mMinTextSize);
+ }
+ mHelpers.put(textView, helper);
+ }
+
+ /**
+ * Returns the {@link AutofitHelper} for this child View.
+ */
+ public AutofitHelper getAutofitHelper(TextView textView) {
+ return mHelpers.get(textView);
+ }
+
+ /**
+ * Returns the {@link AutofitHelper} for this child View.
+ */
+ public AutofitHelper getAutofitHelper(int index) {
+ return mHelpers.get(getChildAt(index));
+ }
+}
diff --git a/src/me/grantland/widget/AutofitTextView.java b/src/me/grantland/widget/AutofitTextView.java
new file mode 100644
index 000000000..f21a9c1bf
--- /dev/null
+++ b/src/me/grantland/widget/AutofitTextView.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2014 Grantland Chew
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package me.grantland.widget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.widget.TextView;
+
+/**
+ * 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_minTextSize
+ * @attr ref R.styleable.AutofitTextView_precision
+ */
+public class AutofitTextView extends TextView implements AutofitHelper.OnTextSizeChangeListener {
+
+ private AutofitHelper mHelper;
+
+ public AutofitTextView(Context context) {
+ super(context);
+ init(context, null, 0);
+ }
+
+ public AutofitTextView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(context, attrs, 0);
+ }
+
+ public AutofitTextView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ init(context, attrs, defStyle);
+ }
+
+ private void init(Context context, AttributeSet attrs, int defStyle) {
+ mHelper = AutofitHelper.create(this, attrs, defStyle)
+ .addOnTextSizeChangeListener(this);
+ }
+
+ // Getters and Setters
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setTextSize(int unit, float size) {
+ super.setTextSize(unit, size);
+ if (mHelper != null) {
+ mHelper.setTextSize(unit, size);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setLines(int lines) {
+ super.setLines(lines);
+ if (mHelper != null) {
+ mHelper.setMaxLines(lines);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setMaxLines(int maxLines) {
+ super.setMaxLines(maxLines);
+ if (mHelper != null) {
+ mHelper.setMaxLines(maxLines);
+ }
+ }
+
+ /**
+ * Returns the {@link AutofitHelper} for this View.
+ */
+ public AutofitHelper getAutofitHelper() {
+ return mHelper;
+ }
+
+ /**
+ * Returns whether or not the text will be automatically re-sized to fit its constraints.
+ */
+ public boolean isSizeToFit() {
+ return mHelper.isEnabled();
+ }
+
+ /**
+ * Sets the property of this field (sizeToFit), to automatically resize the text to fit its
+ * constraints.
+ */
+ public void setSizeToFit() {
+ setSizeToFit(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
+ */
+ public void setSizeToFit(boolean sizeToFit) {
+ mHelper.setEnabled(sizeToFit);
+ }
+
+ /**
+ * Returns the maximum size (in pixels) of the text in this View.
+ */
+ public float getMaxTextSize() {
+ return mHelper.getMaxTextSize();
+ }
+
+ /**
+ * Set the maximum text size to the given value, interpreted as "scaled pixel" units. This size
+ * 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) {
+ mHelper.setMaxTextSize(size);
+ }
+
+ /**
+ * Set the maximum text size to a given unit and value. See TypedValue for the possible
+ * dimension units.
+ *
+ * @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) {
+ mHelper.setMaxTextSize(unit, size);
+ }
+
+ /**
+ * Returns the minimum size (in pixels) of the text in this View.
+ */
+ public float getMinTextSize() {
+ return mHelper.getMinTextSize();
+ }
+
+ /**
+ * Set the minimum text size to the given value, interpreted as "scaled pixel" units. This size
+ * 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) {
+ mHelper.setMinTextSize(TypedValue.COMPLEX_UNIT_SP, 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 minSize The desired size in the given units.
+ *
+ * @attr ref me.grantland.R.styleable#AutofitTextView_minTextSize
+ */
+ public void setMinTextSize(int unit, float minSize) {
+ mHelper.setMinTextSize(unit, minSize);
+ }
+
+ /**
+ * Returns the amount of precision used to calculate the correct text size to fit within its
+ * bounds.
+ */
+ public float getPrecision() {
+ return mHelper.getPrecision();
+ }
+
+ /**
+ * Set the amount of precision used to calculate the correct text size to fit within its
+ * bounds. Lower precision is more precise and takes more time.
+ *
+ * @param precision The amount of precision.
+ */
+ public void setPrecision(float precision) {
+ mHelper.setPrecision(precision);
+ }
+
+ @Override
+ public void onTextSizeChange(float textSize, float oldTextSize) {
+ // do nothing
+ }
+}