Auto Scale TextView Text to Fit within Bounds

后端 未结 30 2660
囚心锁ツ
囚心锁ツ 2020-11-21 05:49

I\'m looking for an optimal way to resize wrapping text in a TextView so that it will fit within its getHeight and getWidth bounds. I\'m not simply looking for

相关标签:
30条回答
  • 2020-11-21 06:05

    My need was to resize text in order to perfectly fit view bounds. Chase's solution only reduces text size, this one enlarges also the text if there is enough space.

    To make all fast & precise i used a bisection method instead of an iterative while, as you can see in resizeText() method. That's why you have also a MAX_TEXT_SIZE option. I also included onoelle's tips.

    Tested on Android 4.4

    /**
     *               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 = 26;
    
        // Maximum text size for this text view
        public static final float MAX_TEXT_SIZE = 128;
    
        private static final int BISECTION_LOOP_WATCH_DOG = 30;
    
        // 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 = MAX_TEXT_SIZE;
    
        // 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;
            }
    
            // Get the text view's paint object
            TextPaint textPaint = getPaint();
    
            // Store the current text size
            float oldTextSize = textPaint.getTextSize();
    
            // Bisection method: fast & precise
            float lower = mMinTextSize;
            float upper = mMaxTextSize;
            int loop_counter=1;
            float targetTextSize = (lower+upper)/2;
            int textHeight = getTextHeight(text, textPaint, width, targetTextSize);
            while(loop_counter < BISECTION_LOOP_WATCH_DOG && upper - lower > 1) {
                targetTextSize = (lower+upper)/2;
                textHeight = getTextHeight(text, textPaint, width, targetTextSize);
                if(textHeight > height)
                    upper = targetTextSize;
                else
                    lower = targetTextSize;
                loop_counter++;
            }
    
            targetTextSize = lower;
            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 paintCopy = new TextPaint(textPaint);
                paintCopy.setTextSize(targetTextSize);
                StaticLayout layout = new StaticLayout(text, paintCopy, 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 = paintCopy.measureText(mEllipsis);
    
                        // Trim characters off until we have enough room to draw the ellipsis
                        while(width < lineWidth + ellipseWidth) {
                            lineWidth = paintCopy.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 originalPaint, 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 paint = new TextPaint(originalPaint);
            // Update the text paint object
            paint.setTextSize(textSize);
            // Measure using a static layout
            StaticLayout layout = new StaticLayout(source, paint, width, Alignment.ALIGN_NORMAL, mSpacingMult, mSpacingAdd, true);
            return layout.getHeight();
        }
    
    }
    
    0 讨论(0)
  • 2020-11-21 06:06

    Here is the approach I take. It's very simple. It uses successive approximation to zero in on the fontsize and can generally have it figured out in less than 10 iterations. Just replace "activityWidth" with the width of whatever view you are using to display the text in. In my example, it's set as a private field to the screen's width. The inital fontsize of 198 is only set in the event the method generates an exception (which really should never happen):

      private float GetFontSizeForScreenWidth(String text)
      {
        float fontsize = 198;
    
        try
        {
          Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
          paint.setColor(Color.RED);
          Typeface typeface = Typeface.create("Helvetica", Typeface.BOLD);
          paint.setTypeface(typeface);
          paint.setTextAlign(Align.CENTER);
    
          int lowVal = 0;
          int highVal = 2000;
          int currentVal = highVal;
    
          /*
           * Successively approximate the screen size until it is 
           * within 2 pixels of the maximum screen width. Generally
           * this will get you to the closest font size within about 10
           * iterations.
           */
    
          do
          {
            paint.setTextSize(currentVal);
            float textWidth = paint.measureText(text);
    
            float diff = activityWidth - textWidth;
    
            if ((diff >= 0) && (diff <= 2))
            {
              fontsize = paint.getTextSize();
              return fontsize;
            }
    
            if (textWidth > activityWidth)
              highVal = currentVal;
            else if (textWidth < activityWidth)
              lowVal = currentVal;
            else
            {
              fontsize = paint.getTextSize();
              return fontsize;
            }
    
            currentVal = (highVal - lowVal) / 2 + lowVal;
    
          } while (true);      
        }
        catch (Exception ex)
        {
          return fontsize;
        }
      }
    
    0 讨论(0)
  • 2020-11-21 06:08

    I just created the following method (based on the ideas of Chase) which might help you if you want to draw text to any canvas:

    private static void drawText(Canvas canvas, int xStart, int yStart,
            int xWidth, int yHeigth, String textToDisplay,
            TextPaint paintToUse, float startTextSizeInPixels,
            float stepSizeForTextSizeSteps) {
    
        // Text view line spacing multiplier
        float mSpacingMult = 1.0f;
        // Text view additional line spacing
        float mSpacingAdd = 0.0f;
        StaticLayout l = null;
        do {
            paintToUse.setTextSize(startTextSizeInPixels);
            startTextSizeInPixels -= stepSizeForTextSizeSteps;
            l = new StaticLayout(textToDisplay, paintToUse, xWidth,
                    Alignment.ALIGN_NORMAL, mSpacingMult, mSpacingAdd, true);
        } while (l.getHeight() > yHeigth);
    
        int textCenterX = xStart + (xWidth / 2);
        int textCenterY = (yHeigth - l.getHeight()) / 2;
    
        canvas.save();
        canvas.translate(textCenterX, textCenterY);
        l.draw(canvas);
        canvas.restore();
    }
    

    This could be used e.g. in any onDraw() method of any custom view.

    0 讨论(0)
  • 2020-11-21 06:08

    Thanks to Chase and onoelle, for the lazy programmers, let me post here a working version of their fantastic merged code, adapted on a Button, instead of a TextView.

    Substitute all your Buttons (not ImageButtons) with AutoResizeTextButtons and the same boring problem is fixed for them too.

    Here is the code. I just removed the imports.

    /**
     *            DO WHAT THE FUCK 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 THE FUCK YOU WANT TO PUBLIC LICENSE
     *   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
     * 
     *  0. You just DO WHAT THE FUCK YOU WANT TO.
     *  made better by onoelle
     *  adapted for button by beppi
     */
    
    /**
     * Text Button 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 AutoResizeTextButton extends Button {
    
        // 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(Button 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 AutoResizeTextButton(Context context) {
            this(context, null);
        }
    
        // Default constructor when inflating from XML file
        public AutoResizeTextButton(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        // Default constructor override
        public AutoResizeTextButton(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;
            }
    
            // 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);
                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
    //      textPaint.setTextSize(targetTextSize);
         // modified: setting text size via this.setTextSize (instead of textPaint.setTextSize(targetTextSize))
            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 originalPaint, 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)
            // Update the text paint object
              TextPaint paint = new TextPaint(originalPaint);
            paint.setTextSize(textSize);
            // Measure using a static layout
            StaticLayout layout = new StaticLayout(source, paint, width, Alignment.ALIGN_NORMAL, mSpacingMult, mSpacingAdd, true);
            return layout.getHeight();
        }
    
    }
    

    Usage:

    put a AutoResizeTextButton inside your xml in replace of a normal Button, without changing anything else. Inside the onCreate() put (for example):

        myButton = (AutoResizeTextButton)getView().findViewById(id.myButton);
        myButton.setMinTextSize(8f);
        myButton.resizeText();
    
    0 讨论(0)
  • 2020-11-21 06:09

    Here's a simple solution that uses TextView itself with a TextChangedListened added to it:

    expressionView = (TextView) findViewById(R.id.expressionView);
    expressionView.addTextChangedListener(textAutoResizeWatcher(expressionView, 25, 55));
    
    private TextWatcher textAutoResizeWatcher(final TextView view, final int MIN_SP, final int MAX_SP) {
        return new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
    
            @Override
            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
    
            @Override
            public void afterTextChanged(Editable editable) {
    
                final int widthLimitPixels = view.getWidth() - view.getPaddingRight() - view.getPaddingLeft();
                Paint paint = new Paint();
                float fontSizeSP = pixelsToSp(view.getTextSize());
                paint.setTextSize(spToPixels(fontSizeSP));
    
                String viewText = view.getText().toString();
    
                float widthPixels = paint.measureText(viewText);
    
                // Increase font size if necessary.
                if (widthPixels < widthLimitPixels){
                    while (widthPixels < widthLimitPixels && fontSizeSP <= MAX_SP){
                        ++fontSizeSP;
                        paint.setTextSize(spToPixels(fontSizeSP));
                        widthPixels = paint.measureText(viewText);
                    }
                    --fontSizeSP;
                }
                // Decrease font size if necessary.
                else {
                    while (widthPixels > widthLimitPixels || fontSizeSP > MAX_SP) {
                        if (fontSizeSP < MIN_SP) {
                            fontSizeSP = MIN_SP;
                            break;
                        }
                        --fontSizeSP;
                        paint.setTextSize(spToPixels(fontSizeSP));
                        widthPixels = paint.measureText(viewText);
                    }
                }
    
                view.setTextSize(fontSizeSP);
            }
        };
    }
    
    private float pixelsToSp(float px) {
        float scaledDensity = getResources().getDisplayMetrics().scaledDensity;
        return px/scaledDensity;
    }
    
    private float spToPixels(float sp) {
        float scaledDensity = getResources().getDisplayMetrics().scaledDensity;
        return sp * scaledDensity;
    }
    

    This approach will increase or decrease the font size as needed to fit the text, respecting the MIN_SP and MAX_SP bounds received as parameters.

    0 讨论(0)
  • 2020-11-21 06:09

    I wrote a blog post about this.

    I created a component called ResizableButton based on Kirill Grouchnikov's blog post about custom components used in the new android market app. I placed the src code here.

    On the other hand, mosabua read my post and told me he was going to open source his implementation which was faster than mine. I hope he release it soon enough :)

    0 讨论(0)
提交回复
热议问题