Skip to content

Commit 6ae62a6

Browse files
committed
Merge branch 'develop' into issue/1690-track-last-seen-screen
Conflicts: WordPress/src/main/java/org/wordpress/android/ui/posts/EditPostActivity.java
2 parents 7b7f0d2 + d1b9bef commit 6ae62a6

File tree

2 files changed

+300
-1
lines changed

2 files changed

+300
-1
lines changed

WordPressUtils/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ android {
3232

3333
defaultConfig {
3434
applicationId "org.wordpress.android.util"
35-
versionName "1.0.2"
35+
versionName "1.1.0"
3636
versionCode 1
3737
minSdkVersion 14
3838
targetSdkVersion 19
Lines changed: 299 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,299 @@
1+
package org.wordpress.android.util;
2+
3+
import android.content.Context;
4+
import android.text.Layout;
5+
import android.text.StaticLayout;
6+
import android.text.TextPaint;
7+
import android.util.AttributeSet;
8+
import android.util.TypedValue;
9+
import android.widget.TextView;
10+
11+
/**
12+
* Text view that auto adjusts text size to fit within the view.
13+
* If the text size equals the minimum text size and still does not
14+
* fit, append with an ellipsis.
15+
*
16+
* See http://stackoverflow.com/a/5535672
17+
*
18+
*/
19+
public class AutoResizeTextView extends TextView {
20+
// Minimum text size for this text view
21+
private static final float MIN_TEXT_SIZE = 20;
22+
23+
// Interface for resize notifications
24+
public interface OnTextResizeListener {
25+
void onTextResize(TextView textView, float oldSize, float newSize);
26+
}
27+
28+
// Our ellipse string - Unicode Character 'HORIZONTAL ELLIPSIS'
29+
private static final String M_ELLIPSIS = "\u2026";
30+
31+
// Registered resize listener
32+
private OnTextResizeListener mTextResizeListener;
33+
34+
// Flag for text and/or size changes to force a resize
35+
private boolean mNeedsResize = false;
36+
37+
// Text size that is set from code. This acts as a starting point for resizing
38+
private float mTextSize;
39+
40+
// Temporary upper bounds on the starting text size
41+
private float mMaxTextSize = 0;
42+
43+
// Lower bounds for text size
44+
private float mMinTextSize = MIN_TEXT_SIZE;
45+
46+
// Text view line spacing multiplier
47+
private float mSpacingMult = 1.0f;
48+
49+
// Text view additional line spacing
50+
private float mSpacingAdd = 0.0f;
51+
52+
// Add ellipsis to text that overflows at the smallest text size
53+
private boolean mAddEllipsis = true;
54+
55+
// Default constructor override
56+
public AutoResizeTextView(Context context) {
57+
this(context, null);
58+
}
59+
60+
// Default constructor when inflating from XML file
61+
public AutoResizeTextView(Context context, AttributeSet attrs) {
62+
this(context, attrs, 0);
63+
}
64+
65+
// Default constructor override
66+
public AutoResizeTextView(Context context, AttributeSet attrs, int defStyle) {
67+
super(context, attrs, defStyle);
68+
mTextSize = getTextSize();
69+
}
70+
71+
/**
72+
* When text changes, set the force resize flag to true and reset the text size.
73+
*/
74+
@Override
75+
protected void onTextChanged(final CharSequence text, final int start, final int before, final int after) {
76+
mNeedsResize = true;
77+
// Since this view may be reused, it is good to reset the text size
78+
resetTextSize();
79+
}
80+
81+
/**
82+
* If the text view size changed, set the force resize flag to true
83+
*/
84+
@Override
85+
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
86+
if (w != oldw || h != oldh) {
87+
mNeedsResize = true;
88+
}
89+
}
90+
91+
/**
92+
* Register listener to receive resize notifications
93+
* @param listener
94+
*/
95+
public void setOnResizeListener(OnTextResizeListener listener) {
96+
mTextResizeListener = listener;
97+
}
98+
99+
/**
100+
* Override the set text size to update our internal reference values
101+
*/
102+
@Override
103+
public void setTextSize(float size) {
104+
super.setTextSize(size);
105+
mTextSize = getTextSize();
106+
}
107+
108+
/**
109+
* Override the set text size to update our internal reference values
110+
*/
111+
@Override
112+
public void setTextSize(int unit, float size) {
113+
super.setTextSize(unit, size);
114+
mTextSize = getTextSize();
115+
}
116+
117+
/**
118+
* Override the set line spacing to update our internal reference values
119+
*/
120+
@Override
121+
public void setLineSpacing(float add, float mult) {
122+
super.setLineSpacing(add, mult);
123+
mSpacingMult = mult;
124+
mSpacingAdd = add;
125+
}
126+
127+
/**
128+
* Set the upper text size limit and invalidate the view
129+
* @param maxTextSize
130+
*/
131+
public void setMaxTextSize(float maxTextSize) {
132+
mMaxTextSize = maxTextSize;
133+
requestLayout();
134+
invalidate();
135+
}
136+
137+
/**
138+
* Return upper text size limit
139+
* @return
140+
*/
141+
public float getMaxTextSize() {
142+
return mMaxTextSize;
143+
}
144+
145+
/**
146+
* Set the lower text size limit and invalidate the view
147+
* @param minTextSize
148+
*/
149+
public void setMinTextSize(float minTextSize) {
150+
mMinTextSize = minTextSize;
151+
requestLayout();
152+
invalidate();
153+
}
154+
155+
/**
156+
* Return lower text size limit
157+
* @return
158+
*/
159+
public float getMinTextSize() {
160+
return mMinTextSize;
161+
}
162+
163+
/**
164+
* Set flag to add ellipsis to text that overflows at the smallest text size
165+
* @param addEllipsis
166+
*/
167+
public void setAddEllipsis(boolean addEllipsis) {
168+
mAddEllipsis = addEllipsis;
169+
}
170+
171+
/**
172+
* Return flag to add ellipsis to text that overflows at the smallest text size
173+
* @return
174+
*/
175+
public boolean getAddEllipsis() {
176+
return mAddEllipsis;
177+
}
178+
179+
/**
180+
* Reset the text to the original size
181+
*/
182+
private void resetTextSize() {
183+
if (mTextSize > 0) {
184+
super.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSize);
185+
mMaxTextSize = mTextSize;
186+
}
187+
}
188+
189+
/**
190+
* Resize text after measuring
191+
*/
192+
@Override
193+
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
194+
if (changed || mNeedsResize) {
195+
int widthLimit = (right - left) - getCompoundPaddingLeft() - getCompoundPaddingRight();
196+
int heightLimit = (bottom - top) - getCompoundPaddingBottom() - getCompoundPaddingTop();
197+
resizeText(widthLimit, heightLimit);
198+
}
199+
super.onLayout(changed, left, top, right, bottom);
200+
}
201+
202+
/**
203+
* Resize the text size with default width and height
204+
*/
205+
public void resizeText() {
206+
int heightLimit = getHeight() - getPaddingBottom() - getPaddingTop();
207+
int widthLimit = getWidth() - getPaddingLeft() - getPaddingRight();
208+
resizeText(widthLimit, heightLimit);
209+
}
210+
211+
/**
212+
* Resize the text size with specified width and height
213+
* @param width
214+
* @param height
215+
*/
216+
public void resizeText(int width, int height) {
217+
CharSequence text = getText();
218+
// Do not resize if the view does not have dimensions or there is no text
219+
if (text == null || text.length() == 0 || height <= 0 || width <= 0 || mTextSize == 0) {
220+
return;
221+
}
222+
223+
// Get the text view's paint object
224+
TextPaint textPaint = getPaint();
225+
226+
// Store the current text size
227+
float oldTextSize = textPaint.getTextSize();
228+
// If there is a max text size set, use the lesser of that and the default text size
229+
float targetTextSize = mMaxTextSize > 0 ? Math.min(mTextSize, mMaxTextSize) : mTextSize;
230+
231+
// Get the required text height
232+
int textHeight = getTextHeight(text, textPaint, width, targetTextSize);
233+
234+
// Until we either fit within our text view or we had reached our min text size, incrementally try smaller sizes
235+
while (textHeight > height && targetTextSize > mMinTextSize) {
236+
targetTextSize = Math.max(targetTextSize - 2, mMinTextSize);
237+
textHeight = getTextHeight(text, textPaint, width, targetTextSize);
238+
}
239+
240+
// If we had reached our minimum text size and still don't fit, append an ellipsis
241+
if (mAddEllipsis && targetTextSize == mMinTextSize && textHeight > height) {
242+
// Draw using a static layout
243+
// modified: use a copy of TextPaint for measuring
244+
TextPaint paint = new TextPaint(textPaint);
245+
// Draw using a static layout
246+
StaticLayout layout = new StaticLayout(text, paint, width, Layout.Alignment.ALIGN_NORMAL,
247+
mSpacingMult, mSpacingAdd, false);
248+
// Check that we have a least one line of rendered text
249+
if (layout.getLineCount() > 0) {
250+
// Since the line at the specific vertical position would be cut off,
251+
// we must trim up to the previous line
252+
int lastLine = layout.getLineForVertical(height) - 1;
253+
// If the text would not even fit on a single line, clear it
254+
if (lastLine < 0) {
255+
setText("");
256+
} else {
257+
// Otherwise, trim to the previous line and add an ellipsis
258+
int start = layout.getLineStart(lastLine);
259+
int end = layout.getLineEnd(lastLine);
260+
float lineWidth = layout.getLineWidth(lastLine);
261+
float ellipseWidth = paint.measureText(M_ELLIPSIS);
262+
263+
// Trim characters off until we have enough room to draw the ellipsis
264+
while (width < lineWidth + ellipseWidth) {
265+
lineWidth = paint.measureText(text.subSequence(start, --end + 1).toString());
266+
}
267+
setText(text.subSequence(0, end) + M_ELLIPSIS);
268+
}
269+
}
270+
}
271+
272+
// Some devices try to auto adjust line spacing, so force default line spacing
273+
// and invalidate the layout as a side effect
274+
setTextSize(TypedValue.COMPLEX_UNIT_PX, targetTextSize);
275+
setLineSpacing(mSpacingAdd, mSpacingMult);
276+
277+
// Notify the listener if registered
278+
if (mTextResizeListener != null) {
279+
mTextResizeListener.onTextResize(this, oldTextSize, targetTextSize);
280+
}
281+
282+
// Reset force resize flag
283+
mNeedsResize = false;
284+
}
285+
286+
// Set the text size of the text paint object and use a static layout to render text off screen before measuring
287+
private int getTextHeight(CharSequence source, TextPaint paint, int width, float textSize) {
288+
// modified: make a copy of the original TextPaint object for measuring
289+
// (apparently the object gets modified while measuring, see also the
290+
// docs for TextView.getPaint() (which states to access it read-only)
291+
TextPaint paintCopy = new TextPaint(paint);
292+
// Update the text paint object
293+
paintCopy.setTextSize(textSize);
294+
// Measure using a static layout
295+
StaticLayout layout = new StaticLayout(source, paintCopy, width, Layout.Alignment.ALIGN_NORMAL,
296+
mSpacingMult, mSpacingAdd, true);
297+
return layout.getHeight();
298+
}
299+
}

0 commit comments

Comments
 (0)