0

How to calculate font size for TextView to fit in bounds defined by me with multiline text?

I have a TextView inside a RelativeLayout. At runtime will be known the screen size for App, based on that.

    Rect boundsTv = TextPositions.getTextViewBounds(1, mainActivity.mWidthPx, mainActivity.mHeightPx);
    Log.e("SlideLoader", "paint.boundsTv() left, right, top, bottom :"+boundsTv.flattenToString());//paint.boundsTv() left, right, top, bottom :163 363 1767 749

    RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) tvText.getLayoutParams();
    params.width = boundsTv.width();// right - left;
    params.height = boundsTv.height();// bottom - top;
    params.leftMargin = boundsTv.left; // Your X coordinate
    params.topMargin = boundsTv.top; // Your Y coordinate
    tvText.setLayoutParams(params);
    tvText.invalidate();

And here it comes the problem. On some devices the font is to small and it fills less than the half of the TextView on other devices is so big as doesn't fit not even the half.

The output code is from a device, where the font size to big by far. I have checked a few methods, probably will suggest those 2 methods, both fail:

    // left, top - right, bottom        
    TextPaint paint = tvText.getPaint();        
    paint.getTextBounds(text, 0, text.length()-1, boundsTv);        
    Log.e("SlideLoader", "paint.getTextBounds() left, right, top, bottom :"+boundsTv.flattenToString());//paint.getTextBounds() left, right, top, bottom :2 -33 16049 9, how to interpret this values?!

    float width = paint.measureText(text);// for single line

2 Answers2

1

If you do not mind defining the text size based on the screensize using foldernames for instance:

res/values-sw600dp/dimens.xml

content of dimens.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <dimen name="text_size">20sp</dimen>
</resources>

res/values-w1024dp

content of dimens.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <dimen name="text_size">35sp</dimen>
</resources>

Now you can use the screen-size specific text size directly inside your layout:

I chose to define a style for my text res/values/styles.xml

<style name="Text">
    <item name="android:padding">5dp</item>
    <item name="android:paddingLeft">10dp</item>
    <item name="android:paddingRight">10dp</item>
    <item name="android:shadowColor">@color/Grey</item>
    <item name="android:shadowDx">1</item>
    <item name="android:shadowDy">1</item>
    <item name="android:shadowRadius">1</item>
    <item name="android:textColor">@color/Black</item>
    <!-- Here the dimen/text_size is used -->
    <item name="android:textSize">@dimen/text_size</item>
    <item name="android:textStyle">bold</item>
    <item name="android:typeface">monospace</item>
    <item name="android:ellipsize">none</item>
    <item name="android:gravity">left</item>
    <item name="android:maxLines">1</item>
    <item name="android:background">@color/Transparent</item>
</style>

And because I dynamically add TextViews to my layout, I define a simple entry.xml layout:

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/Text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:singleLine="false" />

Thats it. When I inflate an entry.xml layout, the text size will reflect the size of the display.

You can read more about the technique here: http://wptrafficanalyzer.in/blog/different-layouts-for-different-screen-sizes-in-android/

cYrixmorten
  • 7,110
  • 3
  • 25
  • 33
  • pff... I deed to define around 40 folders and switch, this is working only theoretically and in Google docs, not in practice ;) +1 for effort, but for sure I wouldn't recommend to use in commercial environment, for homework is good also –  Nov 02 '13 at 15:28
  • Hehe fair enough :) but if you do not need to have different size for each and every display size, then this is a pretty good solution imo. Would think that you would be pretty good on the way with, say, 3 - 5 sizes. Perhaps this suits your needs: https://github.com/danclarke/AutoResizeTextView – cYrixmorten Nov 02 '13 at 16:16
  • I have added 4 of my devices, I bet if I go to workplace and bring 2 devices they will have other metrics :) –  Nov 02 '13 at 16:34
  • True. But if the goal was simply to e.g. show some text on a single line, then all devices with width 600 or below could have one defined textsize, 1024 or below a second textsize and 1920 a third. This way the text would match the constraint but, yes, look a bit different on the different devices. – cYrixmorten Nov 02 '13 at 16:46
0

Here I have posted the solution. I hope somebody will give a better answer than this.

Can be the following result too: 1 and a half row...

enter image description here

Some test devices data: I am "happy" to see I have 2 device with the same screen size or screen size ratio or android version or something? :)

// Lenovo phone: DisplayMetrics{density=1.5, width=800, height=480, scaledDensity=1.6500001, xdpi=240.0, ydpi=240.0}        width/height: 1.666
// Lenovo tablet: DisplayMetrics{density=1.0, width=1024, height=552, scaledDensity=1.0, xdpi=160.0, ydpi=160.0}            width/height: 1.85
// Samsung tablet: DisplayMetrics{density=1.0, width=1280, height=752, scaledDensity=1.0, xdpi=149.82489, ydpi=150.51852}   width/height: 1.7
// THL phone:  DisplayMetrics{density=3.0, width=1920, height=1080, scaledDensity=3.3000002, xdpi=480.0, ydpi=480.0}        width/height: 1.77

Oh, because of different width/heigh ratio the text will not end at the same word. It can happen to have only 1 row, while at other devices needed 2 row...

        // get the text size
        int textSize = 50;
        int maxHeight = boundsTv.height();
        while (getHeightOfMultiLineText(text, textSize, params.width) > maxHeight) {
            textSize--;
        }
        float scaleFactor = mainActivity.getResources().getDisplayMetrics().density;
        float fuckingMagic = 1.8f; // large screens need a lower number, smaller screens a higher number.
        // Samsung tablet 1.4
        // THL phone 1.6
        // Lenovo phone: 2.2
        // Lenovo table 1.8

        float size = textSize / (fuckingMagic * scaleFactor);
Community
  • 1
  • 1
  • While I applaud your creative and descriptive variable names, I'm still curious as to the magic numbers. What are they and where did you get them? – SMBiggs Jul 24 '18 at 21:56