Cách điều chỉnh kích thước phông chữ văn bản cho phù hợp với textview


178

Có cách nào trong Android để điều chỉnh textize trong textview để phù hợp với không gian mà nó chiếm không?

Ví dụ: tôi đang sử dụng một TableLayoutvà thêm một vài TextViews vào mỗi hàng. Vì tôi không muốn TextViews bọc văn bản, tôi thấy rằng nó làm giảm kích thước phông chữ của nội dung.

Có ý kiến ​​gì không?

Tôi đã thử measureText, nhưng vì tôi không biết kích thước của cột nên có vẻ rắc rối khi sử dụng. Đây là mã nơi tôi muốn thay đổi kích thước phông chữ thành một cái gì đó phù hợp

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

Kiểm tra giải pháp của tôi dựa trên mã của dunni tại đây stackoverflow.com/questions/5033012/ Khăn Lưu ý: Tôi đã không thực hiện nó với một vòng lặp. Tái bút: cảm ơn dunni
vsm

Câu trả lời:


129

Giải pháp dưới đây kết hợp tất cả các gợi ý ở đây. Nó bắt đầu với những gì ban đầu được đăng bởi Dunni. Nó sử dụng một tìm kiếm nhị phân như của gjpc, nhưng nó dễ đọc hơn một chút. Nó cũng bao gồm sửa lỗi gregm của tôi và sửa lỗi của riêng tôi.

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
Cảm ơn đã kết hợp tất cả các thông tin phản hồi. Tôi thấy rằng các giải pháp chỉ xem xét chiều rộng . Vấn đề của tôi là fond vượt quá chiều cao.
AlikElzin-kilaka

3
Ồ, nó dường như làm việc một phần trong thời gian chạy. Trên một trình giả lập ot thiết bị, văn bản được cắt một nửa. Trên trình soạn thảo bố trí Eclipse, nó trông ổn. Có ý kiến ​​gì không?
AlikElzin-kilaka

1
bài đăng này có vẻ như sẽ thực hiện thủ thuật (thêm thuộc tính xml tùy chỉnh cho hi / lo): stackoverflow.com/a/8090772/156611
Ed Sinek

7
Đây là một giải pháp tuyệt vời! Trong trường hợp bất kỳ ai khác mới phát triển Android và không biết cách triển khai chế độ xem mở rộng trong XML, thì nó trông như thế này: <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
Công việc tuyệt vời Nó cũng hoạt động với các nút. Để chăm sóc chiều cao của textview chỉ cần đặtfloat hi = this.getHeight() - this.getPaddingBottom() - this.getPaddingTop();
AlexGuti

28

Tôi đã viết một lớp mở rộng TextView và thực hiện điều này. Nó chỉ sử dụng biện pháp như bạn đề nghị. Về cơ bản, nó có kích thước văn bản tối đa và kích thước văn bản tối thiểu (có thể thay đổi) và nó chỉ chạy qua các kích thước giữa chúng theo số giảm cho đến khi tìm thấy kích thước lớn nhất phù hợp. Không đặc biệt thanh lịch, nhưng tôi không biết cách nào khác.

Đây là mã:

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
Một tìm kiếm nhị phân thường sẽ nhanh hơn tìm kiếm tuyến tính.
Ted Hopp

Tôi nghĩ sẽ hợp lý hơn khi thực hiện refitText tại "onLayout" thay vì "onSizeChanged"
Jacky

Bạn có thể vui lòng xem vấn đề của tôi stackoverflow.com/questions/36265448/ Khăn
Muhammad

17

Đây là speedplane'sFontFitTextView , nhưng nó chỉ giảm kích thước phông chữ nếu cần để làm cho văn bản phù hợp và giữ kích thước phông chữ của nó khác. Nó không tăng kích thước phông chữ để phù hợp với chiều cao.

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

}

Dưới đây là một ví dụ về cách sử dụng nó trong 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" />

Điều này sẽ giữ kích thước phông chữ đến 60sp miễn là văn bản vừa với chiều rộng. Nếu văn bản dài hơn, nó sẽ giảm kích thước phông chữ. Trong trường hợp này, TextViewchiều cao của s cũng sẽ thay đổi vì height=wrap_content.

Nếu bạn tìm thấy bất kỳ lỗi, hãy chỉnh sửa.


Làm thế nào bạn sử dụng nó? Tôi đã thêm một ví dụ về cách tôi sử dụng nó để trả lời. Tôi đã thử nghiệm nó với các phiên bản Android khác nhau, trình giả lập và Trình chỉnh sửa bố cục đồ họa ADT.
sulai

Về cơ bản, tôi đã không chỉ định một chiều cao cụ thể. OnMeasure của bạn đang cho phép chế độ xem tiếp quản nếu muốn. Tôi đã xoay sở để đưa ra một giải pháp, thay đổi dòng cuối cùng trong thói quen thànhthis.setMeasuredDimension(parentWidth, height);
PearsonArtPhoto

1
Đầu vào tuyệt vời, tôi đã sửa mã :) Bây giờ nó hoạt động với match_parentwrap_content.
sulai

Tại sao bạn kiểm tra viết chuỗi rỗng "" .equals (s) thay vì chỉ đơn giản là s.isEmpty () ?? Hoặc s.length () == 0? Không hiểu tại sao đôi khi tôi có thể thấy các bài kiểm tra bình đẳng này.
Zordid

1
@PratikButani cảm ơn bạn đã chỉ ra điều này. để tương đương text.isEmpty()sẽ "".equals(text).
sulai

14

Đây là giải pháp của tôi hoạt động trên trình giả lập và điện thoại nhưng không tốt lắm về trình soạn thảo bố cục Eclipse. Nó lấy cảm hứng từ mã của kilaka nhưng kích thước của văn bản không được lấy từ Paint mà từ việc đo chính TextView đang gọi measure(0, 0).

Lớp Java:

public class FontFitTextView extends TextView
{
    private static final float THRESHOLD = 0.5f;

    private enum Mode { Width, Height, Both, None }

    private int minTextSize = 1;
    private int maxTextSize = 1000;

    private Mode mode = Mode.None;
    private boolean inComputation;
    private int widthMeasureSpec;
    private int heightMeasureSpec;

    public FontFitTextView(Context context) {
            super(context);
    }

    public FontFitTextView(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
    }

    public FontFitTextView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);

            TypedArray tAttrs = context.obtainStyledAttributes(attrs, R.styleable.FontFitTextView, defStyle, 0);
            maxTextSize = tAttrs.getDimensionPixelSize(R.styleable.FontFitTextView_maxTextSize, maxTextSize);
            minTextSize = tAttrs.getDimensionPixelSize(R.styleable.FontFitTextView_minTextSize, minTextSize);
            tAttrs.recycle();
    }

    private void resizeText() {
            if (getWidth() <= 0 || getHeight() <= 0)
                    return;
            if(mode == Mode.None)
                    return;

            final int targetWidth = getWidth();
            final int targetHeight = getHeight();

            inComputation = true;
            float higherSize = maxTextSize;
            float lowerSize = minTextSize;
            float textSize = getTextSize();
            while(higherSize - lowerSize > THRESHOLD) {
                    textSize = (higherSize + lowerSize) / 2;
                    if (isTooBig(textSize, targetWidth, targetHeight)) {
                            higherSize = textSize; 
                    } else {
                            lowerSize = textSize;
                    }
            }
            setTextSize(TypedValue.COMPLEX_UNIT_PX, lowerSize);
            measure(widthMeasureSpec, heightMeasureSpec);
            inComputation = false;
    }

    private boolean isTooBig(float textSize, int targetWidth, int targetHeight) {
            setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
            measure(0, 0);
            if(mode == Mode.Both)
                    return getMeasuredWidth() >= targetWidth || getMeasuredHeight() >= targetHeight;
            if(mode == Mode.Width)
                    return getMeasuredWidth() >= targetWidth;
            else
                    return getMeasuredHeight() >= targetHeight;
    }

    private Mode getMode(int widthMeasureSpec, int heightMeasureSpec) {
            int widthMode = MeasureSpec.getMode(widthMeasureSpec);
            int heightMode = MeasureSpec.getMode(heightMeasureSpec);
            if(widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY)
                    return Mode.Both;
            if(widthMode == MeasureSpec.EXACTLY)
                    return Mode.Width;
            if(heightMode == MeasureSpec.EXACTLY)
                    return Mode.Height;
            return Mode.None;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            if(!inComputation) {
                    this.widthMeasureSpec = widthMeasureSpec;
                    this.heightMeasureSpec = heightMeasureSpec;
                    mode = getMode(widthMeasureSpec, heightMeasureSpec);
                    resizeText();
            }
    }

    protected void onTextChanged(final CharSequence text, final int start, final int before, final int after) {
            resizeText();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            if (w != oldw || h != oldh)
                    resizeText();
    }

    public int getMinTextSize() {
            return minTextSize;
    }

    public void setMinTextSize(int minTextSize) {
            this.minTextSize = minTextSize;
            resizeText();
    }

    public int getMaxTextSize() {
            return maxTextSize;
    }

    public void setMaxTextSize(int maxTextSize) {
            this.maxTextSize = maxTextSize;
            resizeText();
    }
}

Tệp thuộc tính XML:

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

Kiểm tra github của tôi để biết phiên bản mới nhất của lớp này. Tôi hy vọng nó có thể hữu ích cho một ai đó. Nếu một lỗi được tìm thấy hoặc nếu mã cần giải thích, vui lòng mở một vấn đề trên Github.


Nó hoạt động hoàn hảo trên Galaxy Nexus của tôi, nhưng tôi gặp sự cố trên các thiết bị có màn hình nhỏ.
Tony Ceralva

Vấn đề của bạn trên các thiết bị có màn hình nhỏ là gì?
yDelouis

Xin lỗi, vấn đề không nằm ở màn hình nhỏ, chế độ xem của bạn không hoạt động trong tất cả các thiết bị có Android 4.0, bao gồm trình giả lập. Tôi đã mở vấn đề mới trong GitHub của bạn
Tony Ceralva

12

Cảm ơn rất nhiều về https://stackoverflow.com/users/234270/speedplane . Câu trả lời chính xác!

Đây là phiên bản cải tiến của phản hồi của anh ấy, cũng quan tâm đến chiều cao và đi kèm với thuộc tính maxFontSize để giới hạn kích thước phông chữ (rất hữu ích trong trường hợp của tôi, vì vậy tôi muốn chia sẻ nó):

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

Tệp /res/values/attr.xml tương ứng:

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

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

</resources>

Thí dụ:

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

Để sử dụng maxFontSizethuộc tính mới , đừng quên thêm xmlns:res-auto="http://schemas.android.com/apk/res-auto"như hiển thị trong ví dụ.


Xin lỗi, nhưng nó vẫn không hoạt động trên tất cả các trường hợp. Tôi cũng nghĩ rằng nó không xử lý nhiều dòng chính xác.
nhà phát triển Android

Oh .. Bạn có thể cho tôi một trường hợp mà nó không hoạt động? (Nhiều dòng không được hỗ trợ, bạn đã đúng)
Pascal

Nhiều trường hợp. Tôi đã tạo một thử nghiệm ngẫu nhiên chỉ để chứng minh rằng nó chính xác. Đây là một mẫu: chiều rộng của chế độ xem: 317px, height: 137px, text: "q7Lr". Những gì tôi thấy là đây: tinypic.com/view.php?pic=2dv5yf9&s=6 . đây là dự án mẫu tôi đã thực hiện: mega.co.nz/ . Tôi nghĩ rằng chế độ xem như vậy sẽ xử lý nhiều dòng, hỗ trợ mọi kích thước phông chữ, xử lý chiều cao và không chỉ chiều rộng, ... Đáng buồn thay, không có mẫu nào tôi tìm thấy đã hoạt động tốt.
nhà phát triển Android

ok, chắc chắn không phải là một giải pháp hoàn chỉnh. lấy làm tiếc. Không có thời gian để cải thiện nó trong thời gian này .. Nếu bạn có thể cải thiện nó, đừng ngần ngại đăng giải pháp của bạn.
Pascal

1
Tôi đã tạo một chủ đề mới cho thấy các thử nghiệm của mình, hy vọng rằng ai đó sẽ có thể tạo ra một giải pháp tốt: stackoverflow.com/questions/16017165/ Kẻ
nhà phát triển Android

7

Tôi có cùng một vấn đề và đã viết một lớp dường như làm việc cho tôi. Về cơ bản, tôi đã sử dụng một bố cục tĩnh để vẽ văn bản trong một khung vẽ riêng biệt và hài lòng cho đến khi tôi tìm thấy một kích thước phông chữ phù hợp. Bạn có thể thấy các lớp được đăng trong chủ đề dưới đây. Tôi hy vọng nó sẽ giúp.

Tự động chia tỷ lệ TextView Text để vừa trong giới hạn


6

Bây giờ bạn có thể làm điều này mà không cần thư viện bên thứ ba hoặc một widget. Nó được tích hợp vào TextView trong API cấp 26. Thêm android:autoSizeTextType="uniform"vào TextViewvà đặt chiều cao cho nó. Đó là tất cả.

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

Bạn cũng có thể sử dụng TextViewCompatđể tương thích.


5

Sửa đổi nhẹ để 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);
}

Và tìm kiếm nhị phân trên 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
Thay vì tìm kiếm nhị phân, một tỷ lệ đơn giản hoạt động khá tốt: trySize *= availableWidth / measured_width(sau đó được kẹp vào minTextSize).
Ted Hopp

5

Tôi tìm thấy những điều sau đây để làm việc tốt cho tôi. Nó không lặp và chiếm cả chiều cao và chiều rộng. Lưu ý rằng điều quan trọng là chỉ định đơn vị PX khi gọi setTextSize trên chế độ xem. Nhờ các mẹo trong một bài viết trước cho điều này!

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

Đây là thói quen tôi sử dụng, chuyển qua getPaint () từ chế độ xem. Chuỗi 10 ký tự có ký tự 'wide' được sử dụng để ước tính độ rộng độc lập với chuỗi thực tế.

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

Làm thế nào để bạn kết hợp nó trong một bố cục xml?
AlikElzin-kilaka

Mã này hữu ích để đặt văn bản trong chế độ xem hiện tại vào vùng kích thước bị ràng buộc hoặc bạn có thể tạo lớp dẫn xuất của riêng mình từ TextView và ghi đè onMeasure như trong các bài đăng khác. Tự nó không thể được sử dụng trong một bố cục.
Glenn

Đảm bảo rằng bạn kiểm tra kích thước của chế độ xem sau khi được vẽ. Làm điều này trong onCreate () là quá sớm. Tôi đã sử dụng ViewTreeObserver để đảm bảo các phép đo được thực hiện đúng lúc.
Scott Bigss

4

Hoạt động với sửa đổi

Bạn cần đặt kích thước chế độ xem văn bản như thế này vì nếu không thì setTextSize giả sử giá trị được tính theo đơn vị SP:

setTextSize(TypedValue.COMPLEX_UNIT_PX, trySize);

Và bạn cần phải thêm mã này một cách rõ ràng.

@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

Sử dụng app:autoSizeTextType="uniform"để tương thích ngược vì android:autoSizeTextType="uniform"chỉ hoạt động trong API cấp 26 trở lên.


2

Tôi đã sử dụng một biến thể của giải pháp Dunni ở trên, nhưng mã cụ thể đó không hoạt động với tôi. Cụ thể, khi cố gắng sử dụng đối tượng Paint được đặt để có các đặc điểm của đối tượng Paint của chế độ xem, sau đó gọi hàm đo lường (), nó không trả về cùng giá trị như gọi trực tiếp đối tượng Paint của khung nhìn. Có lẽ có một số khác biệt trong cách thiết lập quan điểm của tôi khiến hành vi trở nên khác biệt.

Giải pháp của tôi là trực tiếp sử dụng Paint của chế độ xem, mặc dù có thể có một số hình phạt về hiệu suất trong việc thay đổi kích thước phông chữ cho chế độ xem nhiều lần.


2

Tôi đã làm việc để cải thiện giải pháp tuyệt vời từ máy bay tốc độ, và đã đưa ra điều này. Nó quản lý chiều cao, bao gồm thiết lập lề sao cho văn bản phải được căn giữa chính xác theo chiều dọc.

Điều này sử dụng cùng một chức năng để có được chiều rộng, vì nó có vẻ hoạt động tốt nhất, nhưng nó sử dụng một chức năng khác để có được chiều cao, vì chiều cao không được cung cấp ở bất cứ đâu. Có một số chỉnh sửa cần phải được thực hiện, nhưng tôi đã tìm ra một cách để làm điều đó, trong khi trông vừa mắt.

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

Tôi đã có nỗi đau này trong các dự án của mình cho đến khi tôi tìm thấy thư viện này:

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

Bạn chỉ cần thêm xml theo nhu cầu của bạn và thế là xong. Ví dụ:

<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

Lấy cảm hứng từ các áp phích trước tôi muốn chia sẻ giải pháp của tôi. Nó hoạt động với một yếu tố tỷ lệ được áp dụng cho kích thước phông chữ trước đó để làm cho nó phù hợp với không gian có sẵn. Ngoài ra, để ngăn chặn hành vi không mong muốn của phương thức TextViews onDraw, nó chỉ đơn giản là tự vẽ văn bản.

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
Chỉ cần đọc mã của bạn, tôi không nghĩ rằng nó sẽ hoạt động. Hãy nhớ rằng, câu hỏi là về việc tự động điều chỉnh kích thước phông chữ của chế độ xem văn bản để phù hợp với chế độ xem. Bạn đặt một cỡ chữ cố định.
sulai

0

Đây là một giải pháp đơn giản:

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

Mở rộng TextView và ghi đè onDraw bằng mã bên dưới. Nó sẽ giữ tỷ lệ khung hình văn bản nhưng kích thước nó để lấp đầy không gian. Bạn có thể dễ dàng sửa đổi mã để kéo dài nếu cần thiết.

  @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

Tôi đã viết một lớp trình trợ giúp ngắn làm cho một textview vừa vặn trong một chiều rộng nhất định và thêm dấu chấm lửng "..." vào cuối nếu không thể đạt được kích thước văn bản tối thiểu.

Hãy nhớ rằng nó chỉ làm cho văn bản nhỏ hơn cho đến khi nó vừa hoặc cho đến khi đạt được kích thước văn bản tối thiểu. Để kiểm tra với kích thước lớn, hãy đặt textize thành một số lớn trước khi gọi phương thức trợ giúp.

Phải mất Pixels, vì vậy nếu bạn đang sử dụng các giá trị từ mờ, bạn có thể gọi nó như thế này:


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

Đây là lớp tôi sử dụng:


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

Điều này có hỗ trợ nhiều dòng văn bản không?
Roymunson

0

Nếu một thông báo như allCaps được đặt, cách tiếp cận của speedplane là lỗi. Tôi đã sửa nó, dẫn đến đoạn mã sau (xin lỗi, danh tiếng của tôi không cho phép tôi thêm đoạn mã này dưới dạng nhận xét vào giải pháp của 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

Tôi không biết đây là cách chính xác hay không bt hoạt động của nó ... hãy xem và kiểm tra OnGlobalLayoutListener () và lấy textview linecount sau đó đặt textSize.

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

Mã dòng rất đơn giản của nó ..

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.