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