ฉันเริ่มด้วยคลาส AutoResizeTextView ของ Chase และทำการเปลี่ยนแปลงเล็กน้อยดังนั้นมันจะพอดีกับทั้งแนวตั้งและแนวนอน
ฉันยังค้นพบข้อผิดพลาดที่ทำให้ Null Pointer Exception ใน Layout Editor (ใน Eclipse) ภายใต้เงื่อนไขที่ค่อนข้างคลุมเครือ
เปลี่ยน 1: ใส่ข้อความทั้งแนวตั้งและแนวนอน
เวอร์ชันดั้งเดิมของ Chase จะลดขนาดข้อความจนกว่าจะพอดีในแนวตั้ง แต่อนุญาตให้ข้อความกว้างกว่าเป้าหมาย ในกรณีของฉันฉันต้องการข้อความให้พอดีกับความกว้างที่ระบุ
การเปลี่ยนแปลงนี้ทำให้การปรับขนาดจนกว่าข้อความจะพอดีทั้งในแนวตั้งและแนวนอน
ในresizeText(
int ,
int)
เปลี่ยนแปลงจาก:
// Get the required text height
int textHeight = getTextHeight(text, textPaint, width, targetTextSize);
// Until we either fit within our text view or we had reached our min text size, incrementally try smaller sizes
while(textHeight > height && targetTextSize > mMinTextSize) {
targetTextSize = Math.max(targetTextSize - 2, mMinTextSize);
textHeight = getTextHeight(text, textPaint, width, targetTextSize);
}
ถึง:
// Get the required text height
int textHeight = getTextHeight(text, textPaint, width, targetTextSize);
int textWidth = getTextWidth(text, textPaint, width, targetTextSize);
// Until we either fit within our text view or we had reached our min text size, incrementally try smaller sizes
while(((textHeight >= height) || (textWidth >= width) ) && targetTextSize > mMinTextSize) {
targetTextSize = Math.max(targetTextSize - 2, mMinTextSize);
textHeight = getTextHeight(text, textPaint, width, targetTextSize);
textWidth = getTextWidth(text, textPaint, width, targetTextSize);
}
จากนั้นในตอนท้ายของไฟล์ให้ผนวกgetTextWidth()
รูทีน getTextHeight()
มันเป็นเพียงการแก้ไขเล็กน้อย มันอาจจะมีประสิทธิภาพมากกว่าในการรวมเข้ากับรูทีนหนึ่งซึ่งจะส่งกลับทั้งความสูงและความกว้าง
// Set the text size of the text paint object and use a static layout to render text off screen before measuring
private int getTextWidth(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, true);
layout.draw(sTextResizeCanvas);
return layout.getWidth();
}
เปลี่ยน 2: แก้ไข EmptyStackException ใน Eclipse Android Layout Editor
ภายใต้เงื่อนไขที่ค่อนข้างคลุมเครือและแม่นยำมากตัวแก้ไขเลย์เอาท์จะล้มเหลวในการแสดงการแสดงผลกราฟิกของเลย์เอาต์ มันจะโยนข้อยกเว้น "EmptyStackException: null" ใน com.android.ide.eclipse.adt
เงื่อนไขที่ต้องการคือ:
- สร้างวิดเจ็ต AutoResizeTextView
- สร้างสไตล์สำหรับวิดเจ็ตนั้น
- ระบุรายการข้อความในสไตล์; ไม่ได้อยู่ในคำจำกัดความของวิดเจ็ต
ในขณะที่:
ความละเอียด / รูปแบบ / main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<com.ajw.DemoCrashInADT.AutoResizeTextView
android:id="@+id/resizingText"
style="@style/myTextStyle" />
</LinearLayout>
ความละเอียด / ค่า / myStyles.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="myTextStyle" parent="@android:style/Widget.TextView">
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_width">fill_parent</item>
<item name="android:text">some message</item>
</style>
</resources>
ด้วยไฟล์เหล่านี้การเลือกแท็บGraphical Layoutเมื่อแก้ไขmain.xml
จะปรากฏขึ้น:
ข้อผิดพลาด!
EmptyStackException: null
รายละเอียดข้อยกเว้นถูกบันทึกไว้ในหน้าต่าง> แสดงมุมมอง> บันทึกข้อผิดพลาด
แทนมุมมองกราฟิกของเค้าโครง
เพื่อให้เรื่องที่สั้นเกินไปยาวเกินไปฉันได้ติดตามเรื่องนี้ในบรรทัดต่อไปนี้ (อีกครั้งในresizeText
):
// 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;
ปัญหาคือว่าภายใต้เงื่อนไขที่เฉพาะเจาะจง mTextSize จะไม่เริ่มต้น; มันมีค่า 0
ด้วยด้านบนtargetTextSize
ตั้งเป็นศูนย์ (เป็นผลมาจาก Math.min)
ศูนย์นั้นถูกส่งผ่านไปยังgetTextHeight()
(และgetTextWidth()
) เป็นtextSize
อาร์กิวเมนต์ เมื่อมันมาถึง
layout.draw(sTextResizeCanvas);
เราจะได้รับการยกเว้น
การทดสอบว่ามีประสิทธิภาพมากกว่า(mTextSize == 0)
ในตอนต้นหรือresizeText()
ไม่มากกว่าการทดสอบgetTextHeight()
และgetTextWidth()
; การทดสอบก่อนหน้านี้บันทึกงานที่แทรกแซงทั้งหมด
ด้วยการอัปเดตเหล่านี้ไฟล์ (เช่นเดียวกับในแอพทดสอบความผิดพลาดของฉัน) คือ:
//
// from: http://stackoverflow.com/questions/5033012/auto-scale-textview-text-to-fit-within-bounds
//
//
package com.ajw.DemoCrashInADT;
import android.content.Context;
import android.graphics.Canvas;
import android.text.Layout.Alignment;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.widget.TextView;
/**
* Text view that auto adjusts text size to fit within the view. If the text
* size equals the minimum text size and still does not fit, append with an
* ellipsis.
*
* 2011-10-29 changes by Alan Jay Weiner
* * change to fit both vertically and horizontally
* * test mTextSize for 0 in resizeText() to fix exception in Layout Editor
*
* @author Chase Colburn
* @since Apr 4, 2011
*/
public class AutoResizeTextView extends TextView {
// Minimum text size for this text view
public static final float MIN_TEXT_SIZE = 20;
// Interface for resize notifications
public interface OnTextResizeListener {
public void onTextResize(TextView textView, float oldSize, float newSize);
}
// Off screen canvas for text size rendering
private static final Canvas sTextResizeCanvas = new Canvas();
// Our ellipse string
private static final String mEllipsis = "...";
// Registered resize listener
private OnTextResizeListener mTextResizeListener;
// Flag for text and/or size changes to force a resize
private boolean mNeedsResize = false;
// Text size that is set from code. This acts as a starting point for
// resizing
private float mTextSize;
// Temporary upper bounds on the starting text size
private float mMaxTextSize = 0;
// Lower bounds for text size
private float mMinTextSize = MIN_TEXT_SIZE;
// Text view line spacing multiplier
private float mSpacingMult = 1.0f;
// Text view additional line spacing
private float mSpacingAdd = 0.0f;
// Add ellipsis to text that overflows at the smallest text size
private boolean mAddEllipsis = true;
// Default constructor override
public AutoResizeTextView(Context context) {
this(context, null);
}
// Default constructor when inflating from XML file
public AutoResizeTextView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
// Default constructor override
public AutoResizeTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mTextSize = getTextSize();
}
/**
* When text changes, set the force resize flag to true and reset the text
* size.
*/
@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 reset the text size
resetTextSize();
}
/**
* If the text view size changed, set the force resize flag to true
*/
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
if (w != oldw || h != oldh) {
mNeedsResize = true;
}
}
/**
* Register listener to receive resize notifications
*
* @param listener
*/
public void setOnResizeListener(OnTextResizeListener listener) {
mTextResizeListener = listener;
}
/**
* Override the set text size to update our internal reference values
*/
@Override
public void setTextSize(float size) {
super.setTextSize(size);
mTextSize = getTextSize();
}
/**
* Override the set text size to update our internal reference values
*/
@Override
public void setTextSize(int unit, float size) {
super.setTextSize(unit, size);
mTextSize = getTextSize();
}
/**
* Override the set line spacing to update our internal reference values
*/
@Override
public void setLineSpacing(float add, float mult) {
super.setLineSpacing(add, mult);
mSpacingMult = mult;
mSpacingAdd = add;
}
/**
* Set the upper text size limit and invalidate the view
*
* @param maxTextSize
*/
public void setMaxTextSize(float maxTextSize) {
mMaxTextSize = maxTextSize;
requestLayout();
invalidate();
}
/**
* Return upper text size limit
*
* @return
*/
public float getMaxTextSize() {
return mMaxTextSize;
}
/**
* 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
*
* @return
*/
public float getMinTextSize() {
return mMinTextSize;
}
/**
* Set flag to add ellipsis to text that overflows at the smallest text size
*
* @param addEllipsis
*/
public void setAddEllipsis(boolean addEllipsis) {
mAddEllipsis = addEllipsis;
}
/**
* Return flag to add ellipsis to text that overflows at the smallest text
* size
*
* @return
*/
public boolean getAddEllipsis() {
return mAddEllipsis;
}
/**
* Reset the text to the original size
*/
public void resetTextSize() {
super.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSize);
mMaxTextSize = mTextSize;
}
/**
* Resize text after measuring
*/
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
if (changed || mNeedsResize) {
int widthLimit = (right - left) - getCompoundPaddingLeft()
- getCompoundPaddingRight();
int heightLimit = (bottom - top) - getCompoundPaddingBottom()
- getCompoundPaddingTop();
resizeText(widthLimit, heightLimit);
}
super.onLayout(changed, left, top, right, bottom);
}
/**
* Resize the text size with default width and height
*/
public void resizeText() {
int heightLimit = getHeight() - getPaddingBottom() - getPaddingTop();
int widthLimit = getWidth() - getPaddingLeft() - getPaddingRight();
resizeText(widthLimit, heightLimit);
}
/**
* Resize the text size with specified width and height
*
* @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
// or if mTextSize has not been initialized
if (text == null || text.length() == 0 || height <= 0 || width <= 0
|| mTextSize == 0) {
return;
}
// Get the text view's paint object
TextPaint textPaint = getPaint();
// Store the current text size
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
int textHeight = getTextHeight(text, textPaint, width, targetTextSize);
int textWidth = getTextWidth(text, textPaint, width, targetTextSize);
// Until we either fit within our text view or we had reached our min
// text size, incrementally try smaller sizes
while (((textHeight > height) || (textWidth > width))
&& targetTextSize > mMinTextSize) {
targetTextSize = Math.max(targetTextSize - 2, mMinTextSize);
textHeight = getTextHeight(text, textPaint, width, targetTextSize);
textWidth = getTextWidth(text, textPaint, width, targetTextSize);
}
// 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
while (width < lineWidth + ellipseWidth) {
lineWidth = textPaint.measureText(text.subSequence(start, --end + 1)
.toString());
}
setText(text.subSequence(0, end) + mEllipsis);
}
// Some devices try to auto adjust line spacing, so force default line
// spacing
// and invalidate the layout as a side effect
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, true);
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 getTextWidth(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, true);
layout.draw(sTextResizeCanvas);
return layout.getWidth();
}
}
ขอบคุณมากสำหรับ Chase ที่โพสต์โค้ดเริ่มต้น ฉันสนุกกับการอ่านผ่านเพื่อดูว่ามันทำงานอย่างไรและฉันยินดีที่จะเพิ่มเข้าไป