做 Android 国际化,遇到阿拉伯语言,会注意到,他们的本地阅读习惯是从右往左的,也就是 RTL。
Android 自带了 RTL 的方案,文字和布局方向会自动镜像排列,但是图片不会自动镜像。图片的镜像有两种方案,都需要额外的工作:
- 在 drawable 对应的 ldtr 文件夹放入一张对应图片的同名的镜像图片,例如
drawable-ldrtl-xhdpi
文件夹 - 不直接使用图片,创建 drawable,例如:
<?xml version="1.0" encoding="utf-8"?> <bitmap xmlns:android="http://schemas.android.com/apk/res/android" android:src="@drawable/icon" android:autoMirrored="true"/>
上面第 2 种方案,可以注意到 android:autoMirrored="true"
这个属性,他能把 drawable 自动镜像,很遗憾,这个属性不能直接在布局的 XML 文件中使用。
不过,我们可以使用 Android 的 layout infalter 机制,Hook View 树的生成过程,修改 ImageView 的 src 或者 View 的 background,利用 android:autoMirrored="true"
对应的 drawable.setAutoMirrored(true)
代码。
Android 提供了这样的 Hook 接口:LayoutInflater.Factory
或 LayoutInflater.Factory2
,后者比前者多一个重载方法,直接使用后者即可。在 Activity 创建的时候,注入我们自己的 rtlFactory
val inflater = activity.layoutInflater
inflater.factory2 = rtlFactory
XML 生成 View 的时候,会触发 rtlFactory
中的 onCreateView()
方法,这个方法里,我们可以将生成的系统 View 对象替换成我们指定的自定义的 View 对象,比如下面的 RtlImageVIew
。
override fun onCreateView(
parent: View?, name: String, context: Context, attrs: AttributeSet
): View? {
if (name == "ImageView) {
return RtlImageVIew(context, attrs)
}
}
在 RtlImageVIew
中,可以读出 XML 文件中设置好的图片资源,设置自动镜像:
val drawable = AppCompatResources.getDrawable(view.context, srcResId) //读取 drawable
drawable?.isAutoMirrored = true //设置自动镜像
view.setImageDrawable(drawable)
当然也可以给 RtlImageView
添加自定义属性 app:mirrorSrc="true"
,用来标记是否需要自动镜像。View 的 background 也同理。
综上,在布局的 XML 文件中,如果需要图片自动镜像,直接写即可:
<ImageView
android:layout_width="26dp"
android:layout_height="26dp"
app:mirrorSrc="true"
android:src="@drawable/icon" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:mirrorBackground="true"
android:background="@drawable/bg_chat" />
更多详细的代码在 GitHub 上,我封装了个库,可以直接使用。