Как масштабировать / изменить размер текста, чтобы соответствовать TextView? - PullRequest
27 голосов
/ 08 апреля 2010

Я пытаюсь создать метод для изменения размера многострочного текста в TextView таким образом, чтобы он вписывался в границы (оба размера - X и Y) TextView.

В настоящее время у меня есть кое-что, но все, что он делает, это изменяет размер текста так, что только первая буква / символ текста заполняет размеры 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 ]

20 голосов
/ 14 июля 2015

Библиотека AutofitTextView из MavenCentral прекрасно справляется с этой задачей. Источник размещен на 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);

Включить любое представление, расширяющее TextView в 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>

Используйте встроенный виджет в коде или XML:

<me.grantland.widget.AutofitTextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:singleLine="true"
    />
5 голосов
/ 14 июня 2017

Новое с 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 голосов
/ 14 апреля 2012

Наткнулся на это, пока сам искал решение ... Я перепробовал все другие решения, которые я мог видеть при переполнении стека и т. Д., Но ни одно из них не сработало, поэтому я написал свое.

По сути, оборачивая текстовое представление в пользовательский линейный макет, я смог успешно измерить текст должным образом, обеспечив его измерение с фиксированной шириной.

<!-- 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 голосов
/ 09 января 2013

Я играл с этим в течение достаточно долгого времени, пытаясь получить правильный размер шрифта для широкого спектра 7-дюймовых планшетов (kindle fire, Nexus7, и некоторых недорогих в Китае с экранами низкого разрешения) и устройств.

Подход, который наконец-то сработал для меня, заключается в следующем. «32» - это произвольный фактор, который в основном дает около 70+ символов на 7-дюймовой горизонтальной линии планшета, что соответствует размеру шрифта, который я искал. Отрегулируйте соответствующим образом.

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;
}
3 голосов
/ 04 апреля 2011

У меня была та же проблема, и я написал класс, который, кажется, работает для меня.По сути, я использовал статический макет, чтобы нарисовать текст на отдельном холсте и повторять его, пока не найду подходящий размер шрифта.Вы можете увидеть класс, размещенный в теме ниже.Надеюсь, это поможет.

Автоматическое масштабирование текста TextView, чтобы поместиться в пределах границ

3 голосов
/ 08 сентября 2010

Я смог ответить на свой вопрос, используя следующий код (см. Ниже), но мое решение было очень специфичным для приложения.Например, это, вероятно, будет хорошо выглядеть и / или работать только для 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);
    }

Спасибо.

2 голосов
/ 06 сентября 2012

Один из способов - указать разные размеры sp для каждого из обобщенных размеров экрана.Например, предоставьте 8sp для маленьких экранов, 12sp для обычных экранов, 16 sp для больших и 20 sp для больших.Тогда просто сделайте так, чтобы ваши макеты ссылались на @dimen text_size или что-то еще, и вы можете быть уверены, что плотность решается через модуль sp.См. Следующую ссылку для получения дополнительной информации об этом подходе.

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

Однако я должен отметить, что поддержка большего количества языков означает больше работы на этапе тестирования, особенно если вы заинтересованыдержать текст в одну строку, так как в некоторых языках слова намного длиннее.В этом случае, например, создайте файл dimensions.xml в папке values-de-large и настройте значение вручную.Надеюсь, это поможет.

2 голосов
/ 08 апреля 2010

, возможно, попробуйте установить setHoriztonallyScrolling () в значение true, прежде чем проводить измерения текста, чтобы textView не пытался расположить ваш текст на нескольких строках

1 голос
/ 21 января 2014

Вот решение, которое я создал на основе других отзывов. Это решение позволяет вам установить размер текста в 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();
    }
0 голосов
/ 21 июня 2016

Попробуйте это ...

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