Kenny Wyland
Kenny Wyland

Reputation: 21870

Android TextView compat autosizing, only shrink font size?

For years, I've been desperate for Android TextViews to have the "shrink to fit" font size feature that iOS has, and now that autosizing has finally been added I discover that it isn't just "shrink to fit" but it's also "expand to fit" which is really, really not what I was hoping for.

I'm already setting my preferred font size on my TextView (not directly though, I use the built-in styles), the only time I need the font size automatically changed is if the text can't fit inside the given space and I need it to shrink to fit. Unfortunately, what I'm getting is the situation wherein I just need to set the text, let's say, "Kenny" and instead of leaving it at TextAppearance.Medium because it fits within the given space, it's blowing it up to something like 60sp font size. Utterly ridiculous.

1) Is there a direct way to tell the auto sizing to "only shrink"? I don't think so, but in case I missed it, I'd love for you to correct me.

2) Assuming there isn't an option for #1, I assume that my only other option is to set autoSizeMaxTextSize to the same value as my textSize... but I try to avoid every setting that value directly. I try to rely on the TextAppearance.Small, TextAppearance.Medium, and TextAppearance.Large when at all possible.

So, how can I reference the textSize value and use it to set the autoSizeMaxTextSize in my styles?

<style name="Text.Large" parent="TextAppearance.AppCompat.Large">
    <item name="autoSizeTextType">uniform</item>
    <item name="autoSizeMaxTextSize">?????</item>
</style>

Upvotes: 4

Views: 1351

Answers (3)

Delark
Delark

Reputation: 1323

Maybe by the time you needed this, the option was not fully supported, but by now it seems there is an option, maybe your issue was not defining an autoSizeMinTextSize dimension, which I will explain the reason for its necessity bellow.

I achieved this by combining 3 attributes:

    <item name="autoSizeTextType">uniform</item>

which defines the autoSizing to "true"

and then by defining a top and bottom boundary:

top boundary:

    <item name="autoSizeMaxTextSize">@dimen/text_small</item>

bottom boundary:

    <item name="autoSizeMinTextSize">@dimen/text_very_very_small</item>

The catch here is that the top boundary autoSizeMaxTextSize must be EQUAL to that of your text initial size (you already knew this):

    <item name="android:textSize">@dimen/text_small</item>

Now, the reason why a bottom boundary MUST be defined, is that the autoSizeMinTextSize initial default dimension is NOT DEFINED by the initial textSize (which is absolutely crazy if you ask me), as it was in my case that the bottom boundary was actually GREATER than that of the dimension specified by the initial android:textSize, which means that this bottom boundary was also GREATER than that defined by the top boundary dimension (because it was the same as textSize).

This means that if you are using <item name="autoSizeTextType">uniform</item> whatever textSize you define is absolutely pointless, without defining an initial bottom boundary first. In this case defining a textSize would only be useful for the preview at the design preview window of your IDE, but even worst than that, since once the programm compiles and initializes, the initial textSize immidiately resizes to the default bottom boundary (which would be GREATER than what you defined your as your initial textSize) - (BTW I am not saying that you should stop using textSize if you will be using autoSizeTextType with a top and bottom boundary, even though my reasoning is implying it, I don't know what would be the consequences of doing so, maybe it would not even compile).

The consequences of NOT DEFINING A BOTTOM BOUNDARY in this scenario will result in an error when computing autoSizeMaxTextSize as it cannot be GREATER THAN that of its bottom boundary (on the event that your autoSizeMaxTextSize happens to be smaller than it), and an exception will be thrown at compile time (the actual message is very deep in the stackTrace and this may be the reason why you did not saw it).

I hope this explanation was clear (and correct lol)...

Upvotes: 0

Kenny Wyland
Kenny Wyland

Reputation: 21870

If someone finds a more elegant solution, please let me know. Until then, I decided to solve it by subclasses AppCompatTextView and changing the max size in code. The barebones class I'm using is below, if there are any other methods I should override to make sure I'm updating the value whenever the text size changes, please let me know:

public class TextLabel extends AppCompatTextView {

    private void updateAutoSizeConfiguration() {

        int minsize = TextViewCompat.getAutoSizeMinTextSize(this);
        if (minsize < 0) {
            minsize = spToPx(12, getContext());
        }

        int textsize = (int)Math.floor(getTextSize());

        int granularity = TextViewCompat.getAutoSizeStepGranularity(this);
        if (granularity < 0) {
            granularity = spToPx(1, getContext());
        }

        TextViewCompat.setAutoSizeTextTypeUniformWithConfiguration(this, minsize,
                textsize, granularity, TypedValue.COMPLEX_UNIT_PX);

    }

    public TextLabel(Context context) {
        super(context);
        updateAutoSizeConfiguration();
    }

    public TextLabel(Context context, AttributeSet attrs) {
        super(context, attrs);
        updateAutoSizeConfiguration();
    }

    public TextLabel(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        updateAutoSizeConfiguration();
    }

    @Override
    public void setTextSize(int unit, float size) {
        super.setTextSize(unit, size);
        updateAutoSizeConfiguration();
    }

    @Override
    public void setTextAppearance(Context context, int resId) {
        super.setTextAppearance(context, resId);
        updateAutoSizeConfiguration();
    }


}

Upvotes: 4

SIMMORSAL
SIMMORSAL

Reputation: 1674

You could try setting wrap_content to width and height of the textView, and not setting autoResize in its xml, and later in java check to see if the textView's width equals its parent (plus possible margins and paddings you may have gave it) - meaning the text can only get smaller -, if yes set the autoResize to true

Upvotes: -1

Related Questions