blob: bf37f767fb5ece35a306b8b87a21512b90b56667 [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* 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 com.android.deskclock.widget;
import android.text.Layout;
import android.text.TextPaint;
import android.util.TypedValue;
import android.view.View;
import android.widget.TextView;
import static java.lang.Integer.MAX_VALUE;
/**
* A TextView which automatically re-sizes its text to fit within its boundaries.
*/
public final class TextSizeHelper {
// The text view whose size this class controls.
private final TextView mTextView;
// Text paint used for measuring.
private final TextPaint mMeasurePaint = new TextPaint();
// The maximum size the text is allowed to be (in pixels).
private float mMaxTextSize;
// The maximum width the text is allowed to be (in pixels).
private int mWidthConstraint = MAX_VALUE;
// The maximum height the text is allowed to be (in pixels).
private int mHeightConstraint = MAX_VALUE;
// When {@code true} calls to {@link #requestLayout()} should be ignored.
private boolean mIgnoreRequestLayout;
public TextSizeHelper(TextView view) {
mTextView = view;
mMaxTextSize = view.getTextSize();
}
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthConstraint = MAX_VALUE;
if (View.MeasureSpec.getMode(widthMeasureSpec) != View.MeasureSpec.UNSPECIFIED) {
widthConstraint = View.MeasureSpec.getSize(widthMeasureSpec)
- mTextView.getCompoundPaddingLeft() - mTextView.getCompoundPaddingRight();
}
int heightConstraint = MAX_VALUE;
if (View.MeasureSpec.getMode(heightMeasureSpec) != View.MeasureSpec.UNSPECIFIED) {
heightConstraint = View.MeasureSpec.getSize(heightMeasureSpec)
- mTextView.getCompoundPaddingTop() - mTextView.getCompoundPaddingBottom();
}
if (mTextView.isLayoutRequested() || mWidthConstraint != widthConstraint
|| mHeightConstraint != heightConstraint) {
mWidthConstraint = widthConstraint;
mHeightConstraint = heightConstraint;
adjustTextSize();
}
}
public void onTextChanged(int lengthBefore, int lengthAfter) {
// The length of the text has changed, request layout to recalculate the current text
// size. This is necessary to workaround an optimization in TextView#checkForRelayout()
// which will avoid re-layout when the view has a fixed layout width.
if (lengthBefore != lengthAfter) {
mTextView.requestLayout();
}
}
public boolean shouldIgnoreRequestLayout() {
return mIgnoreRequestLayout;
}
private void adjustTextSize() {
final CharSequence text = mTextView.getText();
float textSize = mMaxTextSize;
if (text.length() > 0 && (mWidthConstraint < MAX_VALUE || mHeightConstraint < MAX_VALUE)) {
mMeasurePaint.set(mTextView.getPaint());
float minTextSize = 1f;
float maxTextSize = mMaxTextSize;
while (maxTextSize >= minTextSize) {
final float midTextSize = Math.round((maxTextSize + minTextSize) / 2f);
mMeasurePaint.setTextSize(midTextSize);
final float width = Layout.getDesiredWidth(text, mMeasurePaint);
final float height = mMeasurePaint.getFontMetricsInt(null);
if (width > mWidthConstraint || height > mHeightConstraint) {
maxTextSize = midTextSize - 1f;
} else {
textSize = midTextSize;
minTextSize = midTextSize + 1f;
}
}
}
if (mTextView.getTextSize() != textSize) {
mIgnoreRequestLayout = true;
mTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
mIgnoreRequestLayout = false;
}
}
}