89

文字太多?控件太小?试试 TextView 的新特性 Autosizing 吧!

 6 years ago
source link: https://juejin.im/post/5a56f70cf265da3e5468fa59
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.



160e3b5006615e2c~tplv-t2oaga2asx-zoom-in-crop-mark:4536:0:0:0.image

Hi,大家好,我是承香墨影!

Android 8.0 已经发布了有一阵子了,如果你有在关注它,你应该会知道它新增了一个对于 TextView 字体大小变动的新特性:Autosizing

本身这个新特性,如果只是在 Android 8.0 才有效,对于开发者而言,就显得有点鸡肋了,可能还需要一段时间才能普及使用。不过呢,在 Android Support v26 之上,也对 Autosizing 提供了兼容支持,最低可以支持到 Android Level 14。

这样,我们就有了研究的必要了,接下来本文就来讲解一下,Autosizing 属性,你在使用过程中的所有细节。

一、什么是 Autosizing?

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

简单来说,一个 100dp 长度的 TextView ,正常来说只能显示 10 个 10dp 的文字,而如果它的内容超出了 10 个字,以前的通用做法,是通过属性设置,让它在末尾显示 "…" 。而采用了 Autosizing 这个新特性,它的方案是将字体的尺寸缩小,例如缩小到 8dp,让 TextView 可以容纳下更多的文字,显示完全。而这一切,使用 Autosizing 我们只需要设置一些属性就可以做到,非常的简单。

160e3b5c431829d5~tplv-t2oaga2asx-zoom-in-crop-mark:4536:0:0:0.image

上面这个 Gif 应该可以很直观的描述 Autosizing 的特性,而它也反映出,触发 Autosizing 重新计算 TextSize 的时机有两个:

  1. TextView 中的文字增多到无法容纳的地步。
  2. TextView 本身的尺寸被放大或缩小了。

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

二、使用 Autosizing

2.1 Autosizing 不同使用方式

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

下面,我们先来了解一下它们之间的区别。

Autosizing 的带来的效果很简单,就是根据文字的内容,动态修改 TextSize ,而想要使用它,可以通过动态编码和静态的 layout-xml 布局属性的方式使用。

对于 Android 8.0 Api:

  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 了。此时,TextView 上并没有对应的方法能让我们调用,所以我们需要绕一层。

  1. 动态编码,使用 TextViewCompat 中提供的方法。
  2. layout-xml 布局属性,需要使用 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">

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

使用标准的 8.0 Api 的使用场景,在现阶段会非常的少,所以我们这里只是简单了解一下区别就好了,下面的文章内容会主要以 Support v26 的方式进行讲解。

2.2 Autosizing 基础

到这里,你应该对 Autosizing 有了基本的概念,知道它是干什么的。

那么,如果让你来设计一个这样的功能,你会想要做到哪些点?

  1. 有开关限制,只在我们需要的 TextView 上,才开启这个特性。
  2. 允许设置边界值,最大缩放和最小缩放。
  3. 能配置每次缩放的最小尺寸,例如:10sp 为粒度进行缩放。
  4. 能预设一些缩放的定位尺寸,例如预设一组尺寸,只让它在这个范围内的值中选一个。
  5. 方便的 Api ,可以通过 layout-xml 属性和动态编码的方式操作它。

嗯,功能上大概就是这些了,已经满足我的需要了。

如果你看了 Autosizing 的文档,你会发现,它全部都支持!

Autosizing TextView Doc:

https://developer.android.google.cn/guide/topics/ui/look-and-feel/autosizing-textview.html

2.3 Autosizing 开关

Autosizing 是直接作用在 TextView 上的,对于它的开启和关闭,我们可以直接操作 autoSizeTextType 属性。

对于动态编码,可以使用 TextViewCompat 的 setAutoSizeTextTypeWithDefaults() 方法,下面是它的方法签名。

setAutosizingType

参数中的 textView 是我们要操作的 TextView,而 autoSizeTextType,就是我们关心的 Autosizing 的开关属性了,它接受两个参数。

  • AUTO_SIZE_TEXT_TYPE_NONE:关闭自动调整功能。
  • AUTO_SIZE_TEXT_TYPE_UNIFORM:开启统一缩放碎片轴和垂直轴。

我们也可以通过 layout-xml 属性的方式,来设置 autoSizeTextType,因为是 Support ,所以使用的 app: 命名空间下的属性。

<?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">

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

</LinearLayout>

app:autoSizeTextType 同样接收两个参数 uniform 和 none,含义和上面代码中设置的参数一致。

2.4 操作 Autosizing 的粒度

粒度的含义其实就是 Autosizing 每次变动的最小单位,当然在设置粒度的同时,你还需要为其设置一个缩放的范围,最大值和最小值。

这样,在 Autosizing 生效的时候,它会在这个范围内,按照我们设定的粒度,去动态的调整文字的大小。

想要操作这些属性,动态编码的方式你需要调用 TextViewCompat 的 setAutoSizeTextTypeUniformWithConfiguration() 方法。

参数很直观,没什么好解释的,一个最小值、一个最大值、变动的粒度、前面设置的尺寸的单位。

我们可以通过 unit 参数,通过 TypedValue 来设置前面设置的几个参数的单位,例如:sp 、dp、px,都可以。

这里操作的参数,在 layout-xml 中,都提供了对应的属性可供我们使用。

<?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">

  <TextView
      android:layout_width="match_parent"
      android:layout_height="200dp"
      app:autoSizeTextType="uniform"
      app:autoSizeMinTextSize="12sp"
      app:autoSizeMaxTextSize="100sp"
      app:autoSizeStepGranularity="2sp" />

</LinearLayout>

下面我们举两个例子看看,就清晰了。

在默认情况下,如果你没有设置这三个属性,Autosizing 会根据当前 TextView 控件的大小,估算出一个最大值和最小值,并且将粒度设置为 1sp 。

可以看到,它设置的尺寸是跳动的,非常的不可控,我们很难知道下一次缩放,会将 文本 尺寸,设置成多大,所以才需要使用 粒度 的概念来限制它缩放的大小。

例如,现在我们修改上面的例子,将(minSize,maxSize),限制在 (10sp,80sp)之间,粒度(Granularity)设置为 10sp,此时再来看它的效果。

到这里可以看到,它每次放大或者缩小,粒度都是以 10sp 为基准。

所以,如果你需要使用 Autosizing ,强烈建议你使用 粒度 来控制它缩放的大小,让它在可控的范围内使用。需要注意的是,这里介绍的三个属性,一定要设置在一个合理的范围内,否则 TextView 会认为这是一个无效的设置,将它忽略掉。

2.5 预设尺寸范围

如果你按上一小节,介绍的属性,设置了 Autosizing 的粒度,就可以在这个范围内,根据我们设置的粒度进行缩放。通常,使用粒度来控制基本上可以达到我们的要求,但是如果对缩放有更精准的要求,例如:[10.15,40,60,100] 这样的缩放,使用粒度就达不到我们的要求了。

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

如果想要使用预设尺寸,动态编码的方式,你需要操作 TextViewCompat 的 setAutosizeTextTypeUniformWithPresetSizes() 方法。

预设尺寸可以接受一个尺寸数组,Autosizing 就会从我们设定的尺寸数组中,取一个尺寸进行设置。同时你可以为这些尺寸设置一个统一的尺寸单位。

如果想要在 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 中使用它。

<?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">

  <TextView
      android:layout_width="match_parent"
      android:layout_height="200dp"
      app:autoSizeTextType="uniform"
      app:autoSizePresetSizes="@array/autosize_text_sizes" />
</LinearLayout>

预设尺寸非常简单,这里就不再给运行效果了。

三、查缺补漏

到这里,我们就把 Autosizing 的基本使用细节,都讲解清楚了。但是,依然还有一些概念,是在文档上没有反应出来的,下面我们就来讲讲这些 “经验”。

3.1 TextView 必须限定尺寸

如果你想要使用 Autosizing,就必须对 TextView 这个控件,限定大小,不能使用 wrap_content 来作为限定符。

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

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

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

3.2 Autosizing 不能作用在 EditText 中

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

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

这个,你可以在 AppCompatTextViewAutoSizeHelper 这个类的 supportsAutoSizeText() 方法中找到答案,它是一个兼容类,用于向下兼容 Autosizing 特性。

not_support_edittext

这里可以看到,只要不是 AppCompatEditText 就返回 true,注释也说明了这一点。

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

3.3 预设尺寸不一定都命中

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

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

例如,我们使用预设尺寸,预设了一组[10sp,20sp,25sp,40sp],这样一组尺寸,其中,可能某个尺寸就永远不会被命中,例如 25sp。

这是因为,Autosizing 在起作用的时候,会去计算尺寸是否合适,假如到 20sp 之后,再减少文字,这个时候先获取 25sp ,通过计算发现 25sp 也放不下这些文字,就会直接跳到 40sp 这个尺寸上去。

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

3.4 和 singleLine 冲突

如果你想在 TextView 中,只显示一行文字,在之前你可以使用 android:singleLine 这个属性,对其标记。而如果你 同事使用 Autosizing,你会发现 Autosizing 就不再生效,它会在末尾显示 “…”。

所幸的是,android:singleLine 已经被标记为废弃,所以本身我们就不建议使用它,如果你想让 TextView 只显示单行文字,可以使用 android:maxLines="1" 属性,它是可以正常和 Autosizing 兼容的。

四、使用场景

Autosizing 说起来非常的简单,但是它能有哪些适用场景呢?简单说说我能想到的一些适用场景吧,大家可以开放思维。

4.1 限定条目的 UI

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

例如最近比较火的冲顶大会类 App,就是一个标准的选择题的 UI 布局。

cddh

我们可以在答案文字过多的时候,使用 Autosizing 将它缩小,就能正好放在这个既定大小的选项 UI 中。

4.2 多语言

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

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

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

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

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

本文到这里,就已经完成了 Autosizing 的所有细节,从基本使用到注意事项,应该算是解释的很详细了。

不知道你觉得 Autosizing 在实际使用中,还有什么使用场景?可以在评论中留言,分享给大家!

今天在承香墨影公众号的后台,回复『成长』。我会送你一些我整理的学习资料,包含:Android反编译、算法、设计模式、虚拟机、Linux、Kotlin、Python、爬虫、Web项目源码。

我另外还维护了一个技术交流群,有兴趣可以在公众号后台回复:"加群"

推荐阅读:

15fb3c95b1f1f263~tplv-t2oaga2asx-zoom-in-crop-mark:4536:0:0:0.image




About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK