例如我正在使用
TableLayout
并向每行添加几个TextView
。由于我不希望TextView
包裹文本,所以我宁愿看到它减小了内容的字体大小。有什么想法吗?
我已经尝试过
measureText
,但是由于我不知道列的大小,因此使用起来似乎很麻烦。这是我要在其中将字体大小更改为适合的代码的代码
TableRow row = new TableRow(this);
for (int i=0; i < ColumnNames.length; i++) {
TextView textColumn = new TextView(this);
textColumn.setText(ColumnNames[i]);
textColumn.setPadding(0, 0, 1, 0);
textColumn.setTextColor(getResources().getColor(R.drawable.text_default));
row.addView(textColumn, new TableRow.LayoutParams());
}
table.addView(row, new TableLayout.LayoutParams());
#1 楼
下面的解决方案在这里结合了所有建议。它从Dunni最初发布的内容开始。它使用类似于gjpc的二进制搜索,但是可读性更高。它还包括gregm的错误修复和我自己的错误修复。import android.content.Context;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.widget.TextView;
public class FontFitTextView extends TextView {
public FontFitTextView(Context context) {
super(context);
initialise();
}
public FontFitTextView(Context context, AttributeSet attrs) {
super(context, attrs);
initialise();
}
private void initialise() {
mTestPaint = new Paint();
mTestPaint.set(this.getPaint());
//max size defaults to the initially specified text size unless it is too small
}
/* Re size the font so the specified text fits in the text box
* assuming the text box is the specified width.
*/
private void refitText(String text, int textWidth)
{
if (textWidth <= 0)
return;
int targetWidth = textWidth - this.getPaddingLeft() - this.getPaddingRight();
float hi = 100;
float lo = 2;
final float threshold = 0.5f; // How close we have to be
mTestPaint.set(this.getPaint());
while((hi - lo) > threshold) {
float size = (hi+lo)/2;
mTestPaint.setTextSize(size);
if(mTestPaint.measureText(text) >= targetWidth)
hi = size; // too big
else
lo = size; // too small
}
// Use lo so that we undershoot rather than overshoot
this.setTextSize(TypedValue.COMPLEX_UNIT_PX, lo);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
int height = getMeasuredHeight();
refitText(this.getText().toString(), parentWidth);
this.setMeasuredDimension(parentWidth, height);
}
@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);
}
}
//Attributes
private Paint mTestPaint;
}
评论
感谢您结合所有反馈。我看到解决方案只考虑宽度。我的问题是喜欢的东西超出了高度。
– AlikElzin-kilaka
2011-12-19 14:19
哦,它似乎在运行时部分起作用。在设备仿真器上,文本被剪切一半。在Eclipse布局编辑器上看起来不错。有任何想法吗?
– AlikElzin-kilaka
2011-12-19 16:27
这篇文章看起来可以解决问题(为hi / lo添加自定义xml属性):stackoverflow.com/a/8090772/156611
– Ed Sinek
2012年6月25日17:05
这是一个很好的解决方案!如果其他任何人都不是android开发的新手,并且不太了解如何在XML中实现扩展视图,则如下所示:
–Casey Murray
13年8月27日在22:45
做得好!使用按钮也可以使用。要照顾textview的高度,只需设置float hi = this.getHeight()-this.getPaddingBottom()-this.getPaddingTop();
– AlexGuti
2014年9月9日在11:32
#2 楼
我编写了一个扩展TextView的类并执行此操作。它只是按照您的建议使用measureText。基本上,它具有一个最大的文本大小和一个最小的文本大小(可以更改),并且只在它们之间的大小上递减1,直到找到适合的最大大小为止。不是特别优雅,但我不知道其他方法。代码如下:
import android.content.Context;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.widget.TextView;
public class FontFitTextView extends TextView {
public FontFitTextView(Context context) {
super(context);
initialise();
}
public FontFitTextView(Context context, AttributeSet attrs) {
super(context, attrs);
initialise();
}
private void initialise() {
testPaint = new Paint();
testPaint.set(this.getPaint());
//max size defaults to the intially specified text size unless it is too small
maxTextSize = this.getTextSize();
if (maxTextSize < 11) {
maxTextSize = 20;
}
minTextSize = 10;
}
/* Re size the font so the specified text fits in the text box
* assuming the text box is the specified width.
*/
private void refitText(String text, int textWidth) {
if (textWidth > 0) {
int availableWidth = textWidth - this.getPaddingLeft() - this.getPaddingRight();
float trySize = maxTextSize;
testPaint.setTextSize(trySize);
while ((trySize > minTextSize) && (testPaint.measureText(text) > availableWidth)) {
trySize -= 1;
if (trySize <= minTextSize) {
trySize = minTextSize;
break;
}
testPaint.setTextSize(trySize);
}
this.setTextSize(trySize);
}
}
@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);
}
}
//Getters and Setters
public float getMinTextSize() {
return minTextSize;
}
public void setMinTextSize(int minTextSize) {
this.minTextSize = minTextSize;
}
public float getMaxTextSize() {
return maxTextSize;
}
public void setMaxTextSize(int minTextSize) {
this.maxTextSize = minTextSize;
}
//Attributes
private Paint testPaint;
private float minTextSize;
private float maxTextSize;
}
评论
二进制搜索通常比线性搜索快。
–特德·霍普(Ted Hopp)
11年1月28日在7:13
我认为在“ onLayout”而不是“ onSizeChanged”进行refitText会更合理
–梁望
16年1月4日在8:16
你能看看我的问题stackoverflow.com/questions/36265448/…
–穆罕默德
16-3-29在13:16
#3 楼
这是speedplane的FontFitTextView
,但仅在需要使文本适合时才减小字体大小,否则保持其字体大小。它不会增加字体大小以适合高度。public class FontFitTextView extends TextView {
// Attributes
private Paint mTestPaint;
private float defaultTextSize;
public FontFitTextView(Context context) {
super(context);
initialize();
}
public FontFitTextView(Context context, AttributeSet attrs) {
super(context, attrs);
initialize();
}
private void initialize() {
mTestPaint = new Paint();
mTestPaint.set(this.getPaint());
defaultTextSize = getTextSize();
}
/* Re size the font so the specified text fits in the text box
* assuming the text box is the specified width.
*/
private void refitText(String text, int textWidth) {
if (textWidth <= 0 || text.isEmpty())
return;
int targetWidth = textWidth - this.getPaddingLeft() - this.getPaddingRight();
// this is most likely a non-relevant call
if( targetWidth<=2 )
return;
// text already fits with the xml-defined font size?
mTestPaint.set(this.getPaint());
mTestPaint.setTextSize(defaultTextSize);
if(mTestPaint.measureText(text) <= targetWidth) {
this.setTextSize(TypedValue.COMPLEX_UNIT_PX, defaultTextSize);
return;
}
// adjust text size using binary search for efficiency
float hi = defaultTextSize;
float lo = 2;
final float threshold = 0.5f; // How close we have to be
while (hi - lo > threshold) {
float size = (hi + lo) / 2;
mTestPaint.setTextSize(size);
if(mTestPaint.measureText(text) >= targetWidth )
hi = size; // too big
else
lo = size; // too small
}
// Use lo so that we undershoot rather than overshoot
this.setTextSize(TypedValue.COMPLEX_UNIT_PX, lo);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
int height = getMeasuredHeight();
refitText(this.getText().toString(), parentWidth);
this.setMeasuredDimension(parentWidth, height);
}
@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 || h != oldh) {
refitText(this.getText().toString(), w);
}
}
}
以下是在xml中如何使用它的示例:
<com.your.package.activity.widget.FontFitTextView
android:id="@+id/my_id"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="My Text"
android:textSize="60sp" />
只要文本适合宽度,这将使字体大小保持为60sp。如果文本较长,则会减小字体大小。在这种情况下,
TextView
的高度也会因为height=wrap_content
而改变。如果发现任何错误,请随时进行编辑。
评论
您是如何使用它的?我在答案中添加了一个示例,说明了如何使用它。我已经使用各种Android版本,模拟器和ADT图形布局编辑器对其进行了测试。
–苏莱
2012年11月23日15:33
本质上,我没有指定特定的高度。您的onMeasure允许视图接管。我设法提出了一个解决方案,将例程的最后一行更改为this.setMeasuredDimension(parentWidth,height);。
–PearsonArtPhoto
2012年11月23日15:59
很好的输入,我更正了代码:)现在,它可以与match_parent和wrap_content一起使用。
–苏莱
2012年11月26日9:50
为什么要测试写为“” .equals(s)而不是简单的s.isEmpty()的空字符串?还是s.length()== 0?不明白为什么有时我会看到这些相等性测试。
–Zordid
13年6月12日在8:52
@PratikButani感谢您指出这一点。等价于text.isEmpty()将是“” .equals(text)。
–苏莱
2014年1月18日17:10
#4 楼
这是我的解决方案,可在模拟器和电话上使用,但在Eclipse布局编辑器上效果不佳。它的灵感来自kilaka的代码,但是文本的大小不是从Paint获取的,而是通过测量TextView本身(调用measure(0, 0)
)获得的。Java类:
public class FontFitTextView extends TextView
{
private static final float THRESHOLD = 0.5f;
private enum Mode { Width, Height, Both, None }
private int minTextSize = 1;
private int maxTextSize = 1000;
private Mode mode = Mode.None;
private boolean inComputation;
private int widthMeasureSpec;
private int heightMeasureSpec;
public FontFitTextView(Context context) {
super(context);
}
public FontFitTextView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public FontFitTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray tAttrs = context.obtainStyledAttributes(attrs, R.styleable.FontFitTextView, defStyle, 0);
maxTextSize = tAttrs.getDimensionPixelSize(R.styleable.FontFitTextView_maxTextSize, maxTextSize);
minTextSize = tAttrs.getDimensionPixelSize(R.styleable.FontFitTextView_minTextSize, minTextSize);
tAttrs.recycle();
}
private void resizeText() {
if (getWidth() <= 0 || getHeight() <= 0)
return;
if(mode == Mode.None)
return;
final int targetWidth = getWidth();
final int targetHeight = getHeight();
inComputation = true;
float higherSize = maxTextSize;
float lowerSize = minTextSize;
float textSize = getTextSize();
while(higherSize - lowerSize > THRESHOLD) {
textSize = (higherSize + lowerSize) / 2;
if (isTooBig(textSize, targetWidth, targetHeight)) {
higherSize = textSize;
} else {
lowerSize = textSize;
}
}
setTextSize(TypedValue.COMPLEX_UNIT_PX, lowerSize);
measure(widthMeasureSpec, heightMeasureSpec);
inComputation = false;
}
private boolean isTooBig(float textSize, int targetWidth, int targetHeight) {
setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
measure(0, 0);
if(mode == Mode.Both)
return getMeasuredWidth() >= targetWidth || getMeasuredHeight() >= targetHeight;
if(mode == Mode.Width)
return getMeasuredWidth() >= targetWidth;
else
return getMeasuredHeight() >= targetHeight;
}
private Mode getMode(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
if(widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY)
return Mode.Both;
if(widthMode == MeasureSpec.EXACTLY)
return Mode.Width;
if(heightMode == MeasureSpec.EXACTLY)
return Mode.Height;
return Mode.None;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if(!inComputation) {
this.widthMeasureSpec = widthMeasureSpec;
this.heightMeasureSpec = heightMeasureSpec;
mode = getMode(widthMeasureSpec, heightMeasureSpec);
resizeText();
}
}
protected void onTextChanged(final CharSequence text, final int start, final int before, final int after) {
resizeText();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
if (w != oldw || h != oldh)
resizeText();
}
public int getMinTextSize() {
return minTextSize;
}
public void setMinTextSize(int minTextSize) {
this.minTextSize = minTextSize;
resizeText();
}
public int getMaxTextSize() {
return maxTextSize;
}
public void setMaxTextSize(int maxTextSize) {
this.maxTextSize = maxTextSize;
resizeText();
}
}
XML属性文件:
<resources>
<declare-styleable name="FontFitTextView">
<attr name="minTextSize" format="dimension" />
<attr name="maxTextSize" format="dimension" />
</declare-styleable>
</resources>
检查我的github以获取此类的最新版本。
我希望它对某人有用。
如果发现错误或代码需要说明,请随时在Github上打开问题。
评论
它可以在我的Galaxy Nexus上正常运行,但是在小屏幕设备上出现问题。
– Tony Ceralva
2012年12月18日下午4:25
在小屏幕设备上您遇到什么问题?
– yDelouis
2012-12-18 18:23
抱歉,问题不在于小屏幕,您的视图不适用于所有装有Android 4.0(包括模拟器)的设备。我在您的GitHub上打开了新期刊
– Tony Ceralva
2012-12-19 3:10
#5 楼
非常感谢https://stackoverflow.com/users/234270/speedplane。很好的答案!这是他的回答的改进版本,它还照顾了身高,并带有maxFontSize属性以限制字体大小(在我的情况下非常有用,因此我想分享它):
package com.<your_package>;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.widget.TextView;
public class FontFitTextView extends TextView
{
private Paint mTestPaint;
private float maxFontSize;
private static final float MAX_FONT_SIZE_DEFAULT_VALUE = 20f;
public FontFitTextView(Context context)
{
super(context);
initialise(context, null);
}
public FontFitTextView(Context context, AttributeSet attributeSet)
{
super(context, attributeSet);
initialise(context, attributeSet);
}
public FontFitTextView(Context context, AttributeSet attributeSet, int defStyle)
{
super(context, attributeSet, defStyle);
initialise(context, attributeSet);
}
private void initialise(Context context, AttributeSet attributeSet)
{
if(attributeSet!=null)
{
TypedArray styledAttributes = context.obtainStyledAttributes(attributeSet, R.styleable.FontFitTextView);
maxFontSize = styledAttributes.getDimension(R.styleable.FontFitTextView_maxFontSize, MAX_FONT_SIZE_DEFAULT_VALUE);
styledAttributes.recycle();
}
else
{
maxFontSize = MAX_FONT_SIZE_DEFAULT_VALUE;
}
mTestPaint = new Paint();
mTestPaint.set(this.getPaint());
//max size defaults to the initially specified text size unless it is too small
}
/* Re size the font so the specified text fits in the text box
* assuming the text box is the specified width.
*/
private void refitText(String text, int textWidth, int textHeight)
{
if (textWidth <= 0)
return;
int targetWidth = textWidth - this.getPaddingLeft() - this.getPaddingRight();
int targetHeight = textHeight - this.getPaddingTop() - this.getPaddingBottom();
float hi = maxFontSize;
float lo = 2;
// final float threshold = 0.5f; // How close we have to be
final float threshold = 1f; // How close we have to be
mTestPaint.set(this.getPaint());
Rect bounds = new Rect();
while ((hi - lo) > threshold)
{
float size = (hi + lo) / 2;
mTestPaint.setTextSize(size);
mTestPaint.getTextBounds(text, 0, text.length(), bounds);
if (bounds.width() >= targetWidth || bounds.height() >= targetHeight)
hi = size; // too big
else
lo = size; // too small
// if (mTestPaint.measureText(text) >= targetWidth)
// hi = size; // too big
// else
// lo = size; // too small
}
// Use lo so that we undershoot rather than overshoot
this.setTextSize(TypedValue.COMPLEX_UNIT_PX, lo);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
int height = getMeasuredHeight();
refitText(this.getText().toString(), parentWidth, height);
this.setMeasuredDimension(parentWidth, height);
}
@Override
protected void onTextChanged(final CharSequence text, final int start, final int before, final int after)
{
refitText(text.toString(), this.getWidth(), this.getHeight());
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh)
{
if (w != oldw)
{
refitText(this.getText().toString(), w, h);
}
}
}
对应的/res/values/attr.xml文件:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="FontFitTextView">
<attr name="maxFontSize" format="dimension" />
</declare-styleable>
</resources>
示例:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:res-auto="http://schemas.android.com/apk/res-auto"
android:id="@+id/home_Layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/background"
tools:ignore="ContentDescription" >
...
<com.<your_package>.FontFitTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:text="Sample Text"
android:textSize="28sp"
res-auto:maxFontSize="35sp"/>
...
</RelativeLayout>
要使用新的
maxFontSize
属性,请不要忘记添加xmlns:res-auto="http://schemas.android.com/apk/res-auto"
,如示例所示。评论
抱歉,但并非在所有情况下都有效。我也认为它不能正确处理多行。
– Android开发人员
2013年4月14日9:00
哦..你能给我一个不起作用的案例吗? (不支持多行,您是对的)
–帕斯卡
13年4月15日在10:14
很多情况。我创建了一个随机测试器只是为了证明它是正确的。这是一个示例:视图的宽度:317px,高度:137px,文本:“ q7Lr”。我看到的是:tinypic.com/view.php?pic=2dv5yf9&s=6。这是我制作的示例项目:mega.co.nz/…。我认为这样的视图应该处理多行,支持任何大小的字体,处理高度而不仅仅是宽度,...可悲的是,我发现的所有示例都没有很好地工作。
– Android开发人员
13年4月15日在13:32
好吧,绝对不是一个完整的解决方案。抱歉。暂时没有时间改进它。如果您可以改进它,请毫不犹豫地发布您的解决方案。
–帕斯卡
13年4月15日在13:51
我创建了一个新线程来显示我的测试,希望有人能够提供一个好的解决方案:stackoverflow.com/questions/16017165/…
– Android开发人员
13年4月15日在14:01
#6 楼
现在,您无需第三方库或小部件即可执行此操作。它内置在API级别26的TextView中。将android:autoSizeTextType="uniform"
添加到TextView
中并为其设置高度。就这样。使用app:autoSizeTextType="uniform"
实现向后兼容性https://developer.android.com/guide/topics/ui/look-and-feel/autosizing-textview.html
<?xml version="1.0" encoding="utf-8"?>
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:autoSizeTextType="uniform" />
您也可以使用
TextViewCompat
兼容性。评论
使用app:autoSizeTextType =“ uniform”以获得向后兼容性,因为android:autoSizeTextType =“ uniform”仅在API级别26和更高版本中起作用。
– Atul Bhardwaj
7月23日9:55
#7 楼
我遇到了同样的问题,并写了一个似乎对我有用的课程。基本上,我使用静态布局在单独的画布上绘制文本并重新测量,直到找到合适的字体大小为止。您可以在下面的主题中看到发布的课程。希望对您有所帮助。自动缩放TextView文本以适合边界
#8 楼
略微修改onMeasure:@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);
}
,并在refitText上进行二进制搜索:
private void refitText(String text, int textWidth)
{
if (textWidth > 0)
{
int availableWidth = textWidth - this.getPaddingLeft() - this.getPaddingRight();
int trySize = (int)maxTextSize;
int increment = ~( trySize - (int)minTextSize ) / 2;
testPaint.setTextSize(trySize);
while ((trySize > minTextSize) && (testPaint.measureText(text) > availableWidth))
{
trySize += increment;
increment = ( increment == 0 ) ? -1 : ~increment / 2;
if (trySize <= minTextSize)
{
trySize = (int)minTextSize;
break;
}
testPaint.setTextSize(trySize);
}
this.setTextSize( TypedValue.COMPLEX_UNIT_PX, trySize);
}
}
评论
代替二进制搜索,一个简单的缩放效果很好:trySize * = availableWidth / measured_width(然后钳制为minTextSize)。
–特德·霍普(Ted Hopp)
2011年3月2日,0:05
#9 楼
我发现以下对我来说很好用。它不会循环,并且会同时考虑高度和宽度。请注意,在视图上调用setTextSize时,指定PX单元很重要。感谢以前的帖子中的提示!Paint paint = adjustTextSize(getPaint(), numChars, maxWidth, maxHeight);
setTextSize(TypedValue.COMPLEX_UNIT_PX,paint.getTextSize());
这是我使用的例程,从视图中传入getPaint()。带有“宽”字符的10个字符串用于估计宽度,而与实际字符串无关。
private static final String text10="OOOOOOOOOO";
public static Paint adjustTextSize(Paint paint, int numCharacters, int widthPixels, int heightPixels) {
float width = paint.measureText(text10)*numCharacters/text10.length();
float newSize = (int)((widthPixels/width)*paint.getTextSize());
paint.setTextSize(newSize);
// remeasure with font size near our desired result
width = paint.measureText(text10)*numCharacters/text10.length();
newSize = (int)((widthPixels/width)*paint.getTextSize());
paint.setTextSize(newSize);
// Check height constraints
FontMetricsInt metrics = paint.getFontMetricsInt();
float textHeight = metrics.descent-metrics.ascent;
if (textHeight > heightPixels) {
newSize = (int)(newSize * (heightPixels/textHeight));
paint.setTextSize(newSize);
}
return paint;
}
评论
您如何将其合并到布局xml中?
– AlikElzin-kilaka
2011-12-19 17:11
此代码对于将现有视图中的文本放置在受限制的大小区域中很有用,或者您可以从TextView创建自己的派生类并覆盖onMeasure,如其他文章中所示。就其本身而言,不能在布局中使用。
– Glenn
2012年2月3日在23:20
确保在绘制视图后检查其尺寸。在onCreate()期间执行此操作还为时过早。我使用ViewTreeObserver来确保在正确的时间进行了测量。
– SMBiggs
15年8月27日在3:01
#10 楼
使用app:autoSizeTextType="uniform"
以获得向后兼容性,因为android:autoSizeTextType="uniform"
仅在API级别26和更高版本中起作用。评论
谢谢苏拉杰。这是最好的解决方案
– Atul Bhardwaj
7月23日10:02
我在SDK 24上检查了它,但它不起作用。您确定该应用程序:..适用于SDK 26以下的版本吗?
–乔治·切博塔列夫(Georgiy Chebotarev)
9月4日13:35
是的,它作为应用程序名称空间提供向后兼容性。有一篇关于它的详细文章,检查一下也许可以解决您的问题。 medium.com/over-engineering/…
–苏拉杰·维斯纳夫
9月4日14:07
#11 楼
可以进行修改您需要像这样设置文本视图的大小,因为否则setTextSize会假定该值以SP单位表示:
setTextSize(TypedValue.COMPLEX_UNIT_PX, trySize);
您需要显式添加此代码。
@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);
}
#12 楼
在我找到这个库之前,我一直在项目中苦苦挣扎,直到找到这个库:compile 'me.grantland:autofittextview:0.2.+'
您只需要根据需要添加xml即可。例如:
<me.grantland.widget.AutofitTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:maxLines="2"
android:textSize="40sp"
autofit:minTextSize="16sp"
/>
#13 楼
我在上面使用了Dunni解决方案的一种变体,但是特定的代码对我不起作用。特别是,当尝试使用Paint对象集具有视图的Paint对象的特征,然后调用measureText()时,它不会返回与直接调用视图的Paint对象相同的值。也许我的视图的设置方式有所不同,从而使行为有所不同。我的解决方案是直接使用视图的Paint,即使更改字体大小可能会导致性能下降多次查看。
#14 楼
我一直在努力改进Speedplane的出色解决方案,并提出了这个建议。它管理高度,包括设置边距,以使文本应在垂直方向上正确居中。它使用相同的功能来获得宽度,因为它看起来效果最好,但是使用了不同的方法函数获取高度,因为没有任何地方提供高度。需要进行一些更正,但是我想出了一种方法,同时看起来很悦目。
import android.content.Context;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.widget.TextView;
public class FontFitTextView extends TextView {
public FontFitTextView(Context context) {
super(context);
initialize();
}
public FontFitTextView(Context context, AttributeSet attrs) {
super(context, attrs);
initialize();
}
private void initialize() {
mTestPaint = new Paint();
mTestPaint.set(this.getPaint());
//max size defaults to the initially specified text size unless it is too small
}
/* Re size the font so the specified text fits in the text box
* assuming the text box is the specified width.
*/
private void refitText(String text, int textWidth,int textHeight)
{
if (textWidth <= 0)
return;
int targetWidth = textWidth - this.getPaddingLeft() - this.getPaddingRight();
int targetHeight = textHeight - this.getPaddingTop() - this.getPaddingBottom();
float hi = Math.min(targetHeight,100);
float lo = 2;
final float threshold = 0.5f; // How close we have to be
Rect bounds = new Rect();
mTestPaint.set(this.getPaint());
while((hi - lo) > threshold) {
float size = (hi+lo)/2;
mTestPaint.setTextSize(size);
mTestPaint.getTextBounds(text, 0, text.length(), bounds);
if((mTestPaint.measureText(text)) >= targetWidth || (1+(2*(size+(float)bounds.top)-bounds.bottom)) >=targetHeight)
hi = size; // too big
else
lo = size; // too small
}
// Use lo so that we undershoot rather than overshoot
this.setTextSize(TypedValue.COMPLEX_UNIT_PX,(float) lo);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
int height = getMeasuredHeight();
refitText(this.getText().toString(), parentWidth,height);
this.setMeasuredDimension(parentWidth, height);
}
@Override
protected void onTextChanged(final CharSequence text, final int start, final int before, final int after) {
refitText(text.toString(), this.getWidth(),this.getHeight());
}
@Override
protected void onSizeChanged (int w, int h, int oldw, int oldh) {
if (w != oldw) {
refitText(this.getText().toString(), w,h);
}
}
//Attributes
private Paint mTestPaint;
}
#15 楼
Google已经启用了此功能。<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:autoSizeTextType="uniform" />
https://developer.android.com/guide/topics/ui/look-and-feel/autosizing-textview.html
#16 楼
受以前的海报启发,我想分享我的解决方案。它与缩放系数一起使用,该缩放系数应用于先前的字体大小以使其适合可用空间。除了防止TextViews onDraw方法发生意外行为外,它还可以自己绘制文本。public class FontFitTextView extends TextView {
// How much of the available space should be used in percent.
private static final float MARGINHEIGHT = 0.8f;
private static final float MARGINWIDTH = 0.8f;
private Paint paint;
private int viewWidth;
private int viewHeight;
private float textHeight;
private float textWidth;
public FontFitTextView(Context c) {
this(c, null);
}
public FontFitTextView(Context c, AttributeSet attrs) {
super(c, attrs);
initComponent();
}
// Default constructor override
public FontFitTextView(Context c, AttributeSet attrs, int defStyle) {
super(c, attrs, defStyle);
initComponent();
}
private void initComponent() {
paint = new Paint();
paint.setTextSize(30);
paint.setTextAlign(Align.CENTER);
paint.setAntiAlias(true);
}
public void setFontColor(int c) {
paint.setColor(c);
}
private void calcTextSize(String s, Canvas c) {
float availableHeight = viewHeight;
float availableWidth = viewWidth;
// This value scales the old font up or down to match the available
// space.
float scale = 1.0f;
// Rectangle for measuring the text dimensions
Rect rect = new Rect();
float oldFontSize = paint.getTextSize();
// Calculate the space used with old font size
paint.getTextBounds(s, 0, s.length(), rect);
textWidth = rect.width();
textHeight = rect.height();
// find scale-value to fit the text horizontally
float scaleWidth = 1f;
if (textWidth > 0.0f) {
scaleWidth = (availableWidth) / textWidth * MARGINWIDTH;
}
// find scale-value to fit the text vertically
float scaleHeight = 1f;
if (textHeight > 0.0f) {
scaleHeight = (availableHeight) / textHeight * MARGINHEIGHT;
}
// We are always limited by the smaller one
if (scaleWidth < scaleHeight) {
scale = scaleWidth;
} else {
scale = scaleHeight;
}
// We apply the scale to the old font size to make it bigger or smaller
float newFontSize = (oldFontSize * scale);
paint.setTextSize(newFontSize);
}
/**
* Calculates the origin on the Y-Axis (width) for the text in this view.
*
* @return
*/
private float calcStartDrawingPosX() {
float left = getMeasuredWidth();
float centerY = left - (viewWidth / 2);
return centerY;
}
/**
* Calculates the origin on the Y-Axis (height) for the text in this view.
*
* @return
*/
private float calcStartDrawingPosY() {
float bottom = getMeasuredHeight();
// The paint only centers horizontally, origin on the Y-Axis stays at
// the bottom, thus we have to lift the origin additionally by the
// height of the font.
float centerX = bottom - (viewHeight / 2) + (textHeight / 2);
return centerX;
}
@Override
protected void onDraw(Canvas canvas) {
String text = getText().toString();
if (text.length() > 0) {
calcTextSize(text, canvas);
canvas.drawText(text, calcStartDrawingPosX(),
calcStartDrawingPosY(), paint);
}
};
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
viewWidth = w;
viewHeight = h;
super.onSizeChanged(w, h, oldw, oldh);
}
}
#17 楼
/* get your context */
Context c = getActivity().getApplicationContext();
LinearLayout l = new LinearLayout(c);
l.setOrientation(LinearLayout.VERTICAL);
LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, 0);
l.setLayoutParams(params);
l.setBackgroundResource(R.drawable.border);
TextView tv=new TextView(c);
tv.setText(" your text here");
/* set typeface if needed */
Typeface tf = Typeface.createFromAsset(c.getAssets(),"fonts/VERDANA.TTF");
tv.setTypeface(tf);
// LayoutParams lp = new LayoutParams();
tv.setTextColor(Color.parseColor("#282828"));
tv.setGravity(Gravity.CENTER | Gravity.BOTTOM);
// tv.setLayoutParams(lp);
tv.setTextSize(20);
l.addView(tv);
return l;
评论
仅阅读您的代码,我认为这不会起作用。记住,问题是关于自动调整文本视图的字体大小以适合视图的问题。您设置一个固定的字体大小。
–苏莱
13 Mar 15 '13 at 14:55
#18 楼
这应该是一个简单的解决方案:public void correctWidth(TextView textView, int desiredWidth)
{
Paint paint = new Paint();
Rect bounds = new Rect();
paint.setTypeface(textView.getTypeface());
float textSize = textView.getTextSize();
paint.setTextSize(textSize);
String text = textView.getText().toString();
paint.getTextBounds(text, 0, text.length(), bounds);
while (bounds.width() > desiredWidth)
{
textSize--;
paint.setTextSize(textSize);
paint.getTextBounds(text, 0, text.length(), bounds);
}
textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
}
#19 楼
扩展TextView并使用以下代码覆盖onDraw。它将保持文本的宽高比,但调整其大小以填充空间。您可以根据需要轻松修改代码以进行拉伸。 @Override
protected void onDraw(@NonNull Canvas canvas) {
TextPaint textPaint = getPaint();
textPaint.setColor(getCurrentTextColor());
textPaint.setTextAlign(Paint.Align.CENTER);
textPaint.drawableState = getDrawableState();
String text = getText().toString();
float desiredWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight() - 2;
float desiredHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom() - 2;
float textSize = textPaint.getTextSize();
for (int i = 0; i < 10; i++) {
textPaint.getTextBounds(text, 0, text.length(), rect);
float width = rect.width();
float height = rect.height();
float deltaWidth = width - desiredWidth;
float deltaHeight = height - desiredHeight;
boolean fitsWidth = deltaWidth <= 0;
boolean fitsHeight = deltaHeight <= 0;
if ((fitsWidth && Math.abs(deltaHeight) < 1.0)
|| (fitsHeight && Math.abs(deltaWidth) < 1.0)) {
// close enough
break;
}
float adjustX = desiredWidth / width;
float adjustY = desiredHeight / height;
textSize = textSize * (adjustY < adjustX ? adjustY : adjustX);
// adjust text size
textPaint.setTextSize(textSize);
}
float x = desiredWidth / 2f;
float y = desiredHeight / 2f - rect.top - rect.height() / 2f;
canvas.drawText(text, x, y, textPaint);
}
#20 楼
我编写了一个简短的帮助程序类,该类使textview在一定宽度内适合,如果无法达到最小textsize,则在末尾添加ellipsize“ ...”。请注意,它只会使缩小文本,直到适合为止或达到最小文本大小。要使用大尺寸进行测试,请在调用help方法之前将textsize设置为一个大数字。它需要Pixels,所以如果您使用dimen中的值,则可以这样调用:
float minTextSizePx = getResources().getDimensionPixelSize(R.dimen.min_text_size);
float maxTextWidthPx = getResources().getDimensionPixelSize(R.dimen.max_text_width);
WidgetUtils.fitText(textView, text, minTextSizePx, maxTextWidthPx);
这是我使用的类:
public class WidgetUtils {
public static void fitText(TextView textView, String text, float minTextSizePx, float maxWidthPx) {
textView.setEllipsize(null);
int size = (int)textView.getTextSize();
while (true) {
Rect bounds = new Rect();
Paint textPaint = textView.getPaint();
textPaint.getTextBounds(text, 0, text.length(), bounds);
if(bounds.width() < maxWidthPx){
break;
}
if (size <= minTextSizePx) {
textView.setEllipsize(TextUtils.TruncateAt.END);
break;
}
size -= 1;
textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
}
}
}
评论
这是否支持多行文本?
–鲁蒙森
16年2月18日在2:18
#21 楼
如果设置了类似allCaps的转换,那么Speedplane的方法就会出现问题。我对其进行了修复,得到以下代码(对不起,我的名声不允许我将此添加为Speedplane解决方案的注释):import android.content.Context;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.widget.TextView;
public class FontFitTextView extends TextView {
public FontFitTextView(Context context) {
super(context);
initialise();
}
public FontFitTextView(Context context, AttributeSet attrs) {
super(context, attrs);
initialise();
}
private void initialise() {
mTestPaint = new Paint();
mTestPaint.set(this.getPaint());
//max size defaults to the initially specified text size unless it is too small
}
/* Re size the font so the specified text fits in the text box
* assuming the text box is the specified width.
*/
private void refitText(String text, int textWidth)
{
if (getTransformationMethod() != null) {
text = getTransformationMethod().getTransformation(text, this).toString();
}
if (textWidth <= 0)
return;
int targetWidth = textWidth - this.getPaddingLeft() - this.getPaddingRight();
float hi = 100;
float lo = 2;
final float threshold = 0.5f; // How close we have to be
mTestPaint.set(this.getPaint());
while((hi - lo) > threshold) {
float size = (hi+lo)/2;
if(mTestPaint.measureText(text) >= targetWidth)
hi = size; // too big
else
lo = size; // too small
}
// Use lo so that we undershoot rather than overshoot
this.setTextSize(TypedValue.COMPLEX_UNIT_PX, lo);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
int height = getMeasuredHeight();
refitText(this.getText().toString(), parentWidth);
this.setMeasuredDimension(parentWidth, height);
}
@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);
}
}
//Attributes
private Paint mTestPaint;
}
#22 楼
我不知道这是正确的方法还是无法正常工作...请查看您的视图并检查OnGlobalLayoutListener()并获取textview线数,然后设置textSize。 yourView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
if (textView.getLineCount()>=3) {
textView.setTextSize(20);
}else{
//add somthing
}
}
});
它非常简单的几行代码..
评论
在此处检查基于dunni的代码的解决方案stackoverflow.com/questions/5033012/…注意:我没有使用循环来实现它。 PS:谢谢dunni