Android TextView 兼容自动调整大小,只缩小字体大小?

Android TextView compat autosizing, only shrink font size?

多年来,我一直渴望 Android TextViews 具有 iOS 具有的 "shrink to fit" 字体大小功能,现在终于添加了自动调整大小功能,我发现它不仅是 "shrink to fit",而且也是 "expand to fit",这真的不是我所希望的。

我已经在我的 TextView 上设置了我喜欢的字体大小(虽然不是直接设置,但我使用的是内置样式),我唯一需要自动更改字体大小的时间是文本放不下的时候给定的 space 并且我需要将其缩小以适合。不幸的是,我得到的是我只需要设置文本的情况,比方说,"Kenny" 而不是将它留在 TextAppearance.Medium,因为它适合给定的 space,它正在将其放大到 60sp 字体大小。太可笑了。

1) 是否有直接的方法将自动调整大小设置为 "only shrink"?我不这么认为,但如果我错过了,我希望你能纠正我。

2) 假设 #1 没有选项,我假设我唯一的其他选择是将 autoSizeMaxTextSize 设置为与我的 textSize 相同的值...但我尝试避免直接设置该值。我尽量依赖 TextAppearance.SmallTextAppearance.MediumTextAppearance.Large

那么,如何引用 textSize 值并使用它来设置我的样式中的 autoSizeMaxTextSize

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

您可以尝试将 wrap_content 设置为 textView 的宽度和高度,而不是在其 xml 中设置 autoResize,然后在 java 中设置检查 textView 的宽度是否等于其父级(加上您可能给它的可能的边距和填充) - 这意味着文本只能变小 - 如果是,请将 autoResize 设置为 true

如果有人找到更优雅的解决方案,请告诉我。在那之前,我决定通过 subclasses AppCompatTextView 并更改代码中的最大大小来解决它。下面是我正在使用的准系统class,如果有任何其他方法我应该覆盖以确保我在文本大小发生变化时更新值,请告诉我:

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();
    }


}

也许当你需要这个的时候,这个选项还没有得到完全支持,但现在似乎有一个选项,也许你的问题没有定义 autoSizeMinTextSize 维度,我会解释原因其必要性如下。

我通过组合 3 个属性实现了这一点:

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

将 autoSizing 定义为“true”

然后通过定义顶部和底部边界:

上边界:

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

底部边界:

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

这里的问题是顶部边界 autoSizeMaxTextSize 必须等于您的文本初始大小(您已经知道这一点):

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

现在,必须定义底部边界的原因是 autoSizeMinTextSize 初始默认尺寸不是由初始 textSize 定义的(如果你问我,这绝对是疯狂的),因为它在我的情况是底部边界实际上大于初始 android:textSize 指定的尺寸,这意味着该底部边界也大于顶部边界尺寸定义的尺寸(因为它与 textSize 相同) .

这意味着,如果您使用 <item name="autoSizeTextType">uniform</item>,那么您定义的任何 textSize 都是毫无意义的,而无需先定义初始底部边界。在这种情况下,定义 textSize 仅对 IDE 的设计预览 window 中的预览有用,但更糟糕的是,一旦程序编译并初始化,初始 textSize 会立即调整为默认底部边界(这将比您定义的初始 textSize 更大)-(顺便说一句,如果您将使用具有顶部和底部边界的 autoSizeTextType,我并不是说您应该停止使用 textSize,即使我的推理是暗示它,我不知道这样做会有什么后果,也许它甚至无法编译)。

在这种情况下不定义底部边界的后果将在计算 autoSizeMaxTextSize 时导致错误,因为它不能大于其底部边界(如果您的 autoSizeMaxTextSize 恰好更小)比它),编译时会抛出异常(实际消息在stackTrace中很深,这可能是你没看到的原因)。

我希望这个解释是清楚的(而且是正确的,哈哈)...