텍스트 뷰에 맞게 텍스트 글꼴 크기를 조정하는 방법


178

안드로이드에서 텍스트 뷰의 텍스트 크기를 차지하는 공간에 맞게 텍스트 크기를 조정하는 방법이 있습니까?

예를 들어 a를 사용하고 각 행에 s를 TableLayout추가하고 TextView있습니다. TextViews가 텍스트를 줄 바꿈하고 싶지 않기 때문에 내용의 글꼴 크기가 줄어 듭니다.

어떤 아이디어?

나는 시도 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/... 참고 : 나는 루프를 구현하지 않았다. 추신 : 고마워 던니
vsm

답변:


129

아래 솔루션은 여기에 모든 제안을 통합합니다. Dunni가 처음 게시 한 것으로 시작합니다. gjpc와 같은 이진 검색을 사용하지만 좀 더 읽기 쉽습니다. 그것은 또한 그렘의 버그 수정과 내 버그 수정을 포함합니다.

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
모든 의견을 보내 주셔서 감사합니다. 솔루션은 너비 만 고려 한다는 것을 알았 습니다. 내 문제는 좋아하는 높이가 높이를 초과한다는 것입니다.
AlikElzin-kilaka

3
아, 그것은 부분적으로 런타임에 작동하는 것 같습니다. 장치 및 에뮬레이터에서는 텍스트가 반쯤 잘립니다. Eclipse 레이아웃 편집기에서는 잘 보입니다. 어떤 아이디어?
AlikElzin-kilaka

1
이 게시물은 트릭을 수행하는 것처럼 보입니다 (hi / lo에 사용자 정의 xml 속성 추가) : stackoverflow.com/a/8090772/156611
Ed Sinek

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

2
훌륭한 일! 버튼으로도 작동했습니다. float hi = this.getHeight() - this.getPaddingBottom() - this.getPaddingTop();
텍스트 뷰

28

TextView를 확장하고 이것을 수행하는 클래스를 작성했습니다. 제안한대로 measureText를 사용합니다. 기본적으로 최대 텍스트 크기와 최소 텍스트 크기 (변경 가능)가 있으며 가장 큰 텍스트를 찾을 때까지 그 사이의 크기를 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
이진 검색은 일반적으로 선형 검색보다 빠릅니다.
Ted Hopp

"onSizeChanged"보다는 "onLayout"에서 refitText를하는 것이 더 합리적이라고 생각합니다
Jacky

내 문제를 좀 주시겠습니까? stackoverflow.com/questions/36265448/…
Muhammad

17

이것은 speedplane 'sFontFitTextView 이지만 텍스트를 맞추기 위해 필요한 경우 에만 글꼴 크기를 줄이고 그렇지 않으면 글꼴 크기를 유지합니다. 높이에 맞게 글꼴 크기를 늘리지 않습니다.

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

텍스트가 너비에 맞는 한 글꼴 크기를 60sp로 유지합니다. 텍스트가 더 길면 글꼴 크기가 줄어 듭니다. 이 경우 TextViews 높이도로 인해 변경됩니다 height=wrap_content.

버그를 발견하면 자유롭게 편집하십시오.


어떻게 사용 했습니까? 나는 그것을 어떻게 대답하는지에 대한 예를 추가했다. 다양한 안드로이드 버전, 에뮬레이터 및 ADT 그래픽 레이아웃 편집기로 테스트했습니다.
sulai

본질적으로 특정 높이를 지정하지 않았습니다. onMeasure는 원하는 경우 뷰를 인계 할 수 있습니다. 루틴의 마지막 줄을 다음과 같이 변경하여 해결책을 this.setMeasuredDimension(parentWidth, height);
찾았

1
위대한 입력, 나는 코드 : 지금은 작동 수정 match_parentwrap_content.
sulai

왜 단순히 s.isEmpty () 대신 빈 문자열 쓰기 "".equals (s)를 테스트합니까? 또는 s.length () == 0? 내가 왜 이러한 평등 테스트를 볼 수 있는지 이해하지 못합니다.
Zordid

1
@PratikButani이 점을 지적 해 주셔서 감사합니다. 에 해당 text.isEmpty()합니다 "".equals(text).
sulai

14

다음은 에뮬레이터와 전화에서 작동하지만 Eclipse 레이아웃 편집기에서는 잘 작동하지 않는 솔루션입니다. kilaka의 코드에서 영감을 얻었지만 텍스트의 크기는 Paint에서 얻지 않고 TextView 자체를 호출하여 측정하여 얻습니다 measure(0, 0).

자바 클래스 :

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에서는 기본적으로 작동하지만 작은 화면의 기기에서는 문제가 있습니다.
Tony Ceralva

화면이 작은 기기의 문제는 무엇입니까?
yDelouis

죄송합니다. 작은 화면에서는 문제가 발생하지 않으며 Android 4.0 (에뮬레이터 포함)이 설치된 모든 기기에서보기가 작동하지 않습니다. 나는 당신의 GitHub의 새로운 문제를 열었다
토니 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"예제에 표시된대로 추가해야합니다 .


죄송하지만 모든 경우에 작동하지는 않습니다. 또한 여러 줄을 올바르게 처리하지 못한다고 생각합니다.
안드로이드 개발자

오 .. 작동하지 않는 사례를 알려주시겠습니까? (여러 줄은 지원되지 않습니다, 당신이 맞습니다)
Pascal

많은 경우. 나는 그것이 정확한지 증명하기 위해 무작위 테스터를 만들었습니다. 다음은 하나의 샘플입니다 :보기의 너비 : 317px, 높이 : 137px, 텍스트 : "q7Lr". 내가 보는 것은 : tinypic.com/view.php?pic=2dv5yf9&s=6 입니다. : 여기 내가 만들어 놓은 샘플 프로젝트의 mega.co.nz/... . 그런보기는 여러 줄을 처리하고 글꼴의 모든 크기를 지원하며 너비뿐만 아니라 높이를 처리해야한다고 생각합니다 ... 슬프게도, 내가 찾은 샘플 중 아무것도 제대로 작동하지 않았습니다.
안드로이드 개발자

물론 완벽한 솔루션은 아닙니다. 죄송합니다. 당분간 개선 할 시간이 없습니다. 개선 할 수 있다면 주저하지 말고 솔루션을 게시하십시오.
Pascal

1
나는 새 스레드를 만든 그 사람이 좋은 솔루션을 만들 수있을 것이라고 기대하고 쇼 내 테스트 : stackoverflow.com/questions/16017165/...
안드로이드 개발자

7

나는 같은 문제가 있었고 나를 위해 일하는 것처럼 보이는 수업을 썼습니다. 기본적으로 정적 레이아웃을 사용하여 별도의 캔버스에 텍스트를 그리고 맞는 글꼴 크기를 찾을 때까지 다시 측정했습니다. 아래 주제에 게시 된 수업을 볼 수 있습니다. 도움이 되길 바랍니다.

경계에 맞게 텍스트 크기 조정 텍스트보기 텍스트


6

이제 타사 라이브러리 나 위젯없이이 작업을 수행 할 수 있습니다. API 레벨 26의 TextView에 내장되어 있습니다.에 추가 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에 고정).
Ted Hopp

5

나는 다음이 나를 위해 잘 작동한다는 것을 알았다. 루프하지 않고 높이와 너비를 모두 설명합니다. 뷰에서 setTextSize를 호출 할 때 PX 단위를 지정해야합니다. 이전 게시물의 팁 덕분에!

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에 어떻게 통합합니까?
AlikElzin-kilaka

이 코드는 기존 뷰 내에 텍스트를 제한된 크기 영역에 배치하거나 TextView에서 고유 한 파생 클래스를 만들고 다른 게시물에 표시된대로 onMeasure를 재정의 할 수 있습니다. 그 자체로는 레이아웃에서 사용할 수 없습니다.
Glenn

뷰를 그린 뷰의 치수를 확인하십시오 . onCreate () 중에이 작업을 수행하는 것이 너무 빠릅니다. ViewTreeObserver를 사용하여 적시에 측정을 수행했는지 확인했습니다.
Scott Biggs

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

API 레벨 26 이상에서만 작동 app:autoSizeTextType="uniform"하므로 이전 버전과의 호환성을 위해 사용하십시오 android:autoSizeTextType="uniform".


2

위의 Dunni 솔루션 변형을 사용했지만 특정 코드가 작동하지 않았습니다. 특히, Paint 객체를 사용하여 뷰의 Paint 객체의 특성을 갖도록 시도한 다음 measureText ()를 호출하려고하면 뷰의 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

이 라이브러리를 찾을 때까지 soooo 프로젝트 에서이 고통을 겪었습니다.

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
단순히 코드를 읽음으로써 이것이 효과가 있다고 생각하지 않습니다. 질문은 텍스트보기의 글꼴 크기를보기에 맞게 자동으로 조정하는 것에 관한 것입니다. 고정 글꼴 크기를 설정합니다.
sulai

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

최소 텍스트 크기를 달성 할 수없는 경우 텍스트 뷰를 특정 너비 내에 맞추고 끝에 타원 "..."을 추가하는 짧은 도우미 클래스를 작성했습니다.

텍스트가 적합하거나 최소 텍스트 크기에 도달 할 때까지만 텍스트를 작게 만듭니다. 큰 크기로 테스트하려면 help 메소드를 호출하기 전에 textsize를 큰 숫자로 설정하십시오.

픽셀이 필요하므로 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);
        }
    }
}

여러 줄의 텍스트를 지원합니까?
Roymunson

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 ()를 확인하고 textview linecount를 얻은 다음 textSize를 설정하십시오.

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

아주 간단한 몇 줄 코드 ..

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.