Skip to content

Instantly share code, notes, and snippets.

@haroldolivieri
Last active May 3, 2016 23:45
Show Gist options
  • Save haroldolivieri/282831e2fef2cae3c5b8 to your computer and use it in GitHub Desktop.
Save haroldolivieri/282831e2fef2cae3c5b8 to your computer and use it in GitHub Desktop.
A resizable textview with animation that depends on screen width and decreases in relation to stepTextSize attribute
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<declare-styleable name="ResizableTextView">
<attr name="minTextSize" format="dimension" />
<attr name="maxTextSize" format="dimension" />
<attr name="stepTextSize" format="dimension" />
</declare-styleable>
</resources>
<ResizableTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="bottom|right"
android:enabled="false"
android:textAlignment="textEnd"
android:maxLines="1"
minTextSize="36sp"
maxTextSize="64sp"
stepTextSize="8sp"/>
public class ResizableTextView extends TextView {
private final float mMaximumTextSize;
private final float mMinimumTextSize;
private final float mStepTextSize;
private final Paint mTempPaint = new TextPaint();
private final Rect mTempRect = new Rect();
private int mWidthConstraint = -1;
private OnTextSizeChangeListener mOnTextSizeChangeListener;
public ResizableTextView(Context context) {
this(context, null);
}
public ResizableTextView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ResizableTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
final TypedArray a = context.obtainStyledAttributes(
attrs, R.styleable.ResizableTextView, defStyle, 0);
mMaximumTextSize = a.getDimension(
R.styleable.ResizableTextView_maxTextSize, getTextSize());
mMinimumTextSize = a.getDimension(
R.styleable.ResizableTextView_minTextSize, getTextSize());
mStepTextSize = a.getDimension(R.styleable.ResizableTextView_stepTextSize,
(mMaximumTextSize - mMinimumTextSize) / 3);
a.recycle();
if (isFocusable()) {
setMovementMethod(ScrollingMovementMethod.getInstance());
}
setTextSize(TypedValue.COMPLEX_UNIT_PX, mMaximumTextSize);
setMinHeight(getLineHeight() + getCompoundPaddingBottom() + getCompoundPaddingTop());
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWidthConstraint =
MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight();
setTextSize(TypedValue.COMPLEX_UNIT_PX, getVariableTextSize(getText().toString()));
}
@Override
protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
super.onTextChanged(text, start, lengthBefore, lengthAfter);
setTextSize(TypedValue.COMPLEX_UNIT_PX, getVariableTextSize(text.toString()));
}
@Override
public void setTextSize(int unit, float size) {
final float oldTextSize = getTextSize();
super.setTextSize(unit, size);
if (mOnTextSizeChangeListener != null && getTextSize() != oldTextSize) {
mOnTextSizeChangeListener.onTextSizeChanged(this, oldTextSize);
}
}
public void setOnTextSizeChangeListener(OnTextSizeChangeListener listener) {
mOnTextSizeChangeListener = listener;
}
public float getVariableTextSize(String text) {
if (mWidthConstraint < 0 || mMaximumTextSize <= mMinimumTextSize) {
return getTextSize();
}
mTempPaint.set(getPaint());
float lastFitTextSize = mMinimumTextSize;
while (lastFitTextSize < mMaximumTextSize) {
final float nextSize = Math.min(lastFitTextSize + mStepTextSize, mMaximumTextSize);
mTempPaint.setTextSize(nextSize);
if (mTempPaint.measureText(text) > mWidthConstraint) {
break;
} else {
lastFitTextSize = nextSize;
}
}
return lastFitTextSize;
}
@Override
public int getCompoundPaddingTop() {
getPaint().getTextBounds("H", 0, 1, mTempRect);
final Paint.FontMetricsInt fontMetrics = getPaint().getFontMetricsInt();
final int paddingOffset = -(fontMetrics.ascent + mTempRect.height());
return super.getCompoundPaddingTop() - Math.min(getPaddingTop(), paddingOffset);
}
@Override
public int getCompoundPaddingBottom() {
final Paint.FontMetricsInt fontMetrics = getPaint().getFontMetricsInt();
return super.getCompoundPaddingBottom() - Math.min(getPaddingBottom(), fontMetrics.descent);
}
public interface OnTextSizeChangeListener {
void onTextSizeChanged(TextView textView, float oldSize);
}
}
@Override
public void onTextSizeChanged(TextView textView, float oldSize) {
final float textScale = oldSize / textView.getTextSize();
final float translationX = (1.0f - textScale) *
(textView.getWidth() / 2.0f - textView.getPaddingEnd());
final float translationY = (1.0f - textScale) *
(textView.getHeight() / 2.0f - textView.getPaddingBottom());
final AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(
ObjectAnimator.ofFloat(textView, View.SCALE_X, textScale, 1.0f),
ObjectAnimator.ofFloat(textView, View.SCALE_Y, textScale, 1.0f),
ObjectAnimator.ofFloat(textView, View.TRANSLATION_X, translationX, 0.0f),
ObjectAnimator.ofFloat(textView, View.TRANSLATION_Y, translationY, 0.0f));
animatorSet.setDuration(getResources().getInteger(android.R.integer.config_mediumAnimTime));
animatorSet.setInterpolator(new AccelerateDecelerateInterpolator());
animatorSet.start();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment