我正在尝试创建一种方法来调整TextView
中的多行文本的大小,使其适合TextView
的边界(X和Y维度)。
目前,我有一些东西,但它所做的只是调整文本的大小,使文本的第一个字母/字符填充TextView
的尺寸(即只有第一个字母可见,而且它是巨大)。我需要它来适应TextView边界内文本的所有行。
这是我到目前为止所做的:
public static void autoScaleTextViewTextToHeight(TextView tv)
{
final float initSize = tv.getTextSize();
//get the width of the view's back image (unscaled)....
float minViewHeight;
if(tv.getBackground()!=null)
{
minViewHeight = tv.getBackground().getIntrinsicHeight();
}
else
{
minViewHeight = 10f;//some min.
}
final float maxViewHeight = tv.getHeight() - (tv.getPaddingBottom()+tv.getPaddingTop())-12;// -12 just to be sure
final String s = tv.getText().toString();
//System.out.println(""+tv.getPaddingTop()+"/"+tv.getPaddingBottom());
if(minViewHeight >0 && maxViewHeight >2)
{
Rect currentBounds = new Rect();
tv.getPaint().getTextBounds(s, 0, s.length(), currentBounds);
//System.out.println(""+initSize);
//System.out.println(""+maxViewHeight);
//System.out.println(""+(currentBounds.height()));
float resultingSize = 1;
while(currentBounds.height() < maxViewHeight)
{
resultingSize ++;
tv.setTextSize(resultingSize);
tv.getPaint().getTextBounds(s, 0, s.length(), currentBounds);
//System.out.println(""+(currentBounds.height()+tv.getPaddingBottom()+tv.getPaddingTop()));
//System.out.println("Resulting: "+resultingSize);
}
if(currentBounds.height()>=maxViewHeight)
{
//just to be sure, reduce the value
tv.setTextSize(resultingSize-1);
}
}
}
我认为问题在于使用tv.getPaint().getTextBounds(...)
。它始终返回文本边界的小数字...相对于tv.getWidth()
和tv.getHeight()
值较小...即使文本大小远远大于{{1}的宽度或高度}。
答案 0 :(得分:20)
MavenCentral的AutofitTextView库很好地处理了这个问题。该来源位于Github(1k +星)https://github.com/grantland/android-autofittextview
将以下内容添加到app/build.gradle
repositories {
mavenCentral()
}
dependencies {
compile 'me.grantland:autofittextview:0.2.+'
}
在代码中启用任何View扩展TextView:
AutofitHelper.create(textView);
启用任何以XML格式扩展TextView的View:
<me.grantland.widget.AutofitLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
/>
</me.grantland.widget.AutofitLayout>
在代码或XML中使用内置的Widget:
<me.grantland.widget.AutofitTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
/>
答案 1 :(得分:5)
Android O以来的新功能:
https://developer.android.com/preview/features/autosizing-textview.html
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:autoSizeTextType="uniform"
android:autoSizeMinTextSize="12sp"
android:autoSizeMaxTextSize="100sp"
android:autoSizeStepGranularity="2sp"
/>
答案 2 :(得分:3)
我能够使用以下代码回答我自己的问题(见下文),但我的解决方案非常特定于应用程序。例如,这可能只是看起来很好和/或适用于大小约为的TextView。屏幕的1/2(同时还有40px的上边距和20px的边距......没有底边距)。
使用这种方法,您可以创建自己的类似实现。静态方法基本上只查看字符数并确定应用于TextView文本大小的缩放因子,然后逐渐增加文本大小直到整体高度(估计高度 - 使用文本的宽度,文本高度和TextView的宽度刚好低于TextView的宽度。确定缩放因子所需的参数(即if / else if语句)是通过猜测和检查来设置的。您可能需要使用这些数字来使其适用于您的特定应用程序。
这不是最优雅的解决方案,虽然它很容易编码,但它对我有用。有没有人有更好的方法?
public static void autoScaleTextViewTextToHeight(final TextView tv, String s)
{
float currentWidth=tv.getPaint().measureText(s);
int scalingFactor = 0;
final int characters = s.length();
//scale based on # of characters in the string
if(characters<5)
{
scalingFactor = 1;
}
else if(characters>=5 && characters<10)
{
scalingFactor = 2;
}
else if(characters>=10 && characters<15)
{
scalingFactor = 3;
}
else if(characters>=15 && characters<20)
{
scalingFactor = 3;
}
else if(characters>=20 && characters<25)
{
scalingFactor = 3;
}
else if(characters>=25 && characters<30)
{
scalingFactor = 3;
}
else if(characters>=30 && characters<35)
{
scalingFactor = 3;
}
else if(characters>=35 && characters<40)
{
scalingFactor = 3;
}
else if(characters>=40 && characters<45)
{
scalingFactor = 3;
}
else if(characters>=45 && characters<50)
{
scalingFactor = 3;
}
else if(characters>=50 && characters<55)
{
scalingFactor = 3;
}
else if(characters>=55 && characters<60)
{
scalingFactor = 3;
}
else if(characters>=60 && characters<65)
{
scalingFactor = 3;
}
else if(characters>=65 && characters<70)
{
scalingFactor = 3;
}
else if(characters>=70 && characters<75)
{
scalingFactor = 3;
}
else if(characters>=75)
{
scalingFactor = 5;
}
//System.out.println(((int)Math.ceil(currentWidth)/tv.getWidth()+scalingFactor));
//the +scalingFactor is important... increase this if nec. later
while((((int)Math.ceil(currentWidth)/tv.getWidth()+scalingFactor)*tv.getTextSize())<tv.getHeight())
{
tv.setTextSize(TypedValue.COMPLEX_UNIT_SP, tv.getTextSize()+0.25f);
currentWidth=tv.getPaint().measureText(s);
//System.out.println(((int)Math.ceil(currentWidth)/tv.getWidth()+scalingFactor));
}
tv.setText(s);
}
感谢。
答案 3 :(得分:3)
我遇到了同样的问题并写了一个似乎对我有用的课程。基本上,我使用静态布局在单独的画布中绘制文本并重新测量,直到找到适合的字体大小。您可以在下面的主题中看到该课程。我希望它有所帮助。
答案 4 :(得分:3)
在我自己寻找解决方案的同时偶然发现了...我已经尝试了所有其他解决方案,我可以在堆栈溢出等上看到但是没有真正有效,所以我自己写了。
基本上,通过将文本视图包装在自定义线性布局中,我已经能够通过确保以固定宽度进行测量来成功地正确测量文本。
<!-- TextView wrapped in the custom LinearLayout that expects one child TextView -->
<!-- This view should specify the size you would want the text view to be displayed at -->
<com.custom.ResizeView
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_margin="10dp"
android:layout_weight="1"
android:orientation="horizontal" >
<TextView
android:id="@+id/CustomTextView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
</com.custom.ResizeView>
然后是线性布局代码
public class ResizeView extends LinearLayout {
public ResizeView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ResizeView(Context context) {
super(context);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
// oldWidth used as a fixed width when measuring the size of the text
// view at different font sizes
final int oldWidth = getMeasuredWidth() - getPaddingBottom() - getPaddingTop();
final int oldHeight = getMeasuredHeight() - getPaddingLeft() - getPaddingRight();
// Assume we only have one child and it is the text view to scale
TextView textView = (TextView) getChildAt(0);
// This is the maximum font size... we iterate down from this
// I've specified the sizes in pixels, but sp can be used, just modify
// the call to setTextSize
float size = getResources().getDimensionPixelSize(R.dimen.solutions_view_max_font_size);
for (int textViewHeight = Integer.MAX_VALUE; textViewHeight > oldHeight; size -= 0.1f) {
textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
// measure the text views size using a fixed width and an
// unspecified height - the unspecified height means measure
// returns the textviews ideal height
textView.measure(MeasureSpec.makeMeasureSpec(oldWidth, MeasureSpec.EXACTLY), MeasureSpec.UNSPECIFIED);
textViewHeight = textView.getMeasuredHeight();
}
}
}
希望这有助于某人。
答案 5 :(得分:3)
我已经玩了很长时间了,试图在各种各样的7英寸平板电脑(点燃火,Nexus7,以及在中国使用低分辨率屏幕的一些便宜的平板电脑)和设备上使我的字体大小正确。 / p>
最终对我有用的方法如下。 “32”是一个任意因素,基本上在7“平板电脑水平线上提供了大约70个以上的字符,这是我正在寻找的字体大小。相应调整。
textView.setTextSize(getFontSize(activity));
public static int getFontSize (Activity activity) {
DisplayMetrics dMetrics = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(dMetrics);
// lets try to get them back a font size realtive to the pixel width of the screen
final float WIDE = activity.getResources().getDisplayMetrics().widthPixels;
int valueWide = (int)(WIDE / 32.0f / (dMetrics.scaledDensity));
return valueWide;
}
答案 6 :(得分:2)
尝试在进行文本测量之前尝试将setHoriztonallyScrolling()设置为true,以便textView不会尝试在多行上布局文本
答案 7 :(得分:2)
一种方法是为每种通用屏幕尺寸指定不同的sp尺寸。例如,为小屏幕提供8sp,为普通屏幕提供12sp,为大屏幕提供16sp,为xlarge提供20sp。然后让你的布局引用@dimen text_size或其他什么,你可以放心,因为密度是通过sp单位处理的。有关此方法的详细信息,请参阅以下链接。
http://www.developer.android.com/guide/topics/resources/more-resources.html#Dimension
但是,我必须注意,支持更多语言意味着在测试阶段会有更多的工作,特别是如果你有兴趣将文字放在一行上,因为有些语言有更长的单词。在这种情况下,例如,在values-de-large文件夹中创建一个dimens.xml文件,并手动调整该值。希望这会有所帮助。答案 8 :(得分:1)
这是我根据其他一些反馈创建的解决方案。此解决方案允许您以XML格式设置文本的大小,该大小将是最大大小,并且它将自行调整以适合视图高度。 Size Adjusting TextView
private float findNewTextSize(int width, int height, CharSequence text) {
TextPaint textPaint = new TextPaint(getPaint());
float targetTextSize = textPaint.getTextSize();
int textHeight = getTextHeight(text, textPaint, width, targetTextSize);
while(textHeight > height && targetTextSize > mMinTextSize) {
targetTextSize = Math.max(targetTextSize - 1, mMinTextSize);
textHeight = getTextHeight(text, textPaint, width, targetTextSize);
}
return targetTextSize;
}
private int getTextHeight(CharSequence source, TextPaint paint, int width, float textSize) {
paint.setTextSize(textSize);
StaticLayout layout = new StaticLayout(source, paint, width, Alignment.ALIGN_NORMAL, mSpacingMult, mSpacingAdd, true);
return layout.getHeight();
}
答案 9 :(得分:0)
如果您的唯一要求是让文本自动拆分并在下一行中继续,并且高度并不重要,那么只需这样就可以了。
<TextView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:maxEms="integer"
android:width="integer"/>
根据你的maxEms值,这将使你的TextView垂直包裹它的内容。
答案 10 :(得分:0)
检查我的解决方案是否可以帮助您:
答案 11 :(得分:0)
我发现这对我来说效果很好。见:https://play.google.com/store/apps/details?id=au.id.rupert.chauffeurs_name_board&hl=en
http://www.rupert.id.au/chauffeurs_name_board/verson2.php
的源代码http://catchthecows.com/?p=72和https://github.com/catchthecows/BigTextButton
答案 12 :(得分:0)
这是基于mattmook的回答。它在某些设备上运行良好,但并非总体而言。我将调整大小移动到测量步骤,使最大字体大小成为自定义属性,考虑边距,并扩展FrameLayout而不是LineairLayout。
public class ResizeView extends FrameLayout {
protected float max_font_size;
public ResizeView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.ResizeView,
0, 0);
max_font_size = a.getDimension(R.styleable.ResizeView_maxFontSize, 30.0f);
}
public ResizeView(Context context) {
super(context);
}
@Override
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
// Use the parent's code for the first measure
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// Assume we only have one child and it is the text view to scale
final TextView textView = (TextView) getChildAt(0);
// Check if the default measure resulted in a fitting textView
LayoutParams childLayout = (LayoutParams) textView.getLayoutParams();
final int textHeightAvailable = getMeasuredHeight() - getPaddingTop() - getPaddingBottom() - childLayout.topMargin - childLayout.bottomMargin;
int textViewHeight = textView.getMeasuredHeight();
if (textViewHeight < textHeightAvailable) {
return;
}
final int textWidthSpec = MeasureSpec.makeMeasureSpec(
MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight() - childLayout.leftMargin - childLayout.rightMargin,
MeasureSpec.EXACTLY);
final int textHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
for (float size = max_font_size; size >= 1.05f; size-=0.1f) {
textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
textView.measure(textWidthSpec, textHeightSpec);
textViewHeight = textView.getMeasuredHeight();
if (textViewHeight <= textHeightAvailable) {
break;
}
}
}
}
这是在attrs.xml:
<declare-styleable name="ResizeView">
<attr name="maxFontSize" format="reference|dimension"/>
</declare-styleable>
最后像这样使用:
<PACKAGE_NAME.ui.ResizeView xmlns:custom="http://schemas.android.com/apk/res/PACKAGE_NAME"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="start|center_vertical"
android:padding="5dp"
custom:maxFontSize="@dimen/normal_text">
<TextView android:id="@+id/tabTitle2"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</PACKAGE_NAME.ui.ResizeView>
答案 13 :(得分:0)
试试这个......
tv.setText("Give a very large text anc check , this xample is very usefull");
countLine=tv.getLineHeight();
System.out.println("LineCount " + countLine);
if (countLine>=40){
tv.setTextSize(15);
}