首页 文章

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

提问于
浏览
27

我正在尝试创建一个方法来调整 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 的宽度或高度 .

14 回答

  • 3

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

    我能够使用以下代码回答我自己的问题(见下文),但我的解决方案非常特定于应用程序 . 例如,这可能只是看起来很好和/或适用于大小约为的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);
        }
    

    谢谢 .

  • 20

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

    Auto Scale TextView Text to Fit within Bounds

  • 2

    在我自己寻找解决方案的同时偶然发现了...我已经尝试了所有其他解决方案,我可以在堆栈溢出等上看到但是没有真正有效,所以我自己写了 .

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

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

    希望这有助于某人 .

  • 3

    我已经玩了很长一段时间,试图在各种7英寸平板电脑(点燃火,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;
    }
    
  • 0

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

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

  • 0

    一种方法是为每个通用屏幕尺寸指定不同的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文件,并手动调整该值 . 希望这可以帮助 .

  • 3

    这是我根据其他一些反馈创建的解决方案 . 此解决方案允许您以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();
        }
    
  • 2

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

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

    这将根据您的maxEms值将TextView垂直包装到其内容中 .

  • 1

    检查我的解决方案是否有助于您

    Auto Scale TextView Text to Fit within Bounds

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

    试试这个...

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

相关问题