我正在寻找一种最佳的方法来调整包装文本在一个TextView,使它将适合它的getHeight和getWidth界限。我不是简单地寻找一种方法来包装文本-我想确保它既包装,又足够小,完全适合在屏幕上。
我在StackOverflow上看到了一些需要自动调整大小的情况,但它们要么是非常特殊的情况下的hack解决方案,没有解决方案,或涉及重新绘制TextView递归直到它足够小(这是内存紧张,迫使用户观看文本收缩一步一步与每次递归)。
但我相信有人已经找到了一个很好的解决方案,它不涉及我正在做的事情:编写几个繁重的例程来解析和测量文本,调整文本的大小,然后重复,直到找到一个合适的小尺寸。
TextView使用什么例程来包装文本?难道这些不能用来预测文本是否足够小吗?
是否有一个最佳实践的方法来自动调整TextView的大小,以适应,包装,在它的getHeight和getWidth边界?
这是一个简单的解决方案,使用TextView本身和textchangedlistens添加到它:
expressionView = (TextView) findViewById(R.id.expressionView);
expressionView.addTextChangedListener(textAutoResizeWatcher(expressionView, 25, 55));
private TextWatcher textAutoResizeWatcher(final TextView view, final int MIN_SP, final int MAX_SP) {
return new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
@Override
public void afterTextChanged(Editable editable) {
final int widthLimitPixels = view.getWidth() - view.getPaddingRight() - view.getPaddingLeft();
Paint paint = new Paint();
float fontSizeSP = pixelsToSp(view.getTextSize());
paint.setTextSize(spToPixels(fontSizeSP));
String viewText = view.getText().toString();
float widthPixels = paint.measureText(viewText);
// Increase font size if necessary.
if (widthPixels < widthLimitPixels){
while (widthPixels < widthLimitPixels && fontSizeSP <= MAX_SP){
++fontSizeSP;
paint.setTextSize(spToPixels(fontSizeSP));
widthPixels = paint.measureText(viewText);
}
--fontSizeSP;
}
// Decrease font size if necessary.
else {
while (widthPixels > widthLimitPixels || fontSizeSP > MAX_SP) {
if (fontSizeSP < MIN_SP) {
fontSizeSP = MIN_SP;
break;
}
--fontSizeSP;
paint.setTextSize(spToPixels(fontSizeSP));
widthPixels = paint.measureText(viewText);
}
}
view.setTextSize(fontSizeSP);
}
};
}
private float pixelsToSp(float px) {
float scaledDensity = getResources().getDisplayMetrics().scaledDensity;
return px/scaledDensity;
}
private float spToPixels(float sp) {
float scaledDensity = getResources().getDisplayMetrics().scaledDensity;
return sp * scaledDensity;
}
这种方法将根据需要增加或减少字体大小以适应文本,尊重作为参数接收的MIN_SP和MAX_SP边界。
这个解决方案适合我们:
public class CustomFontButtonTextFit extends CustomFontButton
{
private final float DECREMENT_FACTOR = .1f;
public CustomFontButtonTextFit(Context context) {
super(context);
}
public CustomFontButtonTextFit(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomFontButtonTextFit(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
}
private synchronized void refitText(String text, int textWidth) {
if (textWidth > 0)
{
float availableWidth = textWidth - this.getPaddingLeft()
- this.getPaddingRight();
TextPaint tp = getPaint();
Rect rect = new Rect();
tp.getTextBounds(text, 0, text.length(), rect);
float size = rect.width();
while(size > availableWidth)
{
setTextSize( getTextSize() - DECREMENT_FACTOR );
tp = getPaint();
tp.getTextBounds(text, 0, text.length(), rect);
size = rect.width();
}
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
refitText(this.getText().toString(), parentWidth);
if(parentWidth < getSuggestedMinimumWidth())
parentWidth = getSuggestedMinimumWidth();
if(parentHeight < getSuggestedMinimumHeight())
parentHeight = getSuggestedMinimumHeight();
this.setMeasuredDimension(parentWidth, parentHeight);
}
@Override
protected void onTextChanged(final CharSequence text, final int start,
final int before, final int after)
{
super.onTextChanged(text, start, before, after);
refitText(text.toString(), this.getWidth());
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh)
{
super.onSizeChanged(w, h, oldw, oldh);
if (w != oldw)
refitText(this.getText().toString(), w);
}
}
这里还有另一个解决方案,只是为了好玩。它可能不是很有效,但它确实处理了文本的高度和宽度,以及有标记的文本。
@Override
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec)
{
if ((MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED)
&& (MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.UNSPECIFIED)) {
final float desiredWidth = MeasureSpec.getSize(widthMeasureSpec);
final float desiredHeight = MeasureSpec.getSize(heightMeasureSpec);
float textSize = getTextSize();
float lastScale = Float.NEGATIVE_INFINITY;
while (textSize > MINIMUM_AUTO_TEXT_SIZE_PX) {
// Measure how big the textview would like to be with the current text size.
super.onMeasure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
// Calculate how much we'd need to scale it to fit the desired size, and
// apply that scaling to the text size as an estimate of what we need.
final float widthScale = desiredWidth / getMeasuredWidth();
final float heightScale = desiredHeight / getMeasuredHeight();
final float scale = Math.min(widthScale, heightScale);
// If we don't need to shrink the text, or we don't seem to be converging, we're done.
if ((scale >= 1f) || (scale <= lastScale)) {
break;
}
// Shrink the text size and keep trying.
textSize = Math.max((float) Math.floor(scale * textSize), MINIMUM_AUTO_TEXT_SIZE_PX);
setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
lastScale = scale;
}
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
我希望这对你有所帮助
import android.content.Context;
import android.graphics.Rect;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.widget.TextView;
/* Based on
* from http://stackoverflow.com/questions/2617266/how-to-adjust-text-font-size-to-fit-textview
*/
public class FontFitTextView extends TextView {
private static float MAX_TEXT_SIZE = 20;
public FontFitTextView(Context context) {
this(context, null);
}
public FontFitTextView(Context context, AttributeSet attrs) {
super(context, attrs);
float size = this.getTextSize();
if (size > MAX_TEXT_SIZE)
setTextSize(MAX_TEXT_SIZE);
}
private void refitText(String text, int textWidth) {
if (textWidth > 0) {
float availableWidth = textWidth - this.getPaddingLeft()
- this.getPaddingRight();
TextPaint tp = getPaint();
Rect rect = new Rect();
tp.getTextBounds(text, 0, text.length(), rect);
float size = rect.width();
if (size > availableWidth)
setTextScaleX(availableWidth / size);
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
refitText(this.getText().toString(), parentWidth);
this.setMeasuredDimension(parentWidth, parentHeight);
}
@Override
protected void onTextChanged(final CharSequence text, final int start,
final int before, final int after) {
refitText(text.toString(), this.getWidth());
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
if (w != oldw) {
refitText(this.getText().toString(), w);
}
}
}
注意:我使用MAX_TEXT_SIZE在文本大小大于20的情况下,因为我不想让大字体适用于我的视图,如果这不是你的情况下,你可以简单地删除它。
我的方法是:
public void changeTextSize(int initialSize, TextView tv) {
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
double width = displayMetrics.widthPixels / displayMetrics.xdpi;
double height = displayMetrics.heightPixels / displayMetrics.ydpi;
Log.i("LOG", "The width of the tested emulator is: " + width);
Log.i("LOG", "The height of the tested emulator is: " + height);
double scale = Math.min(width / 2.25, height / 4.0); //See the logcat >>> width = 2.25 and heigt = 4.0
tv.setTextSize(TypedValue.COMPLEX_UNIT_SP, (int) (initialSize * scale));
}
例如:
changeTextSize(16, findViewById(R.id.myTextView));
changeTextSize(12, findViewById(R.id.myEditText));