Як налаштувати розмір шрифту тексту, щоб він відповідав перегляду тексту


178

Чи є в android спосіб налаштувати розмір тексту в текстовому перегляді відповідно до місця, яке він займає?

Наприклад, я використовую a TableLayoutі додаю кілька TextViews у кожен рядок. Оскільки я не хочу 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());  

Перевірте моє рішення , засноване на коді dunni тут stackoverflow.com/questions/5033012 / ... Примітка: я не виконав його з петлею. PS: спасибі dunni
VSM

Відповіді:


129

Нижче наведене рішення містить усі пропозиції. Це починається з того, що спочатку було розміщено Данні. Він використовує двійковий пошук, як 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;
}

9
Дякуємо за поєднання всіх відгуків. Я бачу, що рішення враховує лише ширину . Моя проблема полягає в тому, що кохання перевищує висоту.
АлікЕльзін-кілака

3
О, це, здається, частково працює під час виконання. На пристрої від емулятора текст вирізається наполовину. У редакторі макетів Eclipse це виглядає чудово. Будь-які ідеї?
AlikElzin-kilaka

1
Цей пост виглядає , як він буде робити трюк (додавання призначених для користувача XML - атрибутів Привіт / Lo): stackoverflow.com/a/8090772/156611
Ed Сінеко

7
Це було чудове рішення! У випадку, якщо хтось інший є новим у розробці андроїд і не зовсім знає, як реалізувати розширений вигляд у XML, це виглядає приблизно так: <com.example.zengame1.FontFitTextView android:paddingTop="5dip" android:id="@+id/childs_name" android:layout_width="fill_parent" android:layout_height="0dip" android:layout_weight="1" android:layout_gravity="center" android:textSize="@dimen/text_size"/>
Кейсі Мюррей

2
Чудова робота! Зробив це також і за допомогою кнопок Щоб подбати про встановлену висоту перегляду текстуfloat hi = this.getHeight() - this.getPaddingBottom() - this.getPaddingTop();
AlexGuti

28

Я написав клас, який розширює TextView і робить це. Він просто використовує mereText, як ви пропонуєте. В основному він має максимальний розмір тексту і мінімальний розмір тексту (який можна змінити), і він просто проходить через розміри між ними у зменшенні 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;

}

11
Бінарний пошук, як правило, буде швидшим, ніж лінійний пошук.
Тед Хопп

я думаю, було б розумніше робити refitText на "onLayout", а не "onSizeChanged"
Джекі

Чи можете ви подивіться на мою проблему stackoverflow.com/questions/36265448/…
Мухаммед

17

Це швидкість літака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" />

Це дозволило б зберегти розмір шрифту до 60 шпіль, поки текст підходить по ширині. Якщо текст довший, він зменшить розмір шрифту. У цьому випадку TextViewвисота s також зміниться через height=wrap_content.

Якщо ви знайшли помилки, сміливо редагуйте.


Як ти ним користувався? Я додав приклад того, як я це використовую у своїй відповіді. Я перевірив це з різними версіями для Android, емуляторами та редактором графічного макета ADT.
sulai

По суті, я не вказував конкретної висоти. Ваша програма onMeasure дозволяє переглядати зображення, якщо воно хоче. Мені вдалося придумати рішення, змінивши останній рядок у рутині наthis.setMeasuredDimension(parentWidth, height);
PearsonArtPhoto

1
Чудовий вклад, я виправив код :) Зараз він працює з match_parentі wrap_content.
сулай

Чому ви перевіряєте "" .equals (s) порожнього рядка замість просто s.isEmpty () ?? Або s.length () == 0? Не розумію, чому я іноді бачу ці тести на рівність.
Zordid

1
@PratikButani дякую, що вказали на це. еквівалент був text.isEmpty()би "".equals(text).
сулай

14

Ось моє рішення, яке працює на емуляторі та телефонах, але не дуже добре в редакторі макетів 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, але у мене проблеми на пристроях з маленькими екранами.
Тоні Серальва

Яка ваша проблема на пристроях з маленькими екранами?
yDelouis

На жаль, проблема не у маленьких екранах, ваш погляд працює не на всіх пристроях з Android 4.0, включений емулятор. Я відкрив новий випуск у вашому GitHub
Tony Ceralva

12

Дуже дякую 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"як показ у прикладі.


Вибачте, але це все ще не працює у всіх випадках. Я також думаю, що він не обробляє кілька рядків правильно.
андроїд розробник

Ой, не могли б ви подати мені справу, для якої вона не працює? (Багаторядковий не підтримується, ви праві)
Паскаль

Багато випадків. Я створив випадковий тестер, щоб довести, що це правильно. Ось один зразок: ширина перегляду: 317px, висота: 137px, текст: "q7Lr". Я бачу таке: tinypic.com/view.php?pic=2dv5yf9&s=6 . ось приклад проекту я зробив: mega.co.nz / ... . Я думаю, що такий погляд повинен працювати з багаторядковими, підтримувати будь-який розмір шрифтів, обробляти висоту, а не лише ширину, ... На жаль, жоден із знайдених нами зразків не працював добре.
андроїд розробник

ок, точно не повне рішення. вибачте. Наразі вдосконалювати його немає часу. Якщо ви можете вдосконалити його, не соромтеся розмістити своє рішення.
Паскаль

1
Я створив нову тему, яка показує мої тести, сподіваючись, що хтось зможе зробити хороше рішення: stackoverflow.com/questions/16017165/…
андроїд розробник

7

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

Текст автоматичного масштабування TextView підходить під межі


6

Тепер це можна зробити без сторонньої бібліотеки чи віджета. Він вбудований у TextView на рівні API 26. Додайте android:autoSizeTextType="uniform"до нього TextViewта встановіть висоту. Це все.

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для сумісності.


5

Невелика модифікація на 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);
    }
}

3
Замість двійкового пошуку просте масштабування працює досить добре: trySize *= availableWidth / measured_width(потім затискається на minTextSize).
Тед Хопп

5

Я знайшов наступне, щоб добре працювати на мене. Він не циклічно і враховує як висоту, так і ширину. Зауважте, що важливо вказати блок PX під час виклику setTextSize у поданні. Дякуємо за підказку в попередньому дописі для цього!

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?
АлікЕльзін-кілака

Цей код корисний для розміщення тексту в існуючому представленні в області обмеженого розміру або ви можете створити свій власний похідний клас з TextView і змінити onMeasure, як показано в інших публікаціях. Сам по собі його не можна використовувати в макеті.
Гленн

Переконайтеся, що ви перевірили розміри подання після його малювання. Робити це під час onCreate () занадто рано. Я використовував ViewTreeObserver, щоб переконатися, що вимірювання були зроблені в потрібний час.
Скотт Біггс

4

Працює з модифікацією

Потрібно встановити розмір перегляду тексту таким чином, оскільки в іншому випадку 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);
}

4

Використовуйте app:autoSizeTextType="uniform"для зворотної сумісності, оскільки android:autoSizeTextType="uniform"працює лише в API рівня 26 і вище.


2

Я використовував варіацію рішення Данні вище, але цей конкретний код не працював для мене. Зокрема, при спробі використання об'єкта Paint, встановленого для ознак об’єкта Paint подання, а потім виклику мераText (), він не повертає те саме значення, що безпосередньо викликає об’єкт Paint подання перегляду. Можливо, є певні відмінності в налаштуванні моїх поглядів, які роблять поведінку різною.

Моє рішення полягало в тому, щоб безпосередньо використовувати фарбу подання, хоча можливі певні зміни в розмірі шрифту для перегляду в кілька разів.


2

Я працював над вдосконаленням прекрасного рішення з швидкості літака, і придумав це. Він керує висотою, включаючи встановлення поля таким чином, що текст повинен бути правильно по центру вертикально.

Ця функція використовує ту саму функцію, щоб отримати ширину, оскільки вона, здається, працює найкраще, але для отримання висоти використовується інша функція, оскільки висота ніде не передбачена. Є деякі виправлення, які потрібно внести, але я придумав спосіб зробити це, при цьому приємно дивлячись на око.

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


1

Я боліла в своїх проектах протягом того часу, поки не знайшла цю бібліотеку:

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

0

Надихнувшись попередніми плакатами, я хотів поділитися своїм рішенням. Він працює з коефіцієнтом масштабу, який застосовується до попереднього розміру шрифту, щоб він міг відповідати наявному простору. Окрім запобігання несподіваній поведінці методу 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);
    }
}

0
/* 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;

1
Просто читаючи ваш код, я не думаю, що це спрацює. Пам'ятайте, питання полягало в автоматичному налаштуванні розміру шрифту тексту перегляду, щоб він міг підходити до подання. Ви встановлюєте фіксований розмір шрифту.
сулай

0

Це має бути просте рішення:

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

0

Розширіть 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);
  }

0

Я написав короткий допоміжний клас, який робить перегляд тексту підходящим до певної ширини і додає еліпсис "..." наприкінці, якщо мінімальний розмір тексту не може бути досягнутий.

Майте на увазі, що він лише зменшує текст до тих пір, поки він не підходить або до досягнення мінімального розміру тексту. Щоб перевірити великі розміри, перед тим, як викликати метод довідки, встановіть велику кількість тексту.

Він займає пікселі, тому якщо ви використовуєте значення з 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);
        }
    }
}

Чи підтримує це кілька рядків тексту?
Роймунсон

0

Якщо встановлена ​​така трансформація, як AllCaps, підхід у швидкості літака є помилковим. Я виправив це, у результаті чого з'явився такий код (вибачте, моя репутація не дозволяє мені додавати це як коментар до рішення 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;
}

0

Я не знаю, це правильний спосіб чи не працює його ... перегляньте та перевірте OnGlobalLayoutListener () і отримайте кількість перегляду рядків тексту, а потім встановіть textSize.

 yourView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            if (textView.getLineCount()>=3) {
                textView.setTextSize(20);
            }else{
                //add somthing
              }
        }
    });

Його дуже простий рядковий код ..

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.