如何缩放/调整文本大小以适合 TextView?

发布于 2024-08-28 05:41:39 字数 1932 浏览 10 评论 0原文

我正在尝试创建一种方法来调整 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() 值较小...即使文本大小远大于 TextView 的宽度或高度。

I'm trying to create a method for resizing multi-line text in a TextView such that it fits within the bounds (both the X and Y dimensions) of the TextView.

At present, I have something, but all it does is resize the text such that just the first letter/character of the text fills the dimensions of the TextView (i.e. only the first letter is viewable, and it's huge). I need it to fit all the lines of the text within the bounds of the TextView.

Here is what I have so far:

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);
      }
    }
}

I think the problem is in the use of tv.getPaint().getTextBounds(...). It always returns small numbers for the text bounds... small relative to the tv.getWidth() and tv.getHeight() values... even if the text size is far larger than the width or height of the TextView.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(14

猛虎独行 2024-09-04 05:41:39

MavenCentral 的 AutofitTextView 库可以很好地处理这个问题。源托管在 Github(1k+ 星)上 https://github.com/grantland/android-autofittextview< /a>

将以下内容添加到您的 app/build.gradle

repositories {
    mavenCentral()
}

dependencies {
    implementation 'me.grantland:autofittextview:0.2.+'
}

在代码中启用任何扩展 TextView 的视图:

AutofitHelper.create(textView);

在 XML 中启用任何扩展 TextView 的视图:

<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"
    />

The AutofitTextView library from MavenCentral handles this nicely. The source hosted on Github(1k+ stars) at https://github.com/grantland/android-autofittextview

Add the following to your app/build.gradle

repositories {
    mavenCentral()
}

dependencies {
    implementation 'me.grantland:autofittextview:0.2.+'
}

Enable any View extending TextView in code:

AutofitHelper.create(textView);

Enable any View extending TextView in XML:

<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>

Use the built in Widget in code or XML:

<me.grantland.widget.AutofitTextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:singleLine="true"
    />
╭ゆ眷念 2024-09-04 05:41:39

自 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"
/>

New since 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"
/>
护你周全 2024-09-04 05:41:39

很长一段时间了,试图在各种 7 英寸平板电脑(kindle fire、Nexus7 以及中国一些低分辨率屏幕的廉价平板电脑)和设备上获得正确的字体大小。

我已经使用这个方法 对我来说,“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;
}

I have played with this for quite some time, trying to get my font sizes correct on a wide variety of 7" tablets (kindle fire, Nexus7, and some inexpensive ones in China with low-res screens) and devices.

The approach that finally worked for me is as follows. The "32" is an arbitrary factor that basically gives about 70+ characters across a 7" tablet horizontal line, which is a font size I was looking for. Adjust accordingly.

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;
}
月下客 2024-09-04 05:41:39

我能够使用以下代码回答我自己的问题(见下文),但我的解决方案非常特定于应用程序。例如,这可能只看起来不错和/或适用于大小约为的 TextView。 1/2 屏幕(还有 40 像素的上边距和 20 像素的侧边距...没有下边距)。

不过,使用这种方法,您可以创建自己的类似实现。静态方法基本上只是查看字符数并确定应用于 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);
    }

谢谢。

I was able to answer my own question using the following code (see below), but my solution was very specific to the application. For instance, this will probably only look good and/or work for a TextView sized to approx. 1/2 the screen (with also a 40px top margin and 20px side margins... no bottom margin).

The using this approach though, you can create your own similar implementation. The static method basically just looks at the number of characters and determines a scaling factor to apply to the TextView's text size, and then incrementally increases the text size until the overall height (an estimated height -- using the width of the text, the text height, and the width of the TextView) is just below that of the TextView. The parameters necessary to determine the scaling factor (i.e. the if/else if statements) were set by guess-and-check. You'll likely have to play around with the numbers to make it work for your particular application.

This isn't the most elegant solution, though it was easy to code and it works for me. Does anyone have a better approach?

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);
    }

Thanks.

抽个烟儿 2024-09-04 05:41:39

我遇到了同样的问题,并编写了一个似乎对我有用的课程。基本上,我使用静态布局在单独的画布中绘制文本并重新测量,直到找到适合的字体大小。您可以在下面的主题中查看发布的课程。我希望它有帮助。

自动缩放 TextView 文本以适应边界

I had the same problem and wrote a class that seems to work for me. Basically, I used a static layout to draw the text in a separate canvas and remeasure until I find a font size that fits. You can see the class posted in the topic below. I hope it helps.

Auto Scale TextView Text to Fit within Bounds

始终不够爱げ你 2024-09-04 05:41:39

在自己寻找解决方案时偶然发现了这一点...我尝试了在堆栈溢出等方面可以看到的所有其他解决方案,但没有一个真正起作用,所以我编写了自己的解决方案。

基本上,通过将文本视图包装在自定义线性布局中,我已经能够通过确保使用固定宽度来测量文本来成功地正确测量文本。

<!-- 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();
        }
    }
}

希望这对某人有帮助。

Stumbled upon this whilst looking for a solution myself... I'd tried all the other solutions out there that I could see on stack overflow etc but none really worked so I wrote my own.

Basically by wrapping the text view in a custom linear layout I've been able to successfully measure the text properly by ensuring it is measured with a fixed width.

<!-- 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>

Then the linear layout code

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();
        }
    }
}

Hope this helps someone.

┼── 2024-09-04 05:41:39

也许尝试将 setHoriztonallyScrolling() 设置为 true在进行文本测量之前,以便 textView 不会尝试将文本布局在多行上

maybe try setting setHoriztonallyScrolling() to true before taking text measurements so that the textView doesn't try to layout your text on multiple lines

生活了然无味 2024-09-04 05:41:39

一种方法是为每个通用屏幕尺寸指定不同的 sp 尺寸。例如,为小屏幕提供 8sp,为普通屏幕提供 12sp,为大屏幕提供 16sp,为超大屏幕提供 20sp。然后,只需让您的布局引用 @dimen text_size 或其他内容,您就可以放心,因为密度是通过 sp 单元处理的。有关此方法的更多信息,请参阅以下链接。

http://www.developer.android.com/guide /topics/resources/more-resources.html#Dimension

但是,我必须注意,支持更多语言意味着测试阶段需要更多工作,特别是如果您有兴趣将文本保留在一行上,就像某些语言一样有更长的单词。在这种情况下,例如,在values-de-large 文件夹中创建一个dimens.xml 文件,然后手动调整该值。希望这有帮助。

One way would be to specify different sp dimensions for each of the generalized screen sizes. For instance, provide 8sp for small screens, 12sp for normal screens, 16 sp for large and 20 sp for xlarge. Then just have your layouts refer to @dimen text_size or whatever and you can rest assured, as density is taken care of via the sp unit. See the following link for more info on this approach.

http://www.developer.android.com/guide/topics/resources/more-resources.html#Dimension

I must note, however, that supporting more languages means more work during the testing phase, especially if you're interested in keeping text on one line, as some languages have much longer words. In that case, make a dimens.xml file in the values-de-large folder, for example, and tweak the value manually. Hope this helps.

扛起拖把扫天下 2024-09-04 05:41:39

这是我根据其他一些反馈创建的解决方案。该解决方案允许您在 XML 中设置文本的大小,这将是最大大小,并且它将自行调整以适应视图高度。
调整 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();
    }

Here is a solution that I created based on some other feedback. This solution allows you to set the size of the text in XML which will be the max size and it will adjust itself to fit the view height.
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();
    }
皇甫轩 2024-09-04 05:41:39

如果您唯一的要求是让文本自动分割并在下一行继续,并且高度并不重要,那么就这样吧。

<TextView
    android:layout_height="wrap_content"
    android:layout_width="wrap_content"
    android:maxEms="integer"
    android:width="integer"/>

这将使您的 TextView 根据您的 maxEms 值垂直换行到其内容。

If your only requirement is to have the text automatically split and continue in the next line and the height is not important then just have it like this.

<TextView
    android:layout_height="wrap_content"
    android:layout_width="wrap_content"
    android:maxEms="integer"
    android:width="integer"/>

This will have your TextView wrap to it's content vertically depending on your maxEms value.

一向肩并 2024-09-04 05:41:39

检查我的解决方案是否可以帮助您:

自动缩放 TextView 文本到适合范围内

Check if my solution helps you:

Auto Scale TextView Text to Fit within Bounds

披肩女神 2024-09-04 05:41:39

这是基于 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>

This is based on mattmook's answer. It worked well on some devices, but not on all. I moved the resizing to the measuring step, made the maximum font size a custom attribute, took margins into account, and extended FrameLayout instead of 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;
            }
        }
    }
}

And this in attrs.xml:

<declare-styleable name="ResizeView">
    <attr name="maxFontSize" format="reference|dimension"/>
</declare-styleable>

And finally used like this:

<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>
_蜘蛛 2024-09-04 05:41:39

试试这个...

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);
    }

Try this...

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);
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文