头图@Billy Kwok
一. 序
中秋之后,张小胖第一天上班。
刚翻开 MBP 的盖子,准备起身泡杯茶,就看到产品经理笑嘻嘻的向张小胖走来。
「咦?听说假期 App 收入大涨,也不用这么高兴吧?难道有诈?」
产品经理看着警惕的张小胖说:「收入涨了你知道吧?分析发现都来自阿拉伯地区,我们决定抱好这根大腿,为阿拉伯语做一次本地化,马上再发一版」。
张小胖心想「阿拉伯好像还需要什么 RTL 布局,这…我也没做过啊!」
「怎么样?阿拉伯翻译文件午饭前就能到位,今天下班前能发版吗?」,看着发呆的张小胖,产品继续催促道。
「……」
越来越多的公司 App,都开始淘金海外,寻找更多的机会。然而海外市场千差万别,无论是市场还是用户的使用习惯,都有诸多的不同。
当你接触一款出海 App 的时候,除了需要了解海外 Google Service 的整个生态圈,还要做好不同语言的适配。语言适配最通用的做法就是根据不同系统语言设定,配置不同的语言资源(strings.xml),而其中比较特殊的就是例如阿拉伯的 RTL 布局,它不仅改变了语言,还改变了 UI 布局和使用习惯。
我们常用的习惯,称之为 LTR(Left-To-Right),其意为我们的阅读和书写习惯,是从左向右延伸的。而 RTL(Right-To-Left) 则正好相反,它的阅读和使用的习惯都是从右向左,常见使用 RTL 习惯的语言有阿拉伯语、希伯来语等。
今天就来聊聊,一个成熟的 Android App,想要做 RTL 适配,需要关注什么,想要适配 RTL 有哪些任务清单。
如果你维护的 App 有国际化的要求,那这个问题是迟早需要面对的。
二. Android 支持 RTL
2.1 什么是 RTL?
正如前面介绍的,RTL 是 Right-to-left 的缩写,其意为阅读和书写的习惯,是从右向左延伸的。再对比一下我国人自身的使用习惯,都是 LTR 的,也就是从左向右。
RTL 可以简单理解是 LTR 的镜像,当需要适配 RTL 的时候,除了翻译语言本身,还需要做到的就是 UI 布局,从中轴上镜像反转。
虽然 RTL 不符合我们国人的使用习惯,但是全球范围内依然有一部分人保持着 RTL 的习惯,比较常见的就是阿拉伯语、希伯来语等。
就 Android 系统来说,Android 4.1 开始就在 TextView 和 EditView 中增加了对双向文本的优先支持,允许其文本内容从左向右(LTR)到从右向左(RTL)的显示和切换。而在 Android 4.2 开始,增加了对 RTL 镜像布局完全原生的支持。
也就是在 Android 4.2(Api Level 17)及之后,在 UI 上的布局镜像,是原生支持的。在这些系统版本上,只要用户系统语言切换到「RTL 系语言」,首先系统 UI 会直接左右镜像切换,此时如果你的 App 支持 RTL 镜像布局时,也会自动切换布局方向。
2.2 App 如何支持 RTL 镜像
正如前面介绍的一样,LTR 到 RTL 的切换,不是由开发者控制的,而通常是由系统语言来控制的。
当系统语言切换为「RTL 系语言」时,还需要你的 App 支持 RTL 镜像布局。
这里所谓的支持,其实只需要配置一个属性即可,就是 AndroidManifest.xml 配置文件中的一个清单元素。需要在 <applictaion>
标签下,配置元素 android:supportsRtl="true"
。
此时当系统语言切换的时候,你的 App 也会跟着切换 UI 布局为镜像后的效果。
![](https://i-blog.csdnimg.cn/blog_migrate/a840cf3fa1ee9abbfe84486718b147a4.png)
除了需要开启 supportsRtl
属性之外,还需要一些布局属性的配合。
简单来说,就是将布局需要的所有 xxxLeft/xxxRight "替换"为 xxxStart/xxxEnd。
例如我们常用的 Padding 和 Margin,都有类似 paddingLeft 和 layout_marginRight 属性,这些就需要"替换"成 paddingStart 和 layout_marginEnd 属性。当然不止于此,还有一些 gravity、drawableLeft 等属性需要"替换"。原则上,所有 Left/Right
都需要变换为 Start/End
就好了。
这些属性,官方文档中已经帮我们列举出来了。
![](https://i-blog.csdnimg.cn/blog_migrate/73963593a85cb388bf1b5aa53b0823f6.png)
到这里应该了解了,Android App 支持 RTL 镜像的主要流程,就两步:
App 增加
android:supports="true"
属性。调整 UI 布局属性,从
left/right
到start/end
切换。
那么问题来了,我们在日常编码的过程中,应该使用 left/right 还是 start/end?还是两者都需要?
注意到我前面提到的 UI 布局属性的替换时,是打了引号的,你是否需要使用 start/end
来完全替换 left/right
,完全取决于 App 当前的 minSdkVersion 值。
正如前面所提到的,Android 对 RTL 的原生支持,是在 Android 4.2 中才具备的,也就是说,如果 App 的 minSdkVersion 大于等于 4.2,你只需要使用 start/end
属性,但如果还需要支持 4.2 以下的设备用户,那就需要保留 left/right
和 start/end
两者。
在低于 4.2 的系统中,不识别 supportsRtl
和 start/end
属性,所以不会造成影响。但是需要注意,在适配完成之后,后续开发新页面时的编码习惯。
2.3 AS 助力调整布局属性
如果当前需要适配的是一个成熟项目,并且其中的布局习惯还是使用 left/right
系的属性,那么针对所有页面布局文件,进行手工调整就是一个非常大的工作量了。
所幸的是 AS 提供了自动化的支持。
你可以在 Refactor → Add RTL Support Where Possible 来开启 RTL 的自动调整。
![](https://i-blog.csdnimg.cn/blog_migrate/146c9057be75697e0e869f6faae77aaa.png)
它会自动将项目中所有的 left/right
属性都替换为 start/end
属性,如果想要适配 Android 4.2 以下的设备,需要保留两者,那么在 Run 之前,勾选 Relpace Left/Right Properties with Start/End Properties 选项即可。
早期的 AS 自动支持 RTL 布局的时候,效率会有一些问题,转换的时候如果布局过多,可能会卡死,但是新版的 AS 已经优化了很多,转换效率上还是可以接受的。
另外这毕竟是自动替换,在替换完成之后,还是需要每个页面都测试一遍,看看效果才算完,有时候还需要我们做一些微调的工作。例如 AS 自动替换 RTL 布局的时候,如果使用了 include
标签,其中用到的方向属性不会被替换。
自动化虽然方便了我们机械的重复,但也必须介入人工的干预符合预期。
三. RTL 细节调整
要做这种全全局的改动,必然会有一些细节需要微调的,这里简单写一些 RTL 布局中会需要使用到的细节调整技巧。
3.1 利用全局样式,批量修改属性
在适配 RTL 的过程中,无法避免的就是有一些属性必须要设置。例如 EditView 就需要设置以下属性。
android:textAlignment="viewStart"
android:gravity="start"
android:textDirection="locale"
那我们就可以将这些属性在 style.xml 中全局为 EditText 设置上。
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
...
<item name="editTextStyle">@style/EditTextStyle.Alignment</item>
...
</style>
<style name="EditTextStyle.Alignment" parent="@android:style/Widget.EditText">
<item name="android:textAlignment">viewStart</item>
<item name="android:gravity">start</item>
<item name="android:textDirection">locale</item>
</style>
同时 TextView 也需要设置 android:textDirection
属性,也可以采用相同的方法用 Style 的方式全局设置。
3.2 针对 RTL 的资源适配
除了布局上的适配之外,还有一些资源的适配,资源适配主要说两块内容:Drawable(mipmap) 以及 Layout 布局资源。
先来说说 Drawable 的适配。例如在不同方向的布局下,使用不同的图标。
![](https://i-blog.csdnimg.cn/blog_migrate/fa72e098d1d8fd676acd293ce29181b9.png)
上图就是个很典型的例子,在调整布局到 RTL 时,还需要注意返回「←」的图标也需要替换成「→」。
这里依然使用 Android 对资源使用的限定符的方式,可以创建 drawable-ldrtl
目录,将翻转后的图标,放在这个目录下。如果需要限定 dpi,可以在目录名后面追加。
res/
drawable/
a.png
drawable-ldrtl/
a.png // 对标 drawable/a.png 的 RTL 图标
drawable-xhdpi/
b.png
drawable-ldrtl-xhdpi/
b.png // 对标 drawable-xhdpi/b.png 的 RTL 图标
接下来再说说 Layout 布局的 RTL 布局效果适配。有些特殊的页面,可能光镜像化还不够,还需要针对性的做一些 UI 上的调整,那最简单的做法就是做两套布局,互不影响。
既然 Drawable 可以通过资源限定符的方式,设置 RTL 布局下使用的图标,其实布局也可以。
对于布局文件,可以在目录下追加限定符 layout-ldrtl/
,如果想对某个语言做布局适配,也可以增加语言限定,例如阿拉伯语可以用 layout-ar/
。
res/
layout/
main.xml // 默认布局
layout-ar/
main.xml // 阿拉伯语布局
layout-ldrtl/
main.xml // RTL 布局
针对 RTL 的 UI 布局规范,Material Design 下有一个规范的文档(https://material.io/design/usability/bidirectionality.html#localization),设计师可以参考。
3.3 代码判断是否 RTL?
有些控件的属性,是通过代码动态调整的,那在使用的过程中,就需要在代码中,判断当前的环境,是 RTL 还是 LTF,才可以确定后续的属性设置。
通过获取 Configuration 的 locale 来判断当前的环境,为了兼容,在 TextUtilsCompat 下也提供了类似的方法。
public boolean isRtl() {
return TextUtilsCompat.getLayoutDirectionFromLocale(
getContext().getResources().getConfiguration().locale) == ViewCompat.LAYOUT_DIRECTION_RTL;
}
isRtl()
方法可以直接拿来使用,依此判断结果,执行后续的操作。
3.4 不是所有控件都支持 RTL
虽说从 Android 4.2 开始,原生支持 RTL 方向布局,但是也有一些控件是不支持的,例如 ViewPager,就不支持 RTL 的方向。
这其实没有什么很好的办法,要么和产品商量对此处的容忍,要么找一些其他的解决方案。
针对 ViewPager 的 RTL 化,在 Github 就有对应的开源库 RtlViewPager(https://github.com/diego-gomez-olvera/RtlViewPager) 可供使用。其原理也是将数据进行倒序重排,没什么好说的,源码不多,有兴趣可以自己看看。
四. 适配 RTL 要如何估期?
再来聊聊适配 RTL 时,估算开发周期的问题。
除了 App 本身在设计研发之初,就是为了中东的土豪设计的之外,多数情况下,我们都是因为各种外部原因,需要在一款成熟的 App 上,适配 RTL 镜像布局。例如市场验证有大量「RTL 系语言」的付费用户,或者产品经理认为存在「RTL 系语言」的潜在用户。
什么时候适配 RTL,完全是由外部因素决定的。但是当需要适配的时候,我们作为开发者,最直观要面对的现实问题就是,适配 RTL 需要做哪些事?这个任务需要多少时间能够完成?需要哪些人来配合,哪些任务是可以并行的?
要精确估计 App 的 RTL 化,很难,因为工作量主要来自适配,说到适配,工作量就可大可小了。App 大量使用第三方控件的就比只使用原生控件的工作量大;产品经理和设计师,允许部分页面适配有差异的,也会比高要求还原的工作量大。
这些在适配完成之前,谁也不知道效果如何,既然没有什么好的方法,那就试一试吧,只需要三步,你可以拿着一个明确的镜面翻转效果,来估计适配的难度。
设置
android:supportsRtl="true"
,让 App 支持 RTL。AS 自动转换布局属性,支持 RTL 布局效果。
打开开发者选项中的「强制使用从右到左的布局方向」,强制 RTL 布局。
![](https://i-blog.csdnimg.cn/blog_migrate/633e9761cea136763bb8709445757a82.png)
此时你基本上可以看到一个 80% RTL 化的 App,剩下的就是把页面都检查一遍,看看有没有用到哪些控件不支持 RTL,哪些 Drawable 需要替换、哪些布局需要微调。然后针对性的调整即可。
有一些控件不支持 RTL 就会比较麻烦,有源码的就改改源码,没源码的就看有没有地方可以 Hook 解决。
总之需要做什么,清单是固定的。有了明确的任务,自然就容易估计开发周期了。
列举一下适配 RTL 的任务清单:
App 支持 RTL,AS 自动转换布局属性支持 RTL,从开发者选项里强制 RTL 布局方向。
按页面排查,检查出需要翻转的 Drawable 资源,打包交给设计师,反转后替换。
检查布局翻转后的效果,和设计师确定需要适配翻转后的 UI 效果。
找到不支持 RTL 化的控件,可以从源码的角度分析,能改源码的改源码,不能改源码的尝试 Hook 解决或找替代方案。
翻译 RTL 系语言资源 strings.xml,放入对应的资源目录,例如阿拉伯语需要放入 /values-ar/strings.xml 目录,将系统语言切换到阿拉伯语,排查所有页面文字与控件的匹配度。
整体验收,微调效果。
其中需要和产品、设计、翻译配合的,都可以提前准备,让任务并行化。当然在适配的过程中,还有一些实际的问题,就需要遇到问题再解决问题了。
五. 小结时刻
本文聊了如何在一个成熟的 App 上,适配 RTL 镜像效果,以及如何快速的适配。最后还列出了一个适配时,需要调整关注的清单列表,希望对你有所帮助。
本文就到这里,如果有所帮助,留言、转发、点好看是最大的支持,谢谢!
reference:
https://material.io/design/usability/bidirectionality.html#localization
https://android-developers.googleblog.com/2013/03/native-rtl-support-in-android-42.html
https://developer.android.com/training/basics/supporting-devices/languages?hl=zh-cn
「联机圆桌」????推荐我的知识星球,一年 50 个优质问题,上桌联机学习。
公众号后台回复成长『成长』,将会得到我准备的学习资料,一起学习进步。
![](https://i-blog.csdnimg.cn/blog_migrate/293d9cf13aad72555b2df9ba61f7ef3a.png)