web-dev-qa-db-de.com

So passen Sie die Schriftgröße des Texts an die Textansicht an

Gibt es in Android) eine Möglichkeit, die Textgröße in einer Textansicht an den Platz anzupassen, den sie einnimmt?

Z.B. Ich verwende ein TableLayout und füge jeder Zeile mehrere TextView hinzu. Da ich nicht möchte, dass das TextViews den Text umschließt, sehe ich eher, dass es die Schriftgröße des Inhalts verringert.

Irgendwelche Ideen?

Ich habe measureText ausprobiert, aber da ich die Größe der Spalte nicht kenne, scheint es schwierig zu sein, sie zu verwenden. Dies ist der Code, in dem ich die Schriftgröße auf etwas ändern möchte, das passt

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());  
177
rudas

In der folgenden Lösung sind alle hier aufgeführten Vorschläge enthalten. Es beginnt mit dem, was ursprünglich von Dunni gepostet wurde. Es verwendet eine binäre Suche wie gjpc, ist aber etwas lesbarer. Es enthält auch die Fehlerbehebungen von gregm und eine eigene Fehlerbehebung.

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

Ich habe eine Klasse geschrieben, die TextView erweitert und dies tut. Es wird nur measureText verwendet, wie Sie vorschlagen. Grundsätzlich hat es eine maximale Textgröße und eine minimale Textgröße (die geändert werden kann) und es läuft nur durch die Größen zwischen ihnen in Schritten von 1, bis es die größte findet, die passt. Nicht besonders elegant, aber ich kenne keinen anderen Weg.

Hier ist der Code:

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;

}
28
dunni

Dies ist speedplane's FontFitTextView , aber es wird nur die Schriftgröße verringert , wenn dies erforderlich ist, damit der Text passt, und behält sonst seine Schriftgröße. Die Schriftgröße wird nicht an die Höhe angepasst.

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

}

Hier ist ein Beispiel, wie es in XML verwendet werden könnte:

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

Dies würde die Schriftgröße auf 60sp halten, solange der Text in die Breite passt. Wenn der Text länger ist, verringert sich die Schriftgröße. In diesem Fall ändert sich die Höhe von TextViews auch aufgrund von height=wrap_content.

Wenn Sie Fehler finden, können Sie diese jederzeit bearbeiten.

17
sulai

Hier ist meine Lösung, die auf Emulatoren und Handys funktioniert, aber im Eclipse-Layout-Editor nicht sehr gut. Es ist inspiriert von Kilakas Code, aber die Größe des Texts wird nicht von Paint ermittelt, sondern von der Messung der Textansicht selbst, die measure(0, 0) aufruft.

Die Java Klasse:

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

Die XML-Attributdatei:

<resources>
    <declare-styleable name="FontFitTextView">
        <attr name="minTextSize" format="dimension" />
        <attr name="maxTextSize" format="dimension" />
    </declare-styleable>
</resources>

Check mein Github für die neueste Version dieser Klasse. Ich hoffe, es kann für jemanden nützlich sein. Wenn ein Fehler gefunden wird oder der Code erklärt werden muss, können Sie ein Problem in Github eröffnen.

14
yDelouis

Vielen Dank an https://stackoverflow.com/users/234270/speedplane . Gute Antwort!

Hier ist eine verbesserte Version seiner Antwort, die sich auch um die Höhe kümmert und mit einem maxFontSize-Attribut zur Begrenzung der Schriftgröße ausgestattet ist (war in meinem Fall nützlich, deshalb wollte ich es teilen):

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

Entsprechende Datei /res/values/attr.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <declare-styleable name="FontFitTextView">
        <attr name="maxFontSize" format="dimension" />
    </declare-styleable>

</resources>

Beispiel:

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

Um das neue maxFontSize-Attribut zu verwenden, vergessen Sie nicht, xmlns:res-auto="http://schemas.Android.com/apk/res-auto" wie im Beispiel gezeigt.

12
Pascal

Ich hatte das gleiche Problem und schrieb eine Klasse, die für mich zu funktionieren scheint. Grundsätzlich habe ich ein statisches Layout verwendet, um den Text in einer separaten Zeichenfläche zu zeichnen und erneut zu messen, bis ich eine passende Schriftgröße gefunden habe. Sie können die Klasse im folgenden Thema sehen. Ich hoffe, es hilft.

Automatische Skalierung von TextView-Text, um ihn an Grenzen anzupassen

7
Chase

Ich fand das Folgende gut für mich. Es gibt keine Schleife und berücksichtigt sowohl die Höhe als auch die Breite. Beachten Sie, dass es wichtig ist, die PX-Einheit anzugeben, wenn Sie setTextSize in der Ansicht aufrufen. Danke an den Tipp in einem vorherigen Beitrag dafür!

Paint paint = adjustTextSize(getPaint(), numChars, maxWidth, maxHeight);
setTextSize(TypedValue.COMPLEX_UNIT_PX,Paint.getTextSize());

Hier ist die Routine, die ich benutze, indem ich getPaint () aus der Ansicht übergebe. Eine 10-stellige Zeichenfolge mit einem 'breiten' Zeichen wird verwendet, um die Breite unabhängig von der tatsächlichen Zeichenfolge zu schätzen.

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

Leichte Änderung an 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);
}

Und binäre Suche auf 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);
    }
}
5
gjpc

Sie können dies jetzt ohne eine Drittanbieter-Bibliothek oder ein Widget tun. Es ist in TextView in API-Ebene 26 integriert. Fügen Sie Android:autoSizeTextType="uniform" Zu Ihrem TextView hinzu und legen Sie die Höhe fest. Das ist alles.

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

Sie können aus Kompatibilitätsgründen auch TextViewCompat verwenden.

5
arsent

Funktioniert mit Modifikation

Sie müssen die Größe der Textansicht wie folgt festlegen, da setTextSize andernfalls davon ausgeht, dass der Wert in SP units:

setTextSize(TypedValue.COMPLEX_UNIT_PX, trySize);

Und Sie mussten diesen Code explizit hinzufügen.

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

Verwenden app:autoSizeTextType="uniform" aus Gründen der Abwärtskompatibilität, weil Android:autoSizeTextType="uniform" funktionieren nur in API Level 26 und höher.

3
Suraj Vaishnav

Ich habe daran gearbeitet, die hervorragende Lösung von Speedplane zu verbessern, und bin auf diese Idee gekommen. Hiermit wird die Höhe verwaltet, einschließlich der Einstellung des Rands, sodass der Text vertikal korrekt zentriert werden soll.

Dies verwendet die gleiche Funktion, um die Breite zu ermitteln, da dies am besten zu funktionieren scheint, verwendet jedoch eine andere Funktion, um die Höhe zu ermitteln, da die Höhe nirgendwo angegeben wird. Es gibt einige Korrekturen, die vorgenommen werden müssen, aber ich habe einen Weg gefunden, dies zu tun, während ich für das Auge angenehm aussehe.

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

Google hat diese Funktion bereits erstellt.

<TextView
    Android:layout_width="match_parent"
    Android:layout_height="200dp"
    Android:autoSizeTextType="uniform" />

https://developer.Android.com/guide/topics/ui/look-and-feel/autosizing-textview.html

2
tse

Ich habe oben eine Variante der Dunni-Lösung verwendet, aber dieser bestimmte Code hat bei mir nicht funktioniert. Insbesondere wenn Sie versuchen, mit dem Paint-Objekt die Eigenschaften des Paint-Objekts der Ansicht festzulegen und dann measureText () aufrufen, wird nicht derselbe Wert zurückgegeben wie beim direkten Aufrufen des Paint-Objekts der Ansicht. Vielleicht gibt es einige Unterschiede in der Art und Weise, wie meine Ansichten eingerichtet sind, die das Verhalten anders machen.

Meine Lösung bestand darin, die Farbe der Ansicht direkt zu verwenden, obwohl es einige Leistungseinbußen geben kann, wenn die Schriftgröße für die Ansicht mehrmals geändert wird.

2
ThomasW

Ich hatte diesen Schmerz in meinen Projekten für soooo lange, bis ich diese Bibliothek fand:

compile 'me.grantland:autofittextview:0.2.+'

Sie müssen nur die XML-Datei nach Ihren Wünschen hinzufügen und fertig. Beispielsweise:

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

Ich habe nicht gewusst, dass dies richtig ist oder nicht funktioniert. Sehen Sie sich OnGlobalLayoutListener () an und holen Sie sich die Zeilenanzahl für die Textansicht. Setzen Sie dann textSize.

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

Es ist sehr einfach, einige Zeilen Code ..

0
Mathan Chinna

Inspiriert von den vorherigen Postern wollte ich meine Lösung teilen. Es funktioniert mit einem Skalierungsfaktor, der auf die vorherige Schriftgröße angewendet wird, um sie an den verfügbaren Platz anzupassen. Um ein unerwartetes Verhalten der TextViews onDraw-Methode zu verhindern, wird der Text einfach selbst gezeichnet.

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
unSinn

Erweitern Sie TextView und überschreiben Sie onDraw mit dem folgenden Code. Das Seitenverhältnis des Texts bleibt erhalten, die Größe wird jedoch so angepasst, dass der Raum gefüllt wird. Sie können den Code bei Bedarf problemlos ändern, um ihn zu dehnen.

  @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
Greg Bacchus

Ich habe eine kurze Hilfsklasse geschrieben, die eine Textansicht innerhalb einer bestimmten Breite anpasst und am Ende eine Ellipse "..." hinzufügt, wenn die minimale Textgröße nicht erreicht werden kann.

Denken Sie daran, dass der Text nur so lange verkleinert wird, bis er passt oder die minimale Textgröße erreicht ist. Wenn Sie mit großen Formaten testen möchten, setzen Sie die Textgröße auf eine große Zahl, bevor Sie die Hilfemethode aufrufen.

Es benötigt Pixel. Wenn Sie also Werte von dimen verwenden, können Sie dies folgendermaßen aufrufen:


float minTextSizePx = getResources().getDimensionPixelSize(R.dimen.min_text_size);
float maxTextWidthPx = getResources().getDimensionPixelSize(R.dimen.max_text_width);
WidgetUtils.fitText(textView, text, minTextSizePx, maxTextWidthPx);

Dies ist die Klasse, die ich benutze:


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
Björn Kechel
/* 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;
0
Dhwanik Gandhi

Dies sollte eine einfache Lösung sein:

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

Wenn eine Transformation wie allCaps gesetzt ist, ist der Ansatz des Speedplane fehlerhaft. Ich habe es behoben, was zu folgendem Code führte (mein Ruf erlaubt es mir leider nicht, dies als Kommentar zur Lösung von speedplane hinzuzufügen):

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
dpoetzsch