简体   繁体   中英

How to autoresize textsize in TextView to fit available space vertically and horizontally?

I would like to know if there is an easy way to autoresize the text in a textview to fit its available space (vertically and horizontally) without a linebreak or overlapping. My goal is to make the text as large as possible on any device. This should work in landscape and in portrait.

I have it almost working (only in portrait-mode), it looks like this:

myapp in portrait

simplified javacode:

public class MainActivity extends Activity{

private TextView        mTV_veryTop;
private TextView        mTV_top;
private TextView        mTV_middle;
private TextView        mTV_bottomLeft;
private TextView        mTV_bottomRight;
private Boolean         resized = false;

@Override
protected void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.cockpit_layout);

    mTV_veryTop = (TextView) findViewById(R.id.veryTop);
    mTV_top = (TextView) findViewById(R.id.top);
    mTV_middle = (TextView) findViewById(R.id.middle);
    mTV_bottomLeft = (TextView) findViewById(R.id.bottomLeft);
    mTV_bottomRight = (TextView) findViewById(R.id.bottomRight);

}

@Override
public void onWindowFocusChanged(boolean hasFocus)
{
    // TODO Auto-generated method stub
    super.onWindowFocusChanged(hasFocus);

    if (resized == false)
    {

        shrinkTextToFit(mTV_veryTop.getWidth(), mTV_veryTop, 300, 10);  
        shrinkTextToFit(mTV_top.getWidth(), mTV_top, 300, 10);  
        shrinkTextToFit(mTV_middle.getWidth(), mTV_middle, 300, 10);    
        shrinkTextToFit(mTV_bottomLeft.getWidth(),mTV_bottomLeft, 300, 10); 
        shrinkTextToFit(mTV_bottomRight.getWidth(), mTV_bottomRight, 300, 10);  

        resized = true;
    }

}

public static void shrinkTextToFit(float availableWidth, TextView textView, float startingTextSize, float minimumTextSize)
{

    CharSequence text = textView.getText();
    float textSize = startingTextSize;
    textView.setTextSize(startingTextSize);

    while (text != (TextUtils.ellipsize(text, textView.getPaint(), availableWidth, TextUtils.TruncateAt.END)))
    {
        textSize -= 1;
        if (textSize < minimumTextSize)
        {
            break;
        }
        else
        {
            textView.setTextSize(textSize);
        }
    }
}

}

layout-file:

 <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#000000"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/veryTop"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_weight="1"
        android:gravity="center"
        android:text="very top"
        android:textColor="@android:color/white"
        android:textSize="500dp" >
    </TextView>

     <TextView
        android:id="@+id/top"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_weight="5"
        android:gravity="center"
        android:text="top"
        android:textColor="@android:color/white"
        android:textSize="500dp" />

    <TextView
        android:id="@+id/middle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_weight="1"
        android:gravity="middle"
        android:text="N.A."
        android:textColor="@android:color/white"
        android:textSize="500dp" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="3"
        android:orientation="horizontal" >



        <TextView
            android:id="@+id/bottomLeft"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_weight="5"
            android:gravity="center"
         android:text="bottom left"
            android:textColor="@android:color/white"
            android:textSize="500dp" >
        </TextView>

        <TextView
            android:id="@+id/bottomRight"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_weight="5"
            android:gravity="center"
            android:text="bottom right"
            android:textColor="@android:color/white"
            android:textSize="500dp" >
        </TextView>
    </LinearLayout>

</LinearLayout>

.......but this is not working in landscape and not even perfect in portrait (some pixels are missing at the bottom)

i have read several post like these:

How to scale/resize text to fit a TextView?

Auto Scale TextView Text to Fit within Bounds

Scale text in a view to fit?

......and tried different methods/textView classes, but there was always something that didnt work in case of my needs.

Any help?

Update:

I dont know why my question is downvoted, since it is not easy to find a class/textview (under the "million libraries") which does all i asked for. Here is the class which does all i need (might help someone):

public class AutoResizeTextview extends TextView
{
    private interface SizeTester
    {
        /**
         * 
         * @param suggestedSize
         *            Size of text to be tested
         * @param availableSpace
         *            available space in which text must fit
         * @return an integer < 0 if after applying {@code suggestedSize} to
         *         text, it takes less space than {@code availableSpace}, > 0
         *         otherwise
         */
        public int onTestSize(int suggestedSize, RectF availableSpace);
    }

    private RectF               mTextRect           = new RectF();

    private RectF               mAvailableSpaceRect;

    private SparseIntArray      mTextCachedSizes;

    private TextPaint           mPaint;

    private float               mMaxTextSize;

    private float               mSpacingMult        = 1.0f;

    private float               mSpacingAdd         = 0.0f;

    private float               mMinTextSize        = 3;

    private int                 mWidthLimit;

    private static final int    NO_LINE_LIMIT       = -1;
    private int                 mMaxLines;

    private boolean             mEnableSizeCache    = true;
    private boolean             mInitiallized;

    public AutoResizeTextview(Context context)
    {
        super(context);
        initialize();
    }

    public AutoResizeTextview(Context context, AttributeSet attrs)
    {
        super(context, attrs);
        initialize();
    }

    public AutoResizeTextview(Context context, AttributeSet attrs, int defStyle)
    {
        super(context, attrs, defStyle);
        initialize();
    }

    private void initialize()
    {
        mPaint = new TextPaint(getPaint());
        mMaxTextSize = getTextSize();
        mAvailableSpaceRect = new RectF();
        mTextCachedSizes = new SparseIntArray();
        if (mMaxLines == 0)
        {
            // no value was assigned during construction
            mMaxLines = NO_LINE_LIMIT;
        }
        mInitiallized = true;
    }

    @Override
    public void setText(final CharSequence text, BufferType type)
    {
        super.setText(text, type);
        adjustTextSize(text.toString());
    }

    @Override
    public void setTextSize(float size)
    {
        mMaxTextSize = size;
        mTextCachedSizes.clear();
        adjustTextSize(getText().toString());
    }

    @Override
    public void setMaxLines(int maxlines)
    {
        super.setMaxLines(maxlines);
        mMaxLines = maxlines;
        reAdjust();
    }

    public int getMaxLines()
    {
        return mMaxLines;
    }

    @Override
    public void setSingleLine()
    {
        super.setSingleLine();
        mMaxLines = 1;
        reAdjust();
    }

    @Override
    public void setSingleLine(boolean singleLine)
    {
        super.setSingleLine(singleLine);
        if (singleLine)
        {
            mMaxLines = 1;
        }
        else
        {
            mMaxLines = NO_LINE_LIMIT;
        }
        reAdjust();
    }

    @Override
    public void setLines(int lines)
    {
        super.setLines(lines);
        mMaxLines = lines;
        reAdjust();
    }

    @Override
    public void setTextSize(int unit, float size)
    {
        Context c = getContext();
        Resources r;

        if (c == null)
            r = Resources.getSystem();
        else
            r = c.getResources();
        mMaxTextSize = TypedValue.applyDimension(unit, size, r.getDisplayMetrics());
        mTextCachedSizes.clear();
        adjustTextSize(getText().toString());
    }

    @Override
    public void setLineSpacing(float add, float mult)
    {
        super.setLineSpacing(add, mult);
        mSpacingMult = mult;
        mSpacingAdd = add;
    }

    /**
     * Set the lower text size limit and invalidate the view
     * 
     * @param minTextSize
     */
    public void setMinTextSize(float minTextSize)
    {
        mMinTextSize = minTextSize;
        reAdjust();
    }

    private void reAdjust()
    {
        adjustTextSize(getText().toString());
    }

    private void adjustTextSize(String string)
    {
        if (!mInitiallized)
        {
            return;
        }
        int startSize = (int) mMinTextSize;
        int heightLimit = getMeasuredHeight() - getCompoundPaddingBottom() - getCompoundPaddingTop();
        mWidthLimit = getMeasuredWidth() - getCompoundPaddingLeft() - getCompoundPaddingRight();
        mAvailableSpaceRect.right = mWidthLimit;
        mAvailableSpaceRect.bottom = heightLimit;
        super.setTextSize(TypedValue.COMPLEX_UNIT_PX, efficientTextSizeSearch(startSize, (int) mMaxTextSize, mSizeTester, mAvailableSpaceRect));
    }

    private final SizeTester    mSizeTester = new SizeTester()
    {
        @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
        @Override
        public int onTestSize(int suggestedSize, RectF availableSPace)
        {
            mPaint.setTextSize(suggestedSize);
            String text = getText().toString();
            boolean singleline = getMaxLines() == 1;
            // if (singleline) {    //my comment-out
            // mTextRect.bottom = mPaint.getFontSpacing();  //my comment-out
            // mTextRect.right = mPaint.measureText(text);  //my comment-out
            // } else { //my comment-out
            StaticLayout layout = new StaticLayout(text, mPaint, mWidthLimit, Alignment.ALIGN_NORMAL, mSpacingMult,
                    mSpacingAdd, true);
            // return early if we have more lines
            if (getMaxLines() != NO_LINE_LIMIT && layout.getLineCount() > getMaxLines())
            {
                return 1;
            }
            mTextRect.bottom = layout.getHeight();
            int maxWidth = -1;
            for (int i = 0; i < layout.getLineCount(); i++)
            {
                if (maxWidth < layout.getLineWidth(i))
                {
                    maxWidth = (int) layout.getLineWidth(i);
                }
            }
            mTextRect.right = maxWidth;
            // }    //my comment-out

            mTextRect.offsetTo(0, 0);
            if (availableSPace.contains(mTextRect))
            {
                // may be too small, don't worry we will find the best match
                return -1;
            }
            else
            {
                // too big
                return 1;
            }
        }
    };

    /**
     * Enables or disables size caching, enabling it will improve performance
     * where you are animating a value inside TextView. This stores the font
     * size against getText().length() Be careful though while enabling it as 0
     * takes more space than 1 on some fonts and so on.
     * 
     * @param enable
     *            enable font size caching
     */
    public void enableSizeCache(boolean enable)
    {
        mEnableSizeCache = enable;
        mTextCachedSizes.clear();
        adjustTextSize(getText().toString());
    }

    private int efficientTextSizeSearch(int start, int end, SizeTester sizeTester, RectF availableSpace)
    {
        if (!mEnableSizeCache)
        {
            return binarySearch(start, end, sizeTester, availableSpace);
        }
        String text = getText().toString();
        int key = text == null ? 0 : text.length();
        int size = mTextCachedSizes.get(key);
        if (size != 0)
        {
            return size;
        }
        size = binarySearch(start, end, sizeTester, availableSpace);
        mTextCachedSizes.put(key, size);
        return size;
    }

    private static int binarySearch(int start, int end, SizeTester sizeTester, RectF availableSpace)
    {
        int lastBest = start;
        int lo = start;
        int hi = end - 1;
        int mid = 0;
        while (lo <= hi)
        {
            mid = (lo + hi) >>> 1;
            int midValCmp = sizeTester.onTestSize(mid, availableSpace);
            if (midValCmp < 0)
            {
                lastBest = lo;
                lo = mid + 1;
            }
            else
                if (midValCmp > 0)
                {
                    hi = mid - 1;
                    lastBest = hi;
                }
                else
                {
                    return mid;
                }
        }
        // make sure to return last best
        // this is what should always be returned
        return lastBest;

    }

    @Override
    protected void onTextChanged(final CharSequence text, final int start, final int before, final int after)
    {
        super.onTextChanged(text, start, before, after);
        reAdjust();
    }

    @Override
    protected void onSizeChanged(int width, int height, int oldwidth, int oldheight)
    {
        mTextCachedSizes.clear();
        super.onSizeChanged(width, height, oldwidth, oldheight);
        if (width != oldwidth || height != oldheight)
        {
            reAdjust();
        }
    }
}

There are already about a million libraries that offer this. Look into their implementation, or just use it directly.

https://github.com/grantland/android-autofittextview

https://bitbucket.org/ankri/autoscaletextview/src

5 seconds on google gave me those.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM