android:textAlignment="viewStart"
textAlignment属性支持的有:inherit、gravity、textStart、textEnd、center、viewStart、viewEnd。我们就看下viewStart的含义:
Align to the start of the view, which is ALIGN_LEFT if the view’s resolved layoutDirection is LTR, and ALIGN_RIGHT otherwise.
也就是说当设置了这个属性时,文本的布局会对齐到视图开始的部分,如果视图是LTR,那么对齐到左侧。否则对齐到右侧。所以再来看下设置后的渲染效果:
到这里相信各位小伙伴肯定对这个属性留下了深深的印象。但是,难道我们要给代码中的所有TextView手动添加这行代码吗?这也太累了吧,想偷懒!
我想说:可以!大家对style肯定不陌生吧,我们定义一个通用的TextView的style,设置它的textAlignment属性,然后在App的主题style中应用这个通用的style,自定义TextView的style代码很简单:
<style name="Text.Alignment" parent="@android:style/Widget.TextView">
<item name="android:textAlignment">viewStart</item>
</style>
添加到你自己App的主题中去,记得是android:textViewStyle而不是android:textStyle,两个含义不一样哦:
<style name="AppTheme" parent="Theme.MaterialComponents.NoActionBar">
<item name="android:textViewStyle">@style/Text.Alignment</item>
</style>
但是还有些文本是需要居中显示的,那你要单独再进行处理了,至于全局处理还是单个处理,请自行斟酌。
输入框和上述TextView也有同样的问题,按照上述方法自定义style即可,最后添加到自己App主题的时候记得属性是editTextStyle:
<item name="editTextStyle">@style/EditTextStyle.Alignment</item>
你以为这就解决问题了吗?是的,其实大部分问题都解决了,但是在部分vivo的手机上输入时候居然有半个光标的情况,如下所示:

起初测试直接给提了个bug,我也以为是个bug,后来多次尝试才觉得:这可能是vivo这个机型对阿拉伯语环境的“优化”?因为输入纯数字的时候EditeText无法判断这个到底是阿拉伯语还是其他语言,所以展示前后两个光标。当继续输入英文字符后,此时光标变为一个,位于英文字符之后。干脆回复测试:不予处理!!!
【请仔细阅读下文,思考为何会出现这种情况,且如何解决】
关于ImageView其实在于RTL布局需不需要镜像的问题,这个问题说简单也简单说复杂也可以做的很复杂,我们先说最最最简单的方法。
首先了解下scaleX属性,这个属性我们一般来处理X轴方向上视图的缩放,大于1时放大,小于1时缩小,那么小于0的时候呢?一开始的时候我从来没想过,但是国际化的时候翻阅官网各种镜像View的资料【见这里】,很神奇的就发现了这么个方式:
android:scaleX="-1"
X轴方向缩放到负值的时候,居然就是水平方向上的镜像效果了,牛批牛批,真的长见识了!!!(那么竖直方向的镜像效果你也应该知道怎么设置了,但是RTL布局不需要竖直方向上的镜像效果,不要多此一举哦)
正常LTR布局效果如下所示:

当设置以上属性的时候,布局效果如下所示,水平镜像:

关于是否需要ImageView的镜像效果,看你具体的图片了,以上图片只是为了演示镜像效果选择的,其实这个图片不需要镜像效果,因为上面有非本地化的文字显示,镜像之后反而不合适了。
好的,知道了怎么镜像图片,那么我们怎么根据语言环境设置相应的值呢,可以根据RTL的规范,我们在values文件夹同级目录建立values-ldrtl资源文件夹,这就表示RTL布局会使用该资源文件夹下的资源,然后建立integer.xml文件,内容如下:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<integer name="rtl_scale_x">-1</integer>
</resources>
同样,在values文件夹下也需要建立integer.xml文件,内容同上述xml代码类似,值需要改为1。
然后给ImageView设置应用如下属性值即可,表示在LTR布局中水平缩放是1,也就是正常显示原图片,在RTL布局中则会镜像图片进行显示:
android:scaleX="@integer/rtl_scale_x"
说完了以上最简单的方法,我们再来说一个属性,rotation,这个属性是控制视图的旋转属性的,默认是0,当我们设置:
android:rotation="180"
此时效果图如下所示:

这个效果并不符合我们的预期,因为它还上下颠倒了。但是有些图片,比如我们上文演示的返回按钮,它旋转180度之后显示效果是跟我们预期相符的。
再来一种方式,根据不同的资源文件夹,放置多种方向的图片,这种方式无疑会增加APK的体积(使用bundle的方式不受影响)。
针对所有RTL设置,则命名方式为:
• drawable -> drawable-ldrtl
• drawable-xhdpi -> drawable-ldrtl-xhdpi
如果只适配阿拉伯语这么一种语言,那么也可以是这样:
• drawable-xhdpi -> drawable-ar-xhdpi
同理drawable、values、layout等资源文件夹都可以如法炮制。
我们一般自定义shape属性如下所示:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners
android:bottomLeftRadius="20dp"
android:topLeftRadius="20dp" />
<gradient
android:angle="0"
android:endColor="@color/mainColorEnd"
android:startColor="@color/mainColorStart" />
</shape>
效果如下所示:

此时处理方法也可以类似上文的ImageView一样,给使用该Shape的View设置scaleX来做镜像处理,这样做最简便。
如果想在shape中直接处理的话,部分是可以的,但是还有点限制。
先看gradient属性,它支持设置angle,0表示从左到右,180则表示从右到左,所以可以根据上文一样,使用不同的integer.xml中的值进行处理。
但是corners这个属性只支持bottomLeftRadius、topLeftRadius,并不支持bottomStartRadius、topStartRadius,所以我们无法直接在该shape文件中处理。只能建立drawable-ldrtl文件夹,建立同名文件,然后修改bottomLeftRadius、topLeftRadius为bottomRightRadius、topRightRadius进行处理了。
目前ViewPager不支持RTL的布局方向,由于时间紧任务重,本项目中还未适配该组件,可以参考GitHub上的项目 RtlViewPager 进行处理,该项目已经5年未维护了,参考下原理即可。
同时建议使用ViewPager2逐步替换ViewPager。
TabLayout现在已经支持。
最重要的一点:禁止任何形式的字符串硬编码!!!
同xml一样,自定义View的时候也需要注意相关left、right的问题,其他未标明的也需要自己多注意:
仅支持LTR的属性 | 支持LTR、RTL的属性 |
---|
leftMargin | setMarginStart() |
rightMargin | setMarginEnd() |
setMargins(int left, int top, int right, int bottom) | 需自行处理left、right |
setPadding(int left, int top, int right, int bottom) | setPaddingRelative(int start, int top, int end, int bottom) |
由于自定义View的时候可能会使用到位置属性,而layoutParams中没有Start、End这种属性,只有Left、Right。所以当我们布局视图的位置时就要考虑到RTL和LTR布局了。
比如说:视频通话页面,LTR环境下是,默认远端视频是全屏展示的,自己的视频会显示在右上角。而到了RTL环境下,那么自己的视频就需要显示在左上角了。所以定位自己视频的位置时候就需要根据不同的环境进行处理。默认情况下我们计算出正常LTR布局下小视频流的中心位置,然后判断当前的布局环境,如下所示:
Configuration config = getResources().getConfiguration();
if (config.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
mSmallCenterX = ScreenUtils.getScreenWidth() - mSmallCenterX;
如果是RTL布局环境,那么镜像下处理方式就是:屏幕宽度减去LTR布局下的X坐标的位置。
然后在切换本地视图和远端视图大小的时候,动画效果也可以正常执行了。
一般项目中我们会有些自定义的异常等信息,所以经常会直接使用其构造函数直接硬编码message参数。在国际化项目中建议一开始就先定制基类,传递String资源ID进去。对于一些三方框架可能也要做下兼容处理,例如如下BaseException:
public class BaseException extends Exception {
private final int strId;
private final int errorCode;
* 建议使用资源ID的方式
public BaseException(int errorCode, @StringRes int strId) {
super("请使用资源ID的方式获取错误信息,获取String资源ID请使用getStrId()函数!");
this.strId = strId;
this.errorCode = errorCode;
* 为兼容三方框架,还是保留了String类型的参数信息
public BaseException(int errorCode, String msg) {
super(msg);
this.strId = 0;
this.errorCode = errorCode;
然后一般展示错误信息的话是在Activity,Fragment等进行展示,可以直接使用context.getString()来获取相应的信息:
public String getMessageFromThrowable(Throwable throwable) {
if (throwable instanceof BaseException) {
BaseException exception = (BaseException) throwable;
return getString(exception.getStrId());
} else {
return throwable.getMessage();
还有些情况可能是在工具类中直接吐司展示错误信息的,那么建议获取顶层Activity等然后使用getString()的方式来进行处理。
关于Toast一般项目中都会自定义统一的Toast,单说普通情况下,如下这两种方式都会跟随你设置后的语言进行展示:
Toast.makeText(getContext().getApplicationContext(), getString(R.string.sample_text), Toast.LENGTH_SHORT).show();
Toast.makeText(getContext(), getString(R.string.sample_text), Toast.LENGTH_SHORT).show();
有的项目可能还集成了blankj作者的工具箱utilcodex,在该工具箱中有一个工具类ToastUtils,它支持使用资源ID的形式,如下:
public static void showShort(@StringRes final int resId) {
show(UtilsBridge.getString(resId), Toast.LENGTH_SHORT, DEFAULT_MAKER);
但是使用该方式获取的始终是跟随系统的语言,如果系统语言不在App支持列表内那就会使用App默认的语言,具体原因请查看源码。
一般我们在拼接文字的时候都会用到占位符来进行处理,例如:地址是:%s?,我们把地址固定为:688 号大街(注意688后有一个空格),那么使用String.format()函数处理过后,中文情况下显示效果为:

然而阿拉伯语显示情况却有点异常,如下:
为什么688跑到后面去了,正常情况下这段文字应该从左到右显示的,然而688却显示到了右边。这是因为我们给的固定地址中包含了数字这个特殊文本(同上前文的EditText输入数字时部分手机光标的情况),系统无法确定它是属于LTR的部分还是RTL的部分,所以显示就出了问题。
那么怎么处理呢?需要使用BidiFormatter类,该类unicodeWrap()函数会检测字符串方向,并封装该字符串,官方示例如下,直接使用了BidiFormatter.getInstance():
String.format(
getString(R.string.did_you_mean),
BidiFormatter.getInstance().unicodeWrap(mySuggestion)
这种情况适合系统设置的语言为阿拉伯语的情况,如果我们是应用内切换语言,那么此时BidiFormatter.getInstance()默认的处理方式就不对了,我们需要使用其**BidiFormatter getInstance(Locale locale)**函数了,把我们本地的环境传递进去,例如:new Locale(“ar”)。这样处理完后才会正常显示,效果如下:
加入有时候我们的控件不需要根据RTL布局来改变方向怎么处理呢?使用layoutDirection就可以强制该控件按照LTR或者RTL等方向来布局了。
android:layoutDirection="ltr"
Android 国际化与本地化探索缘起公司项目国际化与本地化的需求,本篇文章将从 语言翻译 、 UI设计 、 代码规范 ,这三个方面来进行阐述,其中不免包含本人的主观偏见,如果有任何宝贵的意见,欢迎指正。1、翻译注意事项由于我们现在使用的是 腾讯文档 协同处理的翻译文本,而腾讯文档导出后的Excel文件会有些奇怪的小问题,比如说标红文字前后的空格导出后空格会丢失,导致英语等其他语言显示异常。同时,这么多的文字,加上多达十几种语言的处理,靠人工来转换成App所需资源实在是效率低下,所以脚本工程请参见:
1.来电通知栏电话号码+号显示在右侧的修改
位置:packages/apps/InCallUI/src/com/android/incallui/StatusBarNotifier.java
刚开始修改的时候,在方法buildAndSendNotification()中添加:
//add by chensenquan20160727
Configuration con =mContext.
貌似是在Android2.1之前的时代,新建Android工程,工程会自动生成一个drawable的目录。
到了Android4.0时代,新建Android工程,工程会自动生成drawable-hdpi, drawable-ldpi, drawable-mdpi, drawable-xhdpi四个目录。
不同点在于,图片资源文件被细分了。系统会根据dip的大小选择对应目录下的资源文件。
支持不同语言
支持不同屏幕支持不同语言Android平台能够在运行时根据本地区域设置来选择不同语言。如果所有string都来源strings.xml,那么定义可选的string.xml文件,android系统在运行时会进行正确选择。实现方法:
默认创建的string.xml在res/values/目录下。为支持不同语言,在res/目录下创建包含”-“和ISO语言码的values目录,形如”valu
1.创建 fragment
2.activity 引入 fragment组件,
3.实例化 fragment类,开启事务,根据情况进行替换//根据手机的方向切换 不同的fragment
public class MainActivity extends AppCompatActivity { @Override
protected void onCreate(Bundle savedI
在res文件下面创建values文件夹,例如values-zh-rTW这里面的xml里面是繁体字,然后切换语言的时候就为台湾语言 或者values-de-rDExml里面使用德语然后xml使用德语。其他依次看列表,列表借鉴别人的,自己再次基础上作了修改
如上图所示,RTL(Right To Left)即视图的表现形式是从右开始向左结束。我们日常更习惯于LTR(Left To Right)视图,但在中东的阿拉伯语系里他们视觉习惯跟我们正好相反,他们更习惯从右向左的视觉形式。
最近公司开发的产品正好需要做RTL适配,本文总结分享在适配RTL过程中的技术要点。
RT...
window: ComposeWindow,
onFileDrop: (List<File>) -> Unit,
content: @Composable BoxScope.() -> Unit
val component = ......
val pane = ......
Box(modifier = modifier.onPlaced ......
[/code]
使用ComposeDesktop开发一款桌面端多功能APK工具
virogu:
使用ComposeDesktop开发一款桌面端多功能APK工具
virogu: