1e96017ffe206b3212a9573982290f9faf0e371baChristine Franks/* 2e96017ffe206b3212a9573982290f9faf0e371baChristine Franks * Copyright (C) 2016 The Android Open Source Project 3e96017ffe206b3212a9573982290f9faf0e371baChristine Franks * 4e96017ffe206b3212a9573982290f9faf0e371baChristine Franks * Licensed under the Apache License, Version 2.0 (the "License"); 5e96017ffe206b3212a9573982290f9faf0e371baChristine Franks * you may not use this file except in compliance with the License. 6e96017ffe206b3212a9573982290f9faf0e371baChristine Franks * You may obtain a copy of the License at 7e96017ffe206b3212a9573982290f9faf0e371baChristine Franks * 8e96017ffe206b3212a9573982290f9faf0e371baChristine Franks * http://www.apache.org/licenses/LICENSE-2.0 9e96017ffe206b3212a9573982290f9faf0e371baChristine Franks * 10e96017ffe206b3212a9573982290f9faf0e371baChristine Franks * Unless required by applicable law or agreed to in writing, software 11e96017ffe206b3212a9573982290f9faf0e371baChristine Franks * distributed under the License is distributed on an "AS IS" BASIS, 12e96017ffe206b3212a9573982290f9faf0e371baChristine Franks * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13e96017ffe206b3212a9573982290f9faf0e371baChristine Franks * See the License for the specific language governing permissions and 14e96017ffe206b3212a9573982290f9faf0e371baChristine Franks * limitations under the License. 15e96017ffe206b3212a9573982290f9faf0e371baChristine Franks */ 16e96017ffe206b3212a9573982290f9faf0e371baChristine Franks 17e96017ffe206b3212a9573982290f9faf0e371baChristine Frankspackage com.android.deskclock.widget; 18e96017ffe206b3212a9573982290f9faf0e371baChristine Franks 19e96017ffe206b3212a9573982290f9faf0e371baChristine Franksimport android.text.Layout; 20e96017ffe206b3212a9573982290f9faf0e371baChristine Franksimport android.text.TextPaint; 21e96017ffe206b3212a9573982290f9faf0e371baChristine Franksimport android.util.TypedValue; 22e96017ffe206b3212a9573982290f9faf0e371baChristine Franksimport android.view.View; 23e96017ffe206b3212a9573982290f9faf0e371baChristine Franksimport android.widget.TextView; 24e96017ffe206b3212a9573982290f9faf0e371baChristine Franks 25e96017ffe206b3212a9573982290f9faf0e371baChristine Franksimport static java.lang.Integer.MAX_VALUE; 26e96017ffe206b3212a9573982290f9faf0e371baChristine Franks 27e96017ffe206b3212a9573982290f9faf0e371baChristine Franks/** 28e96017ffe206b3212a9573982290f9faf0e371baChristine Franks * A TextView which automatically re-sizes its text to fit within its boundaries. 29e96017ffe206b3212a9573982290f9faf0e371baChristine Franks */ 30e96017ffe206b3212a9573982290f9faf0e371baChristine Frankspublic final class TextSizeHelper { 31e96017ffe206b3212a9573982290f9faf0e371baChristine Franks 32e96017ffe206b3212a9573982290f9faf0e371baChristine Franks // The text view whose size this class controls. 33e96017ffe206b3212a9573982290f9faf0e371baChristine Franks private final TextView mTextView; 34e96017ffe206b3212a9573982290f9faf0e371baChristine Franks 35e96017ffe206b3212a9573982290f9faf0e371baChristine Franks // Text paint used for measuring. 36e96017ffe206b3212a9573982290f9faf0e371baChristine Franks private final TextPaint mMeasurePaint = new TextPaint(); 37e96017ffe206b3212a9573982290f9faf0e371baChristine Franks 38e96017ffe206b3212a9573982290f9faf0e371baChristine Franks // The maximum size the text is allowed to be (in pixels). 39e96017ffe206b3212a9573982290f9faf0e371baChristine Franks private float mMaxTextSize; 40e96017ffe206b3212a9573982290f9faf0e371baChristine Franks 41e96017ffe206b3212a9573982290f9faf0e371baChristine Franks // The maximum width the text is allowed to be (in pixels). 42e96017ffe206b3212a9573982290f9faf0e371baChristine Franks private int mWidthConstraint = MAX_VALUE; 43e96017ffe206b3212a9573982290f9faf0e371baChristine Franks 44e96017ffe206b3212a9573982290f9faf0e371baChristine Franks // The maximum height the text is allowed to be (in pixels). 45e96017ffe206b3212a9573982290f9faf0e371baChristine Franks private int mHeightConstraint = MAX_VALUE; 46e96017ffe206b3212a9573982290f9faf0e371baChristine Franks 47e96017ffe206b3212a9573982290f9faf0e371baChristine Franks // When {@code true} calls to {@link #requestLayout()} should be ignored. 48e96017ffe206b3212a9573982290f9faf0e371baChristine Franks private boolean mIgnoreRequestLayout; 49e96017ffe206b3212a9573982290f9faf0e371baChristine Franks 50e96017ffe206b3212a9573982290f9faf0e371baChristine Franks public TextSizeHelper(TextView view) { 51e96017ffe206b3212a9573982290f9faf0e371baChristine Franks mTextView = view; 52e96017ffe206b3212a9573982290f9faf0e371baChristine Franks mMaxTextSize = view.getTextSize(); 53e96017ffe206b3212a9573982290f9faf0e371baChristine Franks } 54e96017ffe206b3212a9573982290f9faf0e371baChristine Franks 55e96017ffe206b3212a9573982290f9faf0e371baChristine Franks public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 56e96017ffe206b3212a9573982290f9faf0e371baChristine Franks int widthConstraint = MAX_VALUE; 57e96017ffe206b3212a9573982290f9faf0e371baChristine Franks if (View.MeasureSpec.getMode(widthMeasureSpec) != View.MeasureSpec.UNSPECIFIED) { 58e96017ffe206b3212a9573982290f9faf0e371baChristine Franks widthConstraint = View.MeasureSpec.getSize(widthMeasureSpec) 59e96017ffe206b3212a9573982290f9faf0e371baChristine Franks - mTextView.getCompoundPaddingLeft() - mTextView.getCompoundPaddingRight(); 60e96017ffe206b3212a9573982290f9faf0e371baChristine Franks } 61e96017ffe206b3212a9573982290f9faf0e371baChristine Franks 62e96017ffe206b3212a9573982290f9faf0e371baChristine Franks int heightConstraint = MAX_VALUE; 63e96017ffe206b3212a9573982290f9faf0e371baChristine Franks if (View.MeasureSpec.getMode(heightMeasureSpec) != View.MeasureSpec.UNSPECIFIED) { 64e96017ffe206b3212a9573982290f9faf0e371baChristine Franks heightConstraint = View.MeasureSpec.getSize(heightMeasureSpec) 65e96017ffe206b3212a9573982290f9faf0e371baChristine Franks - mTextView.getCompoundPaddingTop() - mTextView.getCompoundPaddingBottom(); 66e96017ffe206b3212a9573982290f9faf0e371baChristine Franks } 67e96017ffe206b3212a9573982290f9faf0e371baChristine Franks 68e96017ffe206b3212a9573982290f9faf0e371baChristine Franks if (mTextView.isLayoutRequested() || mWidthConstraint != widthConstraint 69e96017ffe206b3212a9573982290f9faf0e371baChristine Franks || mHeightConstraint != heightConstraint) { 70e96017ffe206b3212a9573982290f9faf0e371baChristine Franks mWidthConstraint = widthConstraint; 71e96017ffe206b3212a9573982290f9faf0e371baChristine Franks mHeightConstraint = heightConstraint; 72e96017ffe206b3212a9573982290f9faf0e371baChristine Franks 73e96017ffe206b3212a9573982290f9faf0e371baChristine Franks adjustTextSize(); 74e96017ffe206b3212a9573982290f9faf0e371baChristine Franks } 75e96017ffe206b3212a9573982290f9faf0e371baChristine Franks } 76e96017ffe206b3212a9573982290f9faf0e371baChristine Franks 77e96017ffe206b3212a9573982290f9faf0e371baChristine Franks public void onTextChanged(int lengthBefore, int lengthAfter) { 78e96017ffe206b3212a9573982290f9faf0e371baChristine Franks // The length of the text has changed, request layout to recalculate the current text 79e96017ffe206b3212a9573982290f9faf0e371baChristine Franks // size. This is necessary to workaround an optimization in TextView#checkForRelayout() 80e96017ffe206b3212a9573982290f9faf0e371baChristine Franks // which will avoid re-layout when the view has a fixed layout width. 81e96017ffe206b3212a9573982290f9faf0e371baChristine Franks if (lengthBefore != lengthAfter) { 82e96017ffe206b3212a9573982290f9faf0e371baChristine Franks mTextView.requestLayout(); 83e96017ffe206b3212a9573982290f9faf0e371baChristine Franks } 84e96017ffe206b3212a9573982290f9faf0e371baChristine Franks } 85e96017ffe206b3212a9573982290f9faf0e371baChristine Franks 86e96017ffe206b3212a9573982290f9faf0e371baChristine Franks public boolean shouldIgnoreRequestLayout() { 87e96017ffe206b3212a9573982290f9faf0e371baChristine Franks return mIgnoreRequestLayout; 88e96017ffe206b3212a9573982290f9faf0e371baChristine Franks } 89e96017ffe206b3212a9573982290f9faf0e371baChristine Franks 90e96017ffe206b3212a9573982290f9faf0e371baChristine Franks private void adjustTextSize() { 91e96017ffe206b3212a9573982290f9faf0e371baChristine Franks final CharSequence text = mTextView.getText(); 92e96017ffe206b3212a9573982290f9faf0e371baChristine Franks float textSize = mMaxTextSize; 93e96017ffe206b3212a9573982290f9faf0e371baChristine Franks if (text.length() > 0 && (mWidthConstraint < MAX_VALUE || mHeightConstraint < MAX_VALUE)) { 94e96017ffe206b3212a9573982290f9faf0e371baChristine Franks mMeasurePaint.set(mTextView.getPaint()); 95e96017ffe206b3212a9573982290f9faf0e371baChristine Franks 96e96017ffe206b3212a9573982290f9faf0e371baChristine Franks float minTextSize = 1f; 97e96017ffe206b3212a9573982290f9faf0e371baChristine Franks float maxTextSize = mMaxTextSize; 98e96017ffe206b3212a9573982290f9faf0e371baChristine Franks while (maxTextSize >= minTextSize) { 99e96017ffe206b3212a9573982290f9faf0e371baChristine Franks final float midTextSize = Math.round((maxTextSize + minTextSize) / 2f); 100e96017ffe206b3212a9573982290f9faf0e371baChristine Franks mMeasurePaint.setTextSize(midTextSize); 101e96017ffe206b3212a9573982290f9faf0e371baChristine Franks 102e96017ffe206b3212a9573982290f9faf0e371baChristine Franks final float width = Layout.getDesiredWidth(text, mMeasurePaint); 103e96017ffe206b3212a9573982290f9faf0e371baChristine Franks final float height = mMeasurePaint.getFontMetricsInt(null); 104e96017ffe206b3212a9573982290f9faf0e371baChristine Franks if (width > mWidthConstraint || height > mHeightConstraint) { 105e96017ffe206b3212a9573982290f9faf0e371baChristine Franks maxTextSize = midTextSize - 1f; 106e96017ffe206b3212a9573982290f9faf0e371baChristine Franks } else { 107e96017ffe206b3212a9573982290f9faf0e371baChristine Franks textSize = midTextSize; 108e96017ffe206b3212a9573982290f9faf0e371baChristine Franks minTextSize = midTextSize + 1f; 109e96017ffe206b3212a9573982290f9faf0e371baChristine Franks } 110e96017ffe206b3212a9573982290f9faf0e371baChristine Franks } 111e96017ffe206b3212a9573982290f9faf0e371baChristine Franks } 112e96017ffe206b3212a9573982290f9faf0e371baChristine Franks 113e96017ffe206b3212a9573982290f9faf0e371baChristine Franks if (mTextView.getTextSize() != textSize) { 114e96017ffe206b3212a9573982290f9faf0e371baChristine Franks mIgnoreRequestLayout = true; 115e96017ffe206b3212a9573982290f9faf0e371baChristine Franks mTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize); 116e96017ffe206b3212a9573982290f9faf0e371baChristine Franks mIgnoreRequestLayout = false; 117e96017ffe206b3212a9573982290f9faf0e371baChristine Franks } 118e96017ffe206b3212a9573982290f9faf0e371baChristine Franks } 119e96017ffe206b3212a9573982290f9faf0e371baChristine Franks}