为了账号安全,请及时绑定邮箱和手机立即绑定

自动缩放TextView文本以适应范围

自动缩放TextView文本以适应范围

扬帆大鱼 2019-05-30 12:59:36
自动缩放TextView文本以适应范围我正在寻找一种调整包装文本大小的最佳方法TextView因此,它将适合于它的getHL.8和getWidth边界。我并不是简单地寻找一种包装文本的方法-我想确保它既能包装,又足够小,可以完全安装在屏幕上。我在StackOverflow上看到了一些需要自动调整大小的案例,但是它们要么是带有Hack解决方案的非常特殊的情况,要么没有解决方案,或者涉及重新绘制TextView递归,直到它足够小(这是内存紧张,并迫使用户看到文本一步收缩,每次递归)。但我肯定有人找到了一个很好的解决方案,不涉及我正在做的事情:编写几个繁重的例程来解析和测量文本,调整文本的大小,然后重复,直到找到合适的小尺寸。什么例程TextView用来包装文字?难道不能用这些来预测文本是否足够小吗?TL;DR*是否有最佳实践方法来自动调整TextView要在它的getHL.8和getWidth界中安装、包装吗?
查看完整描述

3 回答

?
繁华开满天机

TA贡献1816条经验 获得超4个赞

作为一名移动开发人员,我很难过没有发现任何支持自动调整大小的本地产品。我的搜索没有找到任何对我有用的东西,最后,我花了更多的时间在我的周末,并创建了我自己的自动调整文本视图。我将在这里张贴代码,并希望它将对其他人有用。


该类使用静态布局和原始文本视图的文本绘制来度量高度。从那里,我放下了2个字体像素,并重新测量,直到我有一个适合的大小。最后,如果文本仍然不适合,我附加一个省略号。我有动画文本和重用视图的要求,这在我所拥有的设备上似乎运行得很好,并且对我来说运行得足够快。


/**

 *               DO WHAT YOU WANT TO PUBLIC LICENSE

 *                    Version 2, December 2004

 * 

 * Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>

 * 

 * Everyone is permitted to copy and distribute verbatim or modified

 * copies of this license document, and changing it is allowed as long

 * as the name is changed.

 * 

 *            DO WHAT YOU WANT TO PUBLIC LICENSE

 *   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

 * 

 *  0. You just DO WHAT YOU WANT TO.

 */


import android.content.Context;

import android.text.Layout.Alignment;

import android.text.StaticLayout;

import android.text.TextPaint;

import android.util.AttributeSet;

import android.util.TypedValue;

import android.widget.TextView;


/**

 * Text view that auto adjusts text size to fit within the view.

 * If the text size equals the minimum text size and still does not

 * fit, append with an ellipsis.

 * 

 * @author Chase Colburn

 * @since Apr 4, 2011

 */

public class AutoResizeTextView extends TextView {


    // Minimum text size for this text view

    public static final float MIN_TEXT_SIZE = 20;


    // Interface for resize notifications

    public interface OnTextResizeListener {

        public void onTextResize(TextView textView, float oldSize, float newSize);

    }


    // Our ellipse string

    private static final String mEllipsis = "...";


    // Registered resize listener

    private OnTextResizeListener mTextResizeListener;


    // Flag for text and/or size changes to force a resize

    private boolean mNeedsResize = false;


    // Text size that is set from code. This acts as a starting point for resizing

    private float mTextSize;


    // Temporary upper bounds on the starting text size

    private float mMaxTextSize = 0;


    // Lower bounds for text size

    private float mMinTextSize = MIN_TEXT_SIZE;


    // Text view line spacing multiplier

    private float mSpacingMult = 1.0f;


    // Text view additional line spacing

    private float mSpacingAdd = 0.0f;


    // Add ellipsis to text that overflows at the smallest text size

    private boolean mAddEllipsis = true;


    // Default constructor override

    public AutoResizeTextView(Context context) {

        this(context, null);

    }


    // Default constructor when inflating from XML file

    public AutoResizeTextView(Context context, AttributeSet attrs) {

        this(context, attrs, 0);

    }


    // Default constructor override

    public AutoResizeTextView(Context context, AttributeSet attrs, int defStyle) {

        super(context, attrs, defStyle);

        mTextSize = getTextSize();

    }


    /**

     * When text changes, set the force resize flag to true and reset the text size.

     */

    @Override

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

        mNeedsResize = true;

        // Since this view may be reused, it is good to reset the text size

        resetTextSize();

    }


    /**

     * If the text view size changed, set the force resize flag to true

     */

    @Override

    protected void onSizeChanged(int w, int h, int oldw, int oldh) {

        if (w != oldw || h != oldh) {

            mNeedsResize = true;

        }

    }


    /**

     * Register listener to receive resize notifications

     * @param listener

     */

    public void setOnResizeListener(OnTextResizeListener listener) {

        mTextResizeListener = listener;

    }


    /**

     * Override the set text size to update our internal reference values

     */

    @Override

    public void setTextSize(float size) {

        super.setTextSize(size);

        mTextSize = getTextSize();

    }


    /**

     * Override the set text size to update our internal reference values

     */

    @Override

    public void setTextSize(int unit, float size) {

        super.setTextSize(unit, size);

        mTextSize = getTextSize();

    }


    /**

     * Override the set line spacing to update our internal reference values

     */

    @Override

    public void setLineSpacing(float add, float mult) {

        super.setLineSpacing(add, mult);

        mSpacingMult = mult;

        mSpacingAdd = add;

    }


    /**

     * Set the upper text size limit and invalidate the view

     * @param maxTextSize

     */

    public void setMaxTextSize(float maxTextSize) {

        mMaxTextSize = maxTextSize;

        requestLayout();

        invalidate();

    }


    /**

     * Return upper text size limit

     * @return

     */

    public float getMaxTextSize() {

        return mMaxTextSize;

    }


    /**

     * Set the lower text size limit and invalidate the view

     * @param minTextSize

     */

    public void setMinTextSize(float minTextSize) {

        mMinTextSize = minTextSize;

        requestLayout();

        invalidate();

    }


    /**

     * Return lower text size limit

     * @return

     */

    public float getMinTextSize() {

        return mMinTextSize;

    }


    /**

     * Set flag to add ellipsis to text that overflows at the smallest text size

     * @param addEllipsis

     */

    public void setAddEllipsis(boolean addEllipsis) {

        mAddEllipsis = addEllipsis;

    }


    /**

     * Return flag to add ellipsis to text that overflows at the smallest text size

     * @return

     */

    public boolean getAddEllipsis() {

        return mAddEllipsis;

    }


    /**

     * Reset the text to the original size

     */

    public void resetTextSize() {

        if (mTextSize > 0) {

            super.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSize);

            mMaxTextSize = mTextSize;

        }

    }


    /**

     * Resize text after measuring

     */

    @Override

    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {

        if (changed || mNeedsResize) {

            int widthLimit = (right - left) - getCompoundPaddingLeft() - getCompoundPaddingRight();

            int heightLimit = (bottom - top) - getCompoundPaddingBottom() - getCompoundPaddingTop();

            resizeText(widthLimit, heightLimit);

        }

        super.onLayout(changed, left, top, right, bottom);

    }


    /**

     * Resize the text size with default width and height

     */

    public void resizeText() {


        int heightLimit = getHeight() - getPaddingBottom() - getPaddingTop();

        int widthLimit = getWidth() - getPaddingLeft() - getPaddingRight();

        resizeText(widthLimit, heightLimit);

    }


    /**

     * Resize the text size with specified width and height

     * @param width

     * @param height

     */

    public void resizeText(int width, int height) {

        CharSequence text = getText();

        // Do not resize if the view does not have dimensions or there is no text

        if (text == null || text.length() == 0 || height <= 0 || width <= 0 || mTextSize == 0) {

            return;

        }


        if (getTransformationMethod() != null) {

            text = getTransformationMethod().getTransformation(text, this);

        }


        // Get the text view's paint object

        TextPaint textPaint = getPaint();


        // Store the current text size

        float oldTextSize = textPaint.getTextSize();

        // If there is a max text size set, use the lesser of that and the default text size

        float targetTextSize = mMaxTextSize > 0 ? Math.min(mTextSize, mMaxTextSize) : mTextSize;


        // Get the required text height

        int textHeight = getTextHeight(text, textPaint, width, targetTextSize);


        // Until we either fit within our text view or we had reached our min text size, incrementally try smaller sizes

        while (textHeight > height && targetTextSize > mMinTextSize) {

            targetTextSize = Math.max(targetTextSize - 2, mMinTextSize);

            textHeight = getTextHeight(text, textPaint, width, targetTextSize);

        }


        // If we had reached our minimum text size and still don't fit, append an ellipsis

        if (mAddEllipsis && targetTextSize == mMinTextSize && textHeight > height) {

            // Draw using a static layout

            // modified: use a copy of TextPaint for measuring

            TextPaint paint = new TextPaint(textPaint);

            // Draw using a static layout

            StaticLayout layout = new StaticLayout(text, paint, width, Alignment.ALIGN_NORMAL, mSpacingMult, mSpacingAdd, false);

            // Check that we have a least one line of rendered text

            if (layout.getLineCount() > 0) {

                // Since the line at the specific vertical position would be cut off,

                // we must trim up to the previous line

                int lastLine = layout.getLineForVertical(height) - 1;

                // If the text would not even fit on a single line, clear it

                if (lastLine < 0) {

                    setText("");

                }

                // Otherwise, trim to the previous line and add an ellipsis

                else {

                    int start = layout.getLineStart(lastLine);

                    int end = layout.getLineEnd(lastLine);

                    float lineWidth = layout.getLineWidth(lastLine);

                    float ellipseWidth = textPaint.measureText(mEllipsis);


                    // Trim characters off until we have enough room to draw the ellipsis

                    while (width < lineWidth + ellipseWidth) {

                        lineWidth = textPaint.measureText(text.subSequence(start, --end + 1).toString());

                    }

                    setText(text.subSequence(0, end) + mEllipsis);

                }

            }

        }


        // Some devices try to auto adjust line spacing, so force default line spacing

        // and invalidate the layout as a side effect

        setTextSize(TypedValue.COMPLEX_UNIT_PX, targetTextSize);

        setLineSpacing(mSpacingAdd, mSpacingMult);


        // Notify the listener if registered

        if (mTextResizeListener != null) {

            mTextResizeListener.onTextResize(this, oldTextSize, targetTextSize);

        }


        // Reset force resize flag

        mNeedsResize = false;

    }


    // Set the text size of the text paint object and use a static layout to render text off screen before measuring

    private int getTextHeight(CharSequence source, TextPaint paint, int width, float textSize) {

        // modified: make a copy of the original TextPaint object for measuring

        // (apparently the object gets modified while measuring, see also the

        // docs for TextView.getPaint() (which states to access it read-only)

        TextPaint paintCopy = new TextPaint(paint);

        // Update the text paint object

        paintCopy.setTextSize(textSize);

        // Measure using a static layout

        StaticLayout layout = new StaticLayout(source, paintCopy, width, Alignment.ALIGN_NORMAL, mSpacingMult, mSpacingAdd, true);

        return layout.getHeight();

    }


}

警告。影响Android3.1-4.04的一个重要的修复错误导致所有AutoResizingTextView小部件无法工作。


查看完整回答
反对 回复 2019-05-30
  • 3 回答
  • 0 关注
  • 1129 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信