Metin görünümüne sığdırmak için metin yazı tipi boyutunu ayarlama


178

Android'de metin alanını kapladığı alana sığacak şekilde ayarlamak için herhangi bir yol var mı?

Örneğin bir kullanıyorum TableLayoutve TextViewher satıra birkaç s ekliyorum . TextViewS'nin metni sarmasını istemediğimden , içeriğin yazı tipi boyutunu düşürdüğünü görüyorum.

Herhangi bir fikir?

Denedim measureText, ama sütunun boyutunu bilmediğim için kullanmak zahmetli görünüyor. Bu, yazı tipi boyutunu uygun bir şeye değiştirmek istediğim kod

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'nin koduna göre çözümümü buradan kontrol edin stackoverflow.com/questions/5033012/… Not: Bir döngü ile uygulamadım. Not: teşekkürler dunni
vsm

Yanıtlar:


129

Aşağıdaki çözüm, buradaki tüm önerileri içermektedir. Başlangıçta Dunni tarafından gönderilenlerle başlar. Gjpc gibi bir ikili arama kullanır, ancak biraz daha okunabilir. Ayrıca gregm hata düzeltmeleri ve kendi hata düzeltme içerir.

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
Tüm geri bildirimleri birleştirdiğiniz için teşekkür ederiz. Çözümün sadece genişliği dikkate aldığını görüyorum . Benim sorunum düşkün yüksekliği aşması.
AlikElzin-kilaka

3
Oh, kısmen çalışma zamanında çalışıyor gibi görünüyor. Bir aygıtta öykünücüde metin yarıya kadar kesilir. Eclipse mizanpaj düzenleyicisinde iyi görünüyor. Herhangi bir fikir?
AlikElzin-kilaka

1
Bu yazı hile yapacak gibi görünüyor (hi / lo için özel xml öznitelikleri ekleyerek): stackoverflow.com/a/8090772/156611
Ed Sinek

7
Bu harika bir çözümdü! Başka birisinin android geliştirmede yeni olması ve XML'de genişletilmiş bir görünümün nasıl uygulanacağını bilmemesi durumunda, şöyle görünür: <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
Harika iş! Düğmelerle de çalıştım. float hi = this.getHeight() - this.getPaddingBottom() - this.getPaddingTop();
Metin

28

TextView genişleten ve bunu yapan bir sınıf yazdım. Önerdiğiniz gibi sadece tedbir metni kullanır. Temel olarak maksimum metin boyutuna ve minimum metin boyutuna (değiştirilebilir) sahiptir ve sığacak en büyük olanı bulana kadar aralarındaki boyutlar arasında 1'lik adımlarla ilerler. Özellikle zarif değil, ama başka bir yol bilmiyorum.

İşte kod:

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
İkili bir arama genellikle doğrusal bir aramadan daha hızlı olur.
Ted Hopp

ben "onSizeChanged" yerine "onLayout" de refitText yapmak daha makul olacağını düşünüyorum
Jacky

Lütfen sorunuma bir göz
Muhammed

17

Bu speedplane enFontFitTextView , ancak yalnızca azalır metin uygun hale getirilmesi için gerekirse yazı tipi boyutu ve aksi onun yazı tipi boyutunu tutar. Yazı tipi boyutunu yüksekliğe sığacak şekilde büyütmez.

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

}

İşte xml'de nasıl kullanılabileceğine dair bir örnek:

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

Bu, metin genişliğe sığdığı sürece yazı tipi boyutunu 60 punto tutar. Metin daha uzunsa, yazı tipi boyutunu küçültür. Bu durumda, TextViews yüksekliği de nedeniyle değişecektir height=wrap_content.

Herhangi bir hata bulursanız, düzenlemekten çekinmeyin.


Nasıl kullandın? Cevabım için nasıl kullandığımın bir örneğini ekledim. Çeşitli android sürümleri, emülatörleri ve ADT Grafiksel Düzenleyici ile test ettim.
sulai

Aslında, belirli bir yükseklik belirtmiyordum. OnMeasure'ınız, görünümün isterse devralmasına izin veriyor. Bir çözüm bulmayı başardım, rutinin son satırınıthis.setMeasuredDimension(parentWidth, height);
PearsonArtPhoto 23:12

1
Büyük girdi, ben kod :) Şimdi çalışır düzeltilir match_parentve wrap_content.
sulai

Neden sadece s.isEmpty () yerine "" .equals (s) boş dize yazma için test yapıyorsunuz? Veya s.length () == 0? Neden bu eşitlik testlerini bazen görebildiğimi anlamıyorum.
Zordid

1
@PratikButani Bunu belirttiğin için teşekkürler. eşdeğer text.isEmpty()olacaktır "".equals(text).
sulai

14

İşte emülatör ve telefonlarda çalışan ama Eclipse düzen düzenleyicide çok iyi olmayan çözümüm. Kilaka'nın kodundan ilham aldı, ancak metnin boyutu Paint'ten değil, çağrı yapılan TextView'un ölçülmesinden elde edildi measure(0, 0).

Java sınıfı:

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 özellik dosyası:

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

Bu sınıfın en son sürümü için github'umu kontrol et. Umarım birisi için yararlı olabilir. Bir hata bulunursa veya kodun açıklanması gerekiyorsa, Github'da bir sorun açmaktan çekinmeyin.


Galaxy Nexus'umda kaymakam çalışıyor, ancak küçük ekranlı cihazlarda sorun yaşıyorum.
Tony Ceralva

Küçük ekranlı cihazlarda sorununuz nedir?
yDelouis

Üzgünüz, sorun küçük ekranlarda değil, görünümünüz Android 4.0, emülatör dahil tüm cihazlarda çalışmıyor.
GitHub'ınızda

12

Https://stackoverflow.com/users/234270/speedplane adresine çok teşekkür ederiz . Mükemmel cevap!

Burada yanı sıra yüksekliği dikkat ve yazı tipi boyutunu sınırlamak için bir maxFontSize özniteliği ile gelen yanıtı geliştirilmiş bir sürümü (benim durumumda yararlı oldu, bu yüzden paylaşmak istedim):

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

Karşılık gelen /res/values/attr.xml dosyası:

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

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

</resources>

Misal:

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

Yeni maxFontSizeözelliği kullanmak için xmlns:res-auto="http://schemas.android.com/apk/res-auto", örnekte şov olarak eklemeyi unutmayın .


Üzgünüz, ama yine de her durumda işe yaramıyor. Ayrıca çoklu çizgileri doğru işlemediğini düşünüyorum.
android geliştirici

Oh .. Bana bunun işe yaramadığı bir dava verebilir misin? (Çoklu hat desteklenmiyor, haklısın)
Pascal

Birçok vaka. Doğru olduğunu kanıtlamak için rastgele bir test cihazı oluşturdum. İşte bir örnek: görünümün genişliği: 317px, yükseklik: 137px, metin: "q7Lr". Ne görüyorum bu: tinypic.com/view.php?pic=2dv5yf9&s=6 . İşte yaptığım örnek proje: mega.co.nz/… . Böyle bir görünüm çok satır işlemek, herhangi bir yazı tipi boyutunu desteklemeli, sadece genişliği değil, yüksekliği işlemeli, ... Ne yazık ki, bulduğum örneklerin hiçbiri iyi çalışmadı.
android geliştirici

tamam, kesinlikle tam bir çözüm değil. afedersiniz. Şimdilik onu geliştirmek için zaman yok .. Eğer geliştirebilirseniz, çözüm göndermek için tereddüt etmeyin.
Pascal

1
Birinin iyi bir çözüm yapabileceğini umarak testlerimi gösteren yeni bir iş parçacığı oluşturdum: stackoverflow.com/questions/16017165/…
android geliştirici

7

Aynı problemi yaşadım ve benim için işe yarayan bir ders yazdım. Temel olarak, metni ayrı bir tuvalde çizmek ve uygun bir yazı tipi boyutu bulana kadar yeniden ölçmek için statik bir düzen kullandım. Aşağıdaki konuda yayınlanan sınıfı görebilirsiniz. Umut ediyorum bu yardım eder.

Metni Sınırlara Sığacak Şekilde Otomatik Ölçekle


6

Bunu üçüncü taraf kitaplığı veya widget olmadan yapabilirsiniz. API düzeyi 26'da TextView'de yerleşiktir android:autoSizeTextType="uniform". Kendinize ekleyin TextViewve yüksekliğini ayarlayın. Bu kadar.

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

TextViewCompatUyumluluk için de kullanabilirsiniz .


5

On üzerinde hafif değişiklik

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

Ve refitText üzerinde ikili arama:

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
İkili bir arama yerine, basit bir ölçeklendirme oldukça iyi çalışır: trySize *= availableWidth / measured_width(sonra minTextSize'e kenetlenir).
Ted Hopp

5

Benim için güzel çalışmak için aşağıdakileri buldum. Döngü yapmaz ve hem yükseklik hem de genişliği hesaba katar. Görünümde setTextSize çağrılırken PX birimini belirtmenin önemli olduğunu unutmayın. Bunun için bir önceki yazıdaki ipucu sayesinde!

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

İşte görünümden getPaint () geçirerek kullandığım rutin. 'Geniş' karaktere sahip 10 karakterli bir dize, gerçek dizedeki bağımsız genişliği tahmin etmek için kullanılır.

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

Nasıl xml düzenine dahil edersiniz?
AlikElzin-kilaka

Bu kod, varolan bir görünümdeki metni kısıtlı boyut alanına yerleştirmek için yararlıdır veya TextView'dan kendi türetilmiş sınıfınızı oluşturabilir ve diğer yayınlarda gösterildiği gibi onMeasure'u geçersiz kılabilirsiniz. Kendi başına bir düzende kullanılamaz.
Glenn

Görünümün çizildikten sonra boyutlarını kontrol ettiğinizden emin olun . Bunu onCreate () sırasında yapmak çok erken. Ölçümlerin doğru zamanda alındığından emin olmak için ViewTreeObserver kullandım.
Scott Biggs

4

Değişiklikle çalışır

Aksi takdirde setTextSize değerin SP birimlerinde olduğunu varsayacağından, metin görünümü boyutunu şu şekilde ayarlamanız gerekir:

setTextSize(TypedValue.COMPLEX_UNIT_PX, trySize);

Ve bu kodu açıkça eklemeniz gerekiyordu.

@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

Yalnızca API Level 26 ve üstü sürümlerde çalıştığından app:autoSizeTextType="uniform"geriye dönük uyumluluk için kullanın android:autoSizeTextType="uniform".


2

Yukarıda Dunni çözümünün bir varyasyonunu kullandım, ancak bu özel kod benim için çalışmadı. Özellikle, görünümün Paint nesnesinin özelliklerine sahip olacak şekilde Paint nesnesini kullanmaya çalışırken ve daha sonra tedbirText () öğesini çağırırken, görünümün Paint nesnesini doğrudan çağırmakla aynı değeri döndürmez. Belki de davranışlarımı farklı kılan görüşlerimin oluşturulma biçiminde bazı farklılıklar vardır.

Benim çözümüm, görünümün Yazı Tipi boyutunu birden çok kez değiştirmede bazı performans cezaları olsa da doğrudan görünümün Paint'ini kullanmaktı.


2

Speedplane'dan mükemmel çözümü geliştirmek için çalışıyorum ve bununla geldim. Kenar boşluğunu, metnin dikey olarak doğru ortalanması gerektiği şekilde ayarlamak da dahil olmak üzere yüksekliği yönetir.

Bu, en iyi şekilde göründüğü gibi genişliği elde etmek için aynı işlevi kullanır, ancak yükseklik hiçbir yerde sağlanmadığı için yüksekliği elde etmek için farklı bir işlev kullanır. Yapılması gereken bazı düzeltmeler var, ama göze hoş bakarken bunu yapmanın bir yolunu buldum.

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

Bu kütüphaneyi bulana kadar soooo için projelerimde bu acıyı yaşadım:

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

Sadece xml ihtiyaçlarınız tarafından eklemeniz gerekir ve bitti. Örneğin:

<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

Önceki posterlerden esinlenerek çözümümü paylaşmak istedim. Kullanılabilir alana sığdırmak için önceki yazı tipi boyutuna uygulanan ölçek faktörü ile çalışır. TextViews onDraw yönteminin beklenmedik davranışını önlemeye ek olarak, metni tek başına çizer.

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
Sadece kodunuzu okuyarak, bunun işe yarayacağını sanmıyorum. Unutmayın, soru metin görünümünün yazı tipi boyutunu görünüme otomatik olarak ayarlamakla ilgilidir. Sabit yazı tipi boyutu belirlediniz.
sulai

0

Bu basit bir çözüm olmalıdır:

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 öğesini genişletin ve onDraw'ı aşağıdaki kodla geçersiz kılın. Metin en boy oranını koruyacak, ancak boşluğu doldurmak için boyutlandıracaktır. Gerekirse kodu germek için kolayca değiştirebilirsiniz.

  @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

Bir metin görünümünü belirli bir genişliğe sığdıran kısa bir yardımcı sınıf yazdım ve minimum metin boyutuna ulaşılamıyorsa sonunda elipsize "..." ekledim.

Metnin yalnızca sığana kadar veya minimum metin boyutuna ulaşılana kadar küçüldüğünü unutmayın. Büyük boyutlarla test etmek için yardım yöntemini çağırmadan önce metin boyutunu büyük bir sayıya ayarlayın.

Pikseller gerektirir, bu nedenle dimen değerlerini kullanıyorsanız, bunu şu şekilde çağırabilirsiniz:


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

Kullandığım sınıf bu:


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

Bu, birden fazla metin satırını destekliyor mu?
Roymunson

0

AllCaps gibi bir dönüşüm ayarlanırsa, hız düzleminin yaklaşımı hatalıdır. Aşağıdaki kodla sonuçlanan sorunu düzelttim (özür dilerim, itibarım bunu speedplane'in çözümüne yorum olarak eklememe izin vermiyor):

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

Bu doğru yolu ya da çalışma bt bilmiyorum ... senin bakışını almak ve OnGlobalLayoutListener () kontrol ve textview linecount olsun sonra textSize ayarlayın.

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

Çok basit birkaç satır kodu ..

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.