Android -- Autosizing TextView 自动调整文字大小

一、前言

在 Android 8.0 (API level 26)以上(含26)中,TextView 增加了 字体大小变动 新的特性:Autosizing

这个特性不仅在 Android 8.0 (API level 26)上有效,同时对26以下的版本也做了适配,最低可支持到 Android 4.0 (API level 14) 。

二、什么是 Autosizeing?

Autosizeing 允许 TextView 根据其内部文本的显示大小,动态的调整 TextSize 属性值得大小。通过此设置,开发者可以很轻松的在具有动态内容的情况下,对不同的屏幕中,文本大小进行优化。

简单来说,一个 100dp 长度的 TextView ,正常来说只能显示 10 个 10dp 的文字,而如果它的内容超出了 10 个字,以前的通用做法,是通过属性设置,让它在末尾显示 “…” 。当然也可以测量 TextView 文本所占的宽度与 TextView 控件的宽度对比,动态改变TextView的字体大小,写起来即麻烦又耗性能。

而采用 Autosizeing 这个新特性,它的方案是将字体的尺寸缩小,例如缩小到 8dp,让 TextView 可以容纳下更多的文字,显示完全。

而这一切,只需使用 Autosizeing 设置一些属性即可做到。

Autosizeing 的核心思想:
为了让 文本 尽可能的完全显示在既定大小的 TextView 中,哪怕是修改它的文字大小。

三、使用 Autosizeing

3.1 Autosizeing 不同使用方式

前面也提到,使用 Autosizeing 其实是区分使用 Android Api Level 26(8.0) 和 使用 Support Library v26 (4.0以上)两种,他们的使用方式,会略微有点区别。

同时,通过 Autosizeing 属性动态修改文字的实现方式,也分为两种形式:动态编码静态的 layout-xml 布局属性

Android Api Level 26(8.0):

  1. 动态编码,是直接操作的 TextView 上的方法。
  2. layout-xml 布局属性,是使用的 android: 命名空间下的属性进行设置。
<?xml version="1.0" encoding="utf-8"?>
<TextView
    android:layout_width="match_parent"
    android:layout_height="200dp"
    android:autoSizeTextType="uniform" />

而在低于 Android 8.0 的设备上,只能使用 Support v26 了。

Support Library v26 (4.0以上):

  1. 动态编码,在 xml 中用 AppCompatTextView 代替 TextView,或者当前 Activity 继承 AppCompatActivity 使用 TextViewCompat 中提供的方法
  2. layout-xml 布局属性,使用 AppCompatTextView 代替 TextView,同时用 app: 命名空间下的属性,记住要添加:xmlns:app=“http://schemas.android.com/apk/res-auto” 这个命名空间。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

  <AppCompatTextView
      android:layout_width="match_parent"
      android:layout_height="200dp"
      app:autoSizeTextType="uniform" />

</LinearLayout>

用标准 8.0 Api 设置 Autosizing 属性,现阶段用的非常少,因为市面上的手机大多还是8.0以下的版本,这里只是简单了解一下区别就好了,在代码里还是主要采用 Support v26 方式 设置 Autosizing。

3.2 Autosizeing 在 Support v26 基础使用

3.2.1 xml 方式

使用 AppCompatTextView 代替 TextView,同时使用 app: 命名空间下的属性,记住要添加:xmlns:app=“http://schemas.android.com/apk/res-auto” 这个命名空间。

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

    <android.support.v7.widget.AppCompatTextView
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:background="@drawable/shape_bg_008577"
        android:gravity="center_vertical"
        android:text="这是标题,该标题的名字比较长,产品要求不换行全部显示出来;这是标题,该标题的名字比较长,产品要求不换行全部显示出来"
        android:textSize="18sp"
        app:autoSizeTextType="uniform"
        app:autoSizeMaxTextSize="18sp"
        app:autoSizeMinTextSize="10sp"
        app:autoSizeStepGranularity="1sp" />
</LinearLayout>

在这里插入图片描述
Autosizeing 多了如下属性:

  • autoSizeTextType:开关限制,设置 TextView 是否支持自动改变字体大小
    这里有两个属性:
    none:关闭自动调整功能
    uniform:开启统一缩放碎片轴和垂直轴
  • autoSizeMaxTextSize:最大缩放值,例如设置为18sp,表示文字最多只能放大到18sp
  • autoSizeMinTextSize:最小缩放值,例如设置为10sp,表示文字最多只能缩小到10sp
  • autoSizeStepGranularity:缩放粒度,即每次字体大小变化的最小数值,例如设置为1sp,表示每次缩小或放大的值为1sp

肯定很多人说 “为什么自己写的时候不用 AppCompatTextView 而使用 TextView 也能兼容8.0以下设备呢?”
那是因为你当前的xml文件对应的Activity继承的是AppCompatActivity,如果继承的是Activity或FragmentActivity是不能达到兼容的。
这一点其实官方文档 Autosizing TextViews 也没有说清楚,导致很多人误解了,各位可以自己验证下。

3.2.2 动态编码方式

使用 TextViewCompat 中提供的方法

如果要兼容8.0以下设备,要么在 xml 中用 AppCompatTextView 代替 TextView,要么当前 Activity 继承 AppCompatActivity。

public class MainActivity extends AppCompatActivity{

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

        TextView textView = findViewById(R.id.tv_1);
        TextViewCompat.setAutoSizeTextTypeWithDefaults(textView,TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM);
        TextViewCompat.setAutoSizeTextTypeUniformWithConfiguration(textView, 10,
                18, 1, TypedValue.COMPLEX_UNIT_SP);
    }
}
  • setAutoSizeTextTypeWithDefaults() :开关限制,设置 TextView 是否支持自动改变字体大小
    它接受两个参数:
    AUTO_SIZE_TEXT_TYPE_NONE:关闭自动调整功能
    AUTO_SIZE_TEXT_TYPE_UNIFORM:开启统一缩放碎片轴和垂直轴

  • setAutoSizeTextTypeUniformWithConfiguration():
    参数1:需要动态改变字体大小的TextView
    参数2:最小字体大小
    参数3:最大字体大小
    参数4:缩放粒度
    参数5:参数2、3、4的单位,例如sp 、dp、px等

3.3 Autosizeing 在 Android Api Level 26(8.0)及以上的基础使用

3.3.1 xml 方式

使用的 android: 命名空间下的属性进行设置

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center">

   <TextView
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:background="@drawable/shape_bg_008577"
        android:gravity="center_vertical"
        android:text="这是标题,该标题的名字比较长,产品要求不换行全部显示出来;这是标题,该标题的名字比较长,产品要求不换行全部显示出来"
        android:textSize="18sp"
        android:autoSizeTextType="uniform"
        android:autoSizeMaxTextSize="18sp"
        android:autoSizeMinTextSize="10sp"
        android:autoSizeStepGranularity="1sp"/>
</LinearLayout>

Autosizeing 属性说明同 3.2.1

3.3.2 动态编码方式

直接操作的 TextView 上的方法

public class MainActivity extends Activity {

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

        TextView textView = findViewById(R.id.tv_1);
        textView.setAutoSizeTextTypeWithDefaults(TextView.AUTO_SIZE_TEXT_TYPE_UNIFORM);
        textView.setAutoSizeTextTypeUniformWithConfiguration(10, 18, 
                1, TypedValue.COMPLEX_UNIT_SP);
    }
}

Autosizeing 方法说明同 3.2.2

3.4 小结

Autosizing TextViews 是 Android 8.0 新增的特性,可以用来动态改变 TextView 字体大小。如果要兼容 8.0 以下设备,则需要满足以下2个条件中的其中一个

  • 在 xml 中用 AppCompatTextView 代替 TextView,并且上面几个属性的命名空间用 app 命名空间。
  • 当前 Activity 继承 AppCompatActivity,而不是 Activity 或 FragmentActivity。

四、预设尺寸范围

按照上一节的内容,设置了 Autosizeing 的粒度,就可以在这个范围内,根据我们设置的粒度进行缩放。

通常,使用粒度来控制基本上可以达到我们的要求,但是如果对缩放有更精准的要求,例如:[10.15,40,60,100] 这样的缩放,使用粒度就达不到我们的要求了。

针对这样的操作,Autosizeing 也提供了对应的属性来设置,那就是 预设尺寸(Preset Size)

同样,设置预设尺寸范围也分 Android Api Level 26(8.0)和 Support Library v26 (4.0以上) 两种,因为 Android Api Level 26(8.0)基本不使用,所以这里主要针对 Support Library v26 进行讲解。

预设尺寸范围 也是分两种形式实现:动态编码 和 xml属性。

4.1 xml属性

如果想要在 layout-xml 使用属性的形式使用预设尺寸,你首先需要一个 array 的资源,然后通过 autoSizePresetSizes 属性进行设置即可。

array 资源的格式:

<resources>
    <array name="autosize_text_sizes">
        <item>10sp</item>
        <item>12sp</item>
        <item>20sp</item>
        <item>40sp</item>
        <item>100sp</item>
    </array>
</resources>

定义好 array 的尺寸资源之后,就可以在 layout-xml 中使用了。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:background="@drawable/shape_bg_008577"
        android:gravity="center_vertical"
        android:text="这是标题,该标题的名字比较长,产品要求不换行全部显示出来;这是标题,该标题的名字比较长,产品要求不换行全部显示出来"
        android:textSize="18sp"
        app:autoSizeTextType="uniform"
        app:autoSizePresetSizes="@array/autosize_text_sizes"/>
        
</LinearLayout>

注:
在 xml 中,这里使用了 TextView,则当前 Activity 必须继承 AppCompatActivity,而不是 Activity 或 FragmentActivity,否则 Autosizeing 特性不生效。

4.2 动态编码

动态编码的方式,需要操作 TextViewCompat 的 setAutoSizeTextTypeUniformWithPresetSizes() 方法。该方法接受一个尺寸数组,Autosizeing 就会从我们设定的尺寸数组中,取一个尺寸进行设置。

xml 布局

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:background="@drawable/shape_bg_008577"
        android:gravity="center_vertical"
        android:text="这是标题,该标题的名字比较长,产品要求不换行全部显示出来;这是标题,该标题的名字比较长,产品要求不换行全部显示出来"
        android:textSize="18sp" />
        
</LinearLayout>

array 资源的格式:

<resources>
    <array name="autosize_text_sizes">
        <item>10</item>
        <item>12</item>
        <item>20</item>
        <item>40</item>
        <item>100</item>
    </array>
</resources>
public class MainActivity extends AppCompatActivity {

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

        TextView textView2 = findViewById(R.id.tv_2);
        TextViewCompat.setAutoSizeTextTypeWithDefaults(textView2, TextView.AUTO_SIZE_TEXT_TYPE_UNIFORM);
        TextViewCompat.setAutoSizeTextTypeUniformWithPresetSizes(textView2, getResources().getIntArray(R.array.autosize_text_sizes),
                TypedValue.COMPLEX_UNIT_SP);
    }
}

五、查缺补漏

5.1 TextView 必须限定尺寸

使用 Autosizeing,必须对 TextView 这个控件限定大小,不能使用 wrap_content 来作为限定符.。

用官方文档话来说,使用 wrap_content 可能出现不可预料的效果。
其实这也非常好理解,如果 TextView 的尺寸不是固定的,那就不存在 TextView 重新计算尺寸的依据了,同比放大 TextView 就可以达到容纳文字的效果了。

我在实际使用过程中发现,如果不固定 TextView 的尺寸, 它会阻止放大效果。
例如一个 TextView 中使用了 Autosizeing,一直增加文本内容,是可以正常缩小的,但是当你删除文本的时候,它并不会随之放大文字尺寸。

不确定还有没有其它的问题,这里建议按照官方文档的建议来操作,限定 TextView 的尺寸。

5.2 Autosizeing 不能作用在 EditText 中

虽然通常作用在 TextView 上的新属性,对于同样用于显示文本的控件,例如:Button、EditText 等,都是同样适用的。

但是 Autosizeing 就是这么特殊,它只对单纯只能显示 文本 的控件有效,例如 Button,而对于 EditText 这种可以输入 文本 的控件,是无效的

这个,你可以在 AppCompatTextViewAutoSizeHelper 这个类的 supportsAutoSizeText() 方法中找到答案,它是一个兼容类,用于向下兼容 Autosizeing 特性。
在这里插入图片描述
这里可以看到,只要不是 AppCompatEditText 就返回 true。

暂时没有想到这样设计的原因,可能是因为输入文本的控件,本身长度就是在经常变化的,是一个极端不可控的情况,所以应该为输入的控件,限定一个固定的尺寸。

5.3 预设尺寸不一定都命中

如果想要控制文字的缩放尺寸为限定的范围内,例如使用 粒度 限定它在一个 10sp 的精度下缩放;或者使用预设尺寸,限定一些尺寸,让它只能使用我们预定的一些尺寸。

但是这些,并不是一定的。

例如,我们使用预设尺寸,预设了一组[10sp,20sp,25sp,40sp],这样一组尺寸,其中,可能某个尺寸就永远不会被命中,例如 25sp。
这是因为,Autosizeing 在起作用的时候,会去计算尺寸是否合适,假如到 20sp 之后,再减少文字,这个时候先获取 25sp ,通过计算发现 25sp 也放不下这些文字,就会直接跳到 40sp 这个尺寸上去。

所以,并不是我们设定的尺寸,它就是以线性的方式去获取尺寸。

六、使用场景

Autosizeing 说起来非常的简单,但是它能有哪些适用场景呢?简单说说我能想到的一些适用场景吧。

6.1 限定条目的 UI

这个,其实很常见,例如一些选择题的 UI,当你有多个需要选择答案的 UI ,并列的显示出来。如果它们的文字长度是可变的(通常都是可变的),你除了放大某一行的高度之外,现在还可以使用 Autosizeing 来控制它的大小。

例如最近比较火的冲顶大会类 App,就是一个标准的选择题的 UI 布局。
在这里插入图片描述
可以在答案文字过多的时候,使用 Autosizeing 将它缩小,就能正好放在这个既定大小的选项 UI 中。

6.2 多语言

Autosizeing 在 App 的多语言适配中,也可以大放异彩。

首先你要考虑到,当你想让 App 适配多语言的话,一个很严重的问题,就是不同的语言,描述同一个词的时候,长度是不一致的。

例如中文下简单的一句:我是 Android 开发者,翻译成不同的语言,长度是不一致的:

  • 英语:I am an Android developer
  • 阿拉伯语:أنا مطور الروبوت
  • 意大利语:Sono uno sviluppatore Android
  • 德语:Ich bin ein Android-Entwickler
  • 法语:Je suis un développeur Android

在这样的情况下,如果有 Autosizeing 就非常的好解决这个问题了。

6.3 政府文件

政府文件,实现无纸化办公,要求将纸质文件一页展示在 pad 上,页面不能滑动。当页面内容很多时,因为页面不能滑动,所以可以使用 Autosizeing 属性将字体缩小。

由于是政府类文件,涉及机密性,这里就不贴图了。

七、总结

有兴趣的话,还可以继续看下 Autosizing 到底是如何实现的? | 源码分析

推荐文章:
一种非常好用的Android屏幕适配

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值