auto resize text view +layout changes

This commit is contained in:
serso 2011-11-02 01:06:56 +04:00
parent f20c34eb5a
commit a7aae1626a
9 changed files with 212 additions and 238 deletions

View File

@ -111,8 +111,8 @@
<include layout="@layout/calc_two_digit_button"/> <include layout="@layout/calc_two_digit_button"/>
<include layout="@layout/calc_three_digit_button"/> <include layout="@layout/calc_three_digit_button"/>
<include layout="@layout/calc_zero_digit_button"/> <include layout="@layout/calc_zero_digit_button"/>
<include layout="@layout/calc_dot_button"/>
<include layout="@layout/calc_round_brackets_button"/> <include layout="@layout/calc_round_brackets_button"/>
<include layout="@layout/calc_square_brackets_button"/>
</LinearLayout> </LinearLayout>

View File

@ -105,8 +105,8 @@
<include layout="@layout/calc_eight_digit_button"/> <include layout="@layout/calc_eight_digit_button"/>
<include layout="@layout/calc_nine_digit_button"/> <include layout="@layout/calc_nine_digit_button"/>
<include layout="@layout/calc_zero_digit_button"/> <include layout="@layout/calc_zero_digit_button"/>
<include layout="@layout/calc_dot_button"/>
<include layout="@layout/calc_round_brackets_button"/> <include layout="@layout/calc_round_brackets_button"/>
<include layout="@layout/calc_square_brackets_button"/>
</LinearLayout> </LinearLayout>

View File

@ -68,7 +68,7 @@
<include layout="@layout/calc_round_brackets_button"/> <include layout="@layout/calc_round_brackets_button"/>
<include layout="@layout/calc_zero_digit_button"/> <include layout="@layout/calc_zero_digit_button"/>
<include layout="@layout/calc_square_brackets_button"/> <include layout="@layout/calc_dot_button"/>
<include layout="@layout/calc_subtraction_button"/> <include layout="@layout/calc_subtraction_button"/>
<include layout="@layout/calc_paste_button"/> <include layout="@layout/calc_paste_button"/>

View File

@ -62,7 +62,7 @@
<include layout="@layout/calc_round_brackets_button"/> <include layout="@layout/calc_round_brackets_button"/>
<include layout="@layout/calc_zero_digit_button"/> <include layout="@layout/calc_zero_digit_button"/>
<include layout="@layout/calc_square_brackets_button"/> <include layout="@layout/calc_dot_button"/>
<include layout="@layout/calc_subtraction_button"/> <include layout="@layout/calc_subtraction_button"/>
<include layout="@layout/calc_paste_button"/> <include layout="@layout/calc_paste_button"/>

View File

@ -8,10 +8,9 @@
<org.solovyev.android.view.widgets.DirectionDragButton xmlns:a="http://schemas.android.com/apk/res/android" <org.solovyev.android.view.widgets.DirectionDragButton xmlns:a="http://schemas.android.com/apk/res/android"
xmlns:calc="http://schemas.android.com/apk/res/org.solovyev.android.calculator" xmlns:calc="http://schemas.android.com/apk/res/org.solovyev.android.calculator"
a:id="@+id/squareBracketsButton" a:text="[]" a:id="@+id/squareBracketsButton"
calc:textUp="[" a:text="."
calc:textDown="]" calc:textUp=","
calc:directionTextScale="0.5" calc:directionTextScale="0.5"
style="?digitButtonStyle" style="?digitButtonStyle"
a:onClick="digitButtonClickHandler"/> a:onClick="digitButtonClickHandler"/>

View File

@ -7,9 +7,8 @@
--> -->
<org.solovyev.android.view.widgets.DirectionDragButton xmlns:a="http://schemas.android.com/apk/res/android" <org.solovyev.android.view.widgets.DirectionDragButton xmlns:a="http://schemas.android.com/apk/res/android"
xmlns:calc="http://schemas.android.com/apk/res/org.solovyev.android.calculator" xmlns:calc="http://schemas.android.com/apk/res/org.solovyev.android.calculator"
a:id="@+id/subtractionButton" a:text="-" a:id="@+id/subtractionButton"
calc:textUp="," a:text="-"
calc:directionTextScale="0.5" calc:directionTextScale="0.5"
style="?digitButtonStyle" style="?digitButtonStyle"
a:onClick="digitButtonClickHandler"/> a:onClick="digitButtonClickHandler"/>

View File

@ -10,7 +10,6 @@
xmlns:calc="http://schemas.android.com/apk/res/org.solovyev.android.calculator" xmlns:calc="http://schemas.android.com/apk/res/org.solovyev.android.calculator"
a:id="@+id/zeroDigitButton" a:id="@+id/zeroDigitButton"
a:text="0" a:text="0"
calc:textUp="."
calc:textDown="000" calc:textDown="000"
calc:directionTextScale="0.5" calc:directionTextScale="0.5"
style="?digitButtonStyle" style="?digitButtonStyle"

View File

@ -87,8 +87,6 @@ public class CalculatorDisplay extends AutoResizeTextView {
// todo serso: think where to move it (keep in mind org.solovyev.android.view.AutoResizeTextView.resetTextSize()) // todo serso: think where to move it (keep in mind org.solovyev.android.view.AutoResizeTextView.resetTextSize())
setAddEllipsis(false); setAddEllipsis(false);
setMinTextSize(10); setMinTextSize(10);
setMaxTextSize(70);
setTextSize(70);
resizeText(); resizeText();
} }

View File

@ -12,7 +12,6 @@ import android.text.Layout.Alignment;
import android.text.StaticLayout; import android.text.StaticLayout;
import android.text.TextPaint; import android.text.TextPaint;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.TypedValue;
import android.widget.TextView; import android.widget.TextView;
/** /**
@ -25,266 +24,246 @@ import android.widget.TextView;
*/ */
public class AutoResizeTextView extends TextView { public class AutoResizeTextView extends TextView {
// Minimum text size for this text view // Minimum text size for this text view
public static final float MIN_TEXT_SIZE = 20; public static final float MIN_TEXT_SIZE = 20;
// Interface for resize notifications // Interface for resize notifications
public interface OnTextResizeListener { public interface OnTextResizeListener {
public void onTextResize(TextView textView, float oldSize, float newSize); public void onTextResize(TextView textView, float oldSize, float newSize);
} }
// Off screen canvas for text size rendering // Off screen canvas for text size rendering
private static final Canvas sTextResizeCanvas = new Canvas(); private static final Canvas sTextResizeCanvas = new Canvas();
// Our ellipse string // Our ellipse string
private static final String mEllipsis = "..."; private static final String mEllipsis = "...";
// Registered resize listener // Registered resize listener
private OnTextResizeListener mTextResizeListener; private OnTextResizeListener mTextResizeListener;
// Flag for text and/or size changes to force a resize // Flag for text and/or size changes to force a resize
private boolean mNeedsResize = false; private boolean mNeedsResize = false;
// Text size that is set from code. This acts as a starting point for resizing // Lower bounds for text size
private float mTextSize; private float minTextSize = MIN_TEXT_SIZE;
// Temporary upper bounds on the starting text size // Text view line spacing multiplier
private float mMaxTextSize = 0; private float spacingMult = 1.0f;
// Lower bounds for text size // Text view additional line spacing
private float mMinTextSize = MIN_TEXT_SIZE; private float spacingAdd = 0.0f;
// Text view line spacing multiplier // Add ellipsis to text that overflows at the smallest text size
private float mSpacingMult = 1.0f; private boolean addEllipsis = true;
// Text view additional line spacing private float textSizeStartPoint = MIN_TEXT_SIZE;
private float mSpacingAdd = 0.0f;
// Add ellipsis to text that overflows at the smallest text size // Default constructor override
private boolean mAddEllipsis = true; public AutoResizeTextView(Context context) {
this(context, null);
}
// Default constructor override // Default constructor when inflating from XML file
public AutoResizeTextView(Context context) { public AutoResizeTextView(Context context, AttributeSet attrs) {
this(context, null); this(context, attrs, 0);
} }
// Default constructor when inflating from XML file // Default constructor override
public AutoResizeTextView(Context context, AttributeSet attrs) { public AutoResizeTextView(Context context, AttributeSet attrs, int defStyle) {
this(context, attrs, 0); super(context, attrs, defStyle);
} }
// Default constructor override /**
public AutoResizeTextView(Context context, AttributeSet attrs, int defStyle) { * When text changes, set the force resize flag to true and resetInterpreter the text size.
super(context, attrs, defStyle); */
mTextSize = getTextSize(); @Override
} protected void onTextChanged(final CharSequence text, final int start, final int before, final int after) {
mNeedsResize = true;
// Since this view may be reused, it is good to resetInterpreter the text size
}
/** /**
* When text changes, set the force resize flag to true and resetInterpreter the text size. * If the text view size changed, set the force resize flag to true
*/ */
@Override @Override
protected void onTextChanged(final CharSequence text, final int start, final int before, final int after) { protected void onSizeChanged(int w, int h, int oldw, int oldh) {
mNeedsResize = true; if (w != oldw || h != oldh) {
// Since this view may be reused, it is good to resetInterpreter the text size mNeedsResize = true;
resetTextSize(); }
} }
/** /**
* If the text view size changed, set the force resize flag to true * Register listener to receive resize notifications
*/ *
@Override * @param listener
protected void onSizeChanged(int w, int h, int oldw, int oldh) { */
if (w != oldw || h != oldh) { public void setOnResizeListener(OnTextResizeListener listener) {
mNeedsResize = true; mTextResizeListener = listener;
} }
}
/** /**
* Register listener to receive resize notifications * Override the set text size to update our internal reference values
* @param listener */
*/ @Override
public void setOnResizeListener(OnTextResizeListener listener) { public void setTextSize(int unit, float size) {
mTextResizeListener = listener; super.setTextSize(unit, size);
} }
/** /**
* Override the set text size to update our internal reference values * Override the set line spacing to update our internal reference values
*/ */
@Override @Override
public void setTextSize(float size) { public void setLineSpacing(float add, float mult) {
super.setTextSize(size); super.setLineSpacing(add, mult);
mTextSize = getTextSize(); spacingMult = mult;
} spacingAdd = add;
}
/** /**
* Override the set text size to update our internal reference values * Set the lower text size limit and invalidate the view
*/ *
@Override * @param minTextSize
public void setTextSize(int unit, float size) { */
super.setTextSize(unit, size); public void setMinTextSize(float minTextSize) {
mTextSize = getTextSize(); this.minTextSize = minTextSize;
} requestLayout();
invalidate();
}
/** /**
* Override the set line spacing to update our internal reference values * Return lower text size limit
*/ *
@Override * @return
public void setLineSpacing(float add, float mult) { */
super.setLineSpacing(add, mult); public float getMinTextSize() {
mSpacingMult = mult; return minTextSize;
mSpacingAdd = add; }
}
/** /**
* Set the upper text size limit and invalidate the view * Set flag to add ellipsis to text that overflows at the smallest text size
* @param maxTextSize *
*/ * @param addEllipsis
public void setMaxTextSize(float maxTextSize) { */
mMaxTextSize = maxTextSize; public void setAddEllipsis(boolean addEllipsis) {
requestLayout(); this.addEllipsis = addEllipsis;
invalidate(); }
}
/** /**
* Return upper text size limit * Return flag to add ellipsis to text that overflows at the smallest text size
* @return *
*/ * @return
public float getMaxTextSize() { */
return mMaxTextSize; public boolean getAddEllipsis() {
} return addEllipsis;
}
/**
* Set the lower text size limit and invalidate the view
* @param minTextSize
*/
public void setMinTextSize(float minTextSize) {
mMinTextSize = minTextSize;
requestLayout();
invalidate();
}
/** /**
* Return lower text size limit * Resize text after measuring
* @return */
*/ @Override
public float getMinTextSize() { protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
return mMinTextSize; if (changed || mNeedsResize) {
} resizeText(right - left, bottom - top);
}
super.onLayout(changed, left, top, right, bottom);
}
/** /**
* Set flag to add ellipsis to text that overflows at the smallest text size * Resize the text size with default width and height
* @param addEllipsis */
*/ public void resizeText() {
public void setAddEllipsis(boolean addEllipsis) { int heightLimit = getHeight() - getPaddingBottom() - getPaddingTop();
mAddEllipsis = addEllipsis; int widthLimit = getWidth() - getPaddingLeft() - getPaddingRight();
} resizeText(widthLimit, heightLimit);
}
/** /**
* Return flag to add ellipsis to text that overflows at the smallest text size * Resize the text size with specified width and height
* @return *
*/ * @param width
public boolean getAddEllipsis() { * @param height
return mAddEllipsis; */
} public void resizeText(int width, int height) {
CharSequence text = getText();
// Do not resize if the view does not have dimensions or there is no text
if (text == null || text.length() == 0 || height <= 0 || width <= 0) {
return;
}
/** // Get the text view's paint object
* Reset the text to the original size TextPaint textPaint = getPaint();
*/
public void resetTextSize() {
super.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSize);
mMaxTextSize = mTextSize;
}
/** // Store the current text size
* Resize text after measuring float oldTextSize = textPaint.getTextSize();
*/
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
if(changed || mNeedsResize) {
resizeText(right - left, bottom - top);
}
super.onLayout(changed, left, top, right, bottom);
}
/** // If there is a max text size set, use the lesser of that and the default text size
* Resize the text size with default width and height float newTextSize = textSizeStartPoint;
*/
public void resizeText() {
int heightLimit = getHeight() - getPaddingBottom() - getPaddingTop();
int widthLimit = getWidth() - getPaddingLeft() - getPaddingRight();
resizeText(widthLimit, heightLimit);
}
/** // Get the required text height
* Resize the text size with specified width and height int newTextHeight = getTextRect(text, textPaint, width, newTextSize);
* @param width
* @param height
*/
public void resizeText(int width, int height) {
CharSequence text = getText();
// Do not resize if the view does not have dimensions or there is no text
if(text == null || text.length() == 0 || height <= 0 || width <= 0) {
return;
}
// Get the text view's paint object if (newTextHeight > height) {
TextPaint textPaint = getPaint(); // Until we either fit within our text view or we had reached our min text size, incrementally try smaller sizes
while (newTextHeight > height) {
if (newTextSize <= minTextSize) {
break;
}
newTextSize = Math.max(newTextSize - 1, minTextSize);
newTextHeight = getTextRect(text, textPaint, width, newTextSize);
}
} else {
while (newTextHeight < height) {
if (newTextSize <= minTextSize) {
break;
}
newTextSize = Math.max(newTextSize + 1, minTextSize);
newTextHeight = getTextRect(text, textPaint, width, newTextSize);
}
}
// Store the current text size textSizeStartPoint = newTextSize;
float oldTextSize = textPaint.getTextSize();
// If there is a max text size set, use the lesser of that and the default text size
float targetTextSize = mMaxTextSize > 0 ? Math.min(mTextSize, mMaxTextSize) : mTextSize;
// Get the required text height // If we had reached our minimum text size and still don't fit, append an ellipsis
int textHeight = getTextHeight(text, textPaint, width, targetTextSize); if (addEllipsis && newTextSize == minTextSize && newTextHeight > height) {
// Draw using a static layout
StaticLayout layout = new StaticLayout(text, textPaint, width, Alignment.ALIGN_NORMAL, spacingMult, spacingAdd, false);
layout.draw(sTextResizeCanvas);
int lastLine = layout.getLineForVertical(height) - 1;
int start = layout.getLineStart(lastLine);
int end = layout.getLineEnd(lastLine);
float lineWidth = layout.getLineWidth(lastLine);
float ellipseWidth = textPaint.measureText(mEllipsis);
// Until we either fit within our text view or we had reached our min text size, incrementally try smaller sizes // Trim characters off until we have enough room to draw the ellipsis
while(textHeight > height && targetTextSize > mMinTextSize) { while (width < lineWidth + ellipseWidth) {
targetTextSize = Math.max(targetTextSize - 2, mMinTextSize); lineWidth = textPaint.measureText(text.subSequence(start, --end + 1).toString());
textHeight = getTextHeight(text, textPaint, width, targetTextSize); }
} setText(text.subSequence(0, end) + mEllipsis);
// If we had reached our minimum text size and still don't fit, append an ellipsis }
if(mAddEllipsis && targetTextSize == mMinTextSize && textHeight > height) {
// Draw using a static layout
StaticLayout layout = new StaticLayout(text, textPaint, width, Alignment.ALIGN_NORMAL, mSpacingMult, mSpacingAdd, false);
layout.draw(sTextResizeCanvas);
int lastLine = layout.getLineForVertical(height) - 1;
int start = layout.getLineStart(lastLine);
int end = layout.getLineEnd(lastLine);
float lineWidth = layout.getLineWidth(lastLine);
float ellipseWidth = textPaint.measureText(mEllipsis);
// Trim characters off until we have enough room to draw the ellipsis // Some devices try to auto adjust line spacing, so force default line spacing
while(width < lineWidth + ellipseWidth) { // and invalidate the layout as a side effect
lineWidth = textPaint.measureText(text.subSequence(start, --end + 1).toString()); textPaint.setTextSize(newTextSize);
} //setLineSpacing(spacingAdd, spacingMult);
setText(text.subSequence(0, end) + mEllipsis);
} // Notify the listener if registered
if (mTextResizeListener != null) {
mTextResizeListener.onTextResize(this, oldTextSize, newTextSize);
}
// Some devices try to auto adjust line spacing, so force default line spacing // Reset force resize flag
// and invalidate the layout as a side effect mNeedsResize = false;
textPaint.setTextSize(targetTextSize); }
setLineSpacing(mSpacingAdd, mSpacingMult);
// Notify the listener if registered
if(mTextResizeListener != null) {
mTextResizeListener.onTextResize(this, oldTextSize, targetTextSize);
}
// Reset force resize flag
mNeedsResize = false;
}
// Set the text size of the text paint object and use a static layout to render text off screen before measuring
private int getTextHeight(CharSequence source, TextPaint paint, int width, float textSize) {
// Update the text paint object
paint.setTextSize(textSize);
// Draw using a static layout
StaticLayout layout = new StaticLayout(source, paint, width, Alignment.ALIGN_NORMAL, mSpacingMult, mSpacingAdd, false);
layout.draw(sTextResizeCanvas);
return layout.getHeight();
}
// Set the text size of the text paint object and use a static layout to render text off screen before measuring
private int getTextRect(CharSequence source, TextPaint paint, int width, float textSize) {
// Update the text paint object
paint.setTextSize(textSize);
// Draw using a static layout
StaticLayout layout = new StaticLayout(source, paint, width, Alignment.ALIGN_NORMAL, spacingMult, spacingAdd, false);
layout.draw(sTextResizeCanvas);
return layout.getHeight();
}
} }