Android国际化,阿语RTL适配总结
语言切换
语言种类
例如:
/*** 英国(英语)*/EN("en"),/*** 西班牙(西班牙语)*/ES("es"),/*** 西班牙(葡萄牙语)*/PT("pt"),/*** 法国(法语)*/FR("fr"),/*** 俄罗斯(俄语)*/RU("ru"),/*** 意大利(意大利语)*/IT("it"),/*** 德国(德语)*/DE("de"),/*** 荷兰(荷兰语)*/NL("nl"),/*** 阿拉伯(阿拉伯语)*/AR("ar"),/*** 韩国、朝鲜(韩语)*/KO("ko"),/*** 日本(日语)*/JA("ja");
代码切换App语言
更改context语言配置
@JvmStaticfun wrap(context: Context, newLocale: Locale): Context {return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {createConfigurationResources(context, newLocale)} else {applyLanguage(context, newLocale)context}}private fun applyLanguage(context: Context, newLocale: Locale) {val resources = context.resourcesval configuration = resources.configurationif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {// apply localeconfiguration.setLocale(newLocale)} else {// updateConfigurationconfiguration.locale = newLocaleval dm = resources.displayMetricsresources.updateConfiguration(configuration, dm)}}@TargetApi(Build.VERSION_CODES.N)private fun createConfigurationResources(context: Context, newLocale: Locale): Context {val resources = context.resourcesval configuration = resources.configurationconfiguration.setLocale(newLocale)val localeList = LocaleList(newLocale)LocaleList.setDefault(localeList)configuration.setLocales(localeList)return context.createConfigurationContext(configuration)}
基类activity应用语言配置
open class BaseActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)}override fun attachBaseContext(newBase: Context) {super.attachBaseContext(LanguageUtil.wrap(newBase, Language.getCurrentLanguage()))}
}
LTR和RTL,适配阿语
RTL 语言由来
RTL 是 Right-to-left(从右向左) 的缩写。其意为人们书写阅读习惯是从右向左,朝左继续的,常见的 RTL 语言有阿拉伯语,希伯来语等。
全局API设置
从 Android 4.2 即 SDK 17 开始,提供了全面的本地布局支持,允许镜像布局,可以同时支持 RTL 和 LTR。
Android Studio全局设置RTL
AndroidManifest.xml
<application...android:theme="@style/AppTheme"android:supportsRtl="true">...
</application>
- 图片配置
将RTL切图放在下图对应的文件夹内,可以实现阿语下图片的转换
- styles里的AppTheme对于TextView、EditText等控件的文本方向、对齐方式设置
android:layoutDirection 设置组件的布局排列方向
android:textDirection 设置组件的文字排列方向
android:textAlignment 设置文字的对齐方式
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">...<item name="editTextStyle">@style/AppTheme.EditTextStyle</item><item name="android:textViewStyle">@style/AppTheme.TextViewStyle</item><item name="android:autoCompleteTextViewStyle">@style/AppTheme.AutoCompleteTextView</item><item name="textInputStyle">@style/AppTheme.TextInputLayout</item>...</style><style name="AppTheme.EditTextStyle" parent="Widget.AppCompat.EditText"><item name="android:textAlignment">viewStart</item><item name="android:textDirection">locale</item></style><style name="AppTheme.TextViewStyle" parent="Widget.AppCompat.TextView"><item name="android:textDirection">locale</item></style><style name="AppTheme.AutoCompleteTextView" parent="Widget.AppCompat.AutoCompleteTextView"><item name="android:textAlignment">viewStart</item><item name="android:textDirection">locale</item></style><style name="AppTheme.TextInputLayout" parent="Widget.Design.TextInputLayout"><item name="android:textAlignment">viewStart</item><item name="android:textDirection">locale</item><item name="hintEnabled">false</item></style>
特别注意:在AppTheme中设置控件的文本方向和对齐方式,按道理是全局生效的。但是在实际使用中,并没有全局生效,这个时候需要在具体控件使用时进行单独设置
<TextView...style="@style/AppTheme.TextViewStyle".../><com.google.android.material.textfield.TextInputLayout...style="@style/AppTheme.TextInputLayout"...><MultiAutoCompleteTextView...style="@style/AppTheme.AutoCompleteTextView"... /></com.google.android.material.textfield.TextInputLayout>
关于Padding、Margin、setCompoundDrawables等带有方向属性的设置
通过Android Studio的RTL工具全局替换,XML里面发生的变化有:
paddingLeft -> paddingStart
paddingRight -> paddingEnd
marginLeft -> marginStart
marginRight -> marginEnd
layout_alignParentLeft -> layout_alignParentStart
layout_alignParentRight -> layout_alignParentEnd
drawableLeft -> drawableStart
drawableRight -> drawableEnd
layout_constraintLeft_toLeftOf -> layout_constraintRight_toRightOf
layout_constraintRight_toRightOf -> layout_constraintEnd_toEndOf
…
所以,在代码和styles中关于上述带有方向的属性需要手动修改。
例如:
<resources>...<style name="XXX"><item name="android:layout_width">30dp</item><item name="android:layout_height">20dp</item><item name="android:layout_alignParentRight">true</item><item name="android:layout_alignParentEnd">true</item><item name="android:clickable">false</item><item name="android:layout_marginEnd">15dp</item><item name="android:layout_marginStart">15dp</item><item name="android:focusable">false</item></style>...
</resources>
...
enterBtn.setPaddingRelative(12F.dp2Px, 10F.dp2Px, 8F.dp2Px, 10F.dp2Px)
enterBtn.setCompoundDrawablesRelative(null, null, drawable, null)
...
/*** 设置相对位置的margin*/fun <T : ViewGroup.MarginLayoutParams> T.setRelativeMargin(margin: MICMargin) {marginStart = margin.lefttopMargin = margin.topmarginEnd = margin.rightbottomMargin = margin.bottom}
(parent.layoutParams as ViewGroup.MarginLayoutParams).setRelativeMargin(24f.dp2Px, 7f.dp2Px, 7f.dp2Px, 7f.dp2Px)
部分重要控件适配
ViewPager2
相比于ViewPager,ViewPager2基于RecyclerView实现,支持垂直方向滚动,支持RTL。
使用 ViewPager效果ViewPager实现的图片查看
使用 ViewPager2效果
ViewPager2实现的图片查看
流式布局(FlexBoxLayout)
使用之前的自定义控件FlowLayout
使用谷歌官方的FlexBoxLayout
3. PopWindow
mPopupWin.showAsDropDown(targetView, 0, 0);
targetView应该为显示位置相对应的目标view
- Banner
public class BannerLocaleCircleIndicator extends BaseIndicator {private int mNormalRadius;private int mSelectedRadius;private int maxRadius;public BannerLocaleCircleIndicator(Context context) {this(context, null);}public BannerLocaleCircleIndicator(Context context, AttributeSet attrs) {this(context, attrs, 0);}public BannerLocaleCircleIndicator(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);mNormalRadius = config.getNormalWidth() / 2;mSelectedRadius = config.getSelectedWidth() / 2;}...@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);int count = config.getIndicatorSize();if (count <= 1) {return;}float left = 0;int currentPosition = LanguageUtil.isAR() ? count - config.getCurrentPosition() - 1 : config.getCurrentPosition();for (int i = 0; i < count; i++) {mPaint.setColor(currentPosition == i ? config.getSelectedColor() : config.getNormalColor());int indicatorWidth = currentPosition == i ? config.getSelectedWidth() : config.getNormalWidth();int radius = currentPosition == i ? mSelectedRadius : mNormalRadius;canvas.drawCircle(left + radius, maxRadius, radius, mPaint);left += indicatorWidth + config.getIndicatorSpace();}}
}
banner指示器currentPosition在阿语下需要count - config.getCurrentPosition() - 1,为镜像位置
适配阿语的banner
针对性兼容
- ImageView,字体图标TextView镜像
/*** 将image、字体图标等控件镜像翻转*/fun <T : View> T.imageToRTL() {if (LanguageUtil.isAR()) {scaleX = -1f}}
- 和Drawable镜像
/*** 设置镜像drawable*/fun <T : View?> T.setRtlBackgroundDrawable(drawable: Drawable?) {drawable?.isAutoMirrored = LanguageUtil.isAR()this?.backgroundDrawable = drawable}
- 动画方向
在阿语下,X坐标轴方向会改变
/*** 购物车动画** @param startView 动画的起点位置* @param endView 动画的终点位置* @param context* @param root 父窗体 用于添加的动画的View* @param time 动画持续时间单位s*/public static void AddToCart(final Context context, final View startView, final View endView, final RelativeLayout root, final Drawable bgDrawable, final float time) {if (context == null || (context instanceof Activity && ((Activity) context).isFinishing()) || startView == null || endView == null || root == null || bgDrawable == null)return;endView.postDelayed(new Runnable() {@Overridepublic void run() {//新建一个ImageView 用于动画显示final ImageView view = new ImageView(context);//确定ImageView大小与传进来的ImageView相同RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);//获取ImageView的图片 并设置在新的ImageView上view.setImageDrawable(bgDrawable);//计算父控件的位置int[] parent = new int[2];root.getLocationInWindow(parent);//计算起点控件位置int[] startLocation = new int[2];startView.getLocationInWindow(startLocation);if (LanguageUtil.isAR()) {startLocation[0] = FMFScreenUtil.getWindowWidth() - startLocation[0];}//计算终点控件位置int[] endLocation = new int[2];endView.getLocationInWindow(endLocation);if (LanguageUtil.isAR()) {endLocation[0] = FMFScreenUtil.getWindowWidth() - endLocation[0];}//确定ImageView的位置与startView相同params.setMarginStart(startLocation[0] - parent[0] - root.getPaddingStart());params.topMargin = startLocation[1] - parent[1] - root.getPaddingTop();root.addView(view, params);//计算两者的横向X轴的距离差int XtoX = endLocation[0] - startLocation[0]/* + endView.getWidth() / 2 - startView.getWidth() / 2*/ + endView.getWidth() / 2;//根据距离 时间 获取到对应的X轴的初速度final float xv = XtoX / time;//计算两者的横向X轴的距离差int YtoY = endLocation[1] - startLocation[1] + endView.getHeight() / 2;//根据距离 时间 初始设置的Y轴初速度与X轴初速度相同 获取到竖直方向上的加速度final float g;if (xv > 0) {g = (YtoY + xv * time) / time / time * 2;} else {g = (YtoY - xv * time) / time / time * 2;}final ValueAnimator va = new ValueAnimator();va.setDuration((long) (time * 1000));va.setObjectValues(new PointF(0, 0));//计算位置va.setEvaluator(new TypeEvaluator<PointF>() {@Overridepublic PointF evaluate(float v, PointF pointF, PointF t1) {PointF point = new PointF();point.x = v * xv * time;if (xv > 0) {point.y = g * (v * time) * (v * time) / 2 - xv * v * time;} else {point.y = g * (v * time) * (v * time) / 2 + xv * v * time;}return point;}});va.start();//设置动画va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator valueAnimator) {PointF point = (PointF) valueAnimator.getAnimatedValue();view.setTranslationX(point.x);view.setTranslationY(point.y);}});//在动画结束时去掉动画添加的ImageViewva.addListener(new Animator.AnimatorListener() {@Overridepublic void onAnimationStart(Animator animator) {}@Overridepublic void onAnimationEnd(Animator animator) {root.removeView(view);}@Overridepublic void onAnimationCancel(Animator animator) {}@Overridepublic void onAnimationRepeat(Animator animator) {}});}}, 100);}
React-Native RTL适配
- 图片RTL适配
import {isSA} from '@utils/common';const imagePath = {XXX: () => {return isSA()? require('@assets/img/XXX@rtl.png'): require('@assets/img/XXX.png');},XXXX: () => {return isSA()? require('@assets/img/XXXX@rtl.png'): require('@assets/img/XXXX.png');},
};export default imagePath;
<ImageBackground...source={imagePath.member_center_footer_image()}>...</ImageBackground>
Android和iOS适配细节
问题类型 | 结论 |
---|---|
排版总体整齐,个别文案显示错乱或者不规范 | 个别文案或者控件显示错乱不对齐时,按照整体页面的位置和方向进行对齐,可以考虑单独适配的技术实现方式。 |
阿语 英文混合出现,按照什么样式展示? | 1.整体按照语言环境去显示位置,比如英语环境下,整体居左;在阿语环境下,整体居右。 2.英文单词本身还是按照英文顺序从左往右展示。 |
纯英文在阿语下按照什么样式展示? | 1.整体按照语言环境去显示位置,比如英语环境下,整体居左;在阿语环境下,整体居右。2.按照英文单词顺序显示。 |
输入框输入光标,英文对齐方式 | Android 1.光标英语环境下,居左;阿语环境下,居右。 2.阿语环境下,输入的文案,如果是英文,显示在左边;如果是阿语,显示在右边 。iOS 1.ios15以下,光标和输入内容都是居右显示。 2.ios15以上,光标和输入内容都是根据输入语言,英文居左,阿语居右。 |
Android国际化,阿语RTL适配总结相关推荐
- iOS 阿拉伯语 RTL适配
阿拉伯语: 背景:阿拉伯语使用者占世界人口的6%,使用阿拉伯字母,为从右至左书写的文字,主要通行于18个阿拉伯国家及4个国际组织的官方语言.以阿拉伯语作为母语的人数超过2.6亿人.阿拉伯语在全球范围使 ...
- Android阿拉伯语UI适配问题汇总
Android阿拉伯语UI适配问题汇总 Sensi Sun RTL和LTR LTR 是 Left-to-right(从左向右) 的缩写.(常用) RTL 是 Right-to-left(从右向左) 的 ...
- Android 国际化与本地化探索
Android 国际化与本地化探索 1.翻译注意事项 1.1.尽量避免使用简写! 1.2.严禁回车.换行等! 1.3.尽量避免使用&.'."等特殊符号! 1.4.注意空格的使用! 2 ...
- Android 国际化
由于公司的项目是投放 google play store , 所以要做国际化.国际化遇到的两个大问题 字符串国际化 布局样式国际化 一:字符串国际化 解决这个问题很简单,在res目录下放 ...
- android国际化语言编码对照表
android国际化语言编码对照表 (查询整理以备不时之需,以下内容均已附原文连接) 中文(中国):values-zh-rCN 中文(台湾):values-zh-rTW 中文(香港):values-z ...
- 重拾Android之路(三)手机适配
随着android智能手机的发展和普及,各种各样的大小和尺寸的android智能机不断的退出,通过各种各样的设备机型,我们能够让自己的APP接触到广大的用户.为了能在各种android平台上使用,我们 ...
- Unity阿拉伯语的适配(终极版)
最近在做阿语的适配,发现网上并没有一套完整的方案,这次给大家带来一套完整的解决方案,并配上一套可扩展的代码,希望我们每个人以后再遇到阿拉伯语的适配的时候,可以使用这套终极解决方案来搞定.同时随着uni ...
- android屏幕适配的目的,Android 不同分辨率下屏幕适配的实战方案与经验总结
Android 开发中,屏幕适配是一大考点,几乎每一场面试,都不会落下这个问题,这个问题说简单也简单,说难也难,当然对于有过真实的适配经验的人来说,这个根本不算什么问题,从坑里爬过的人,自然知道这其中 ...
- 技术实践 | Android 设备音视频兼容性适配
导读:WebRTC 是一个非常优秀的项目, 可以支持 Web.iOS.Android.Mac.Windows.Linux 在内的所有平台的 API,保证了 API 在所有平台的一致性.然而 WebRT ...
最新文章
- 关于Bulk加载模式
- iOS自动打包并发布脚本
- sap.m.library acts as one of the two most core framework library
- 《TCP/IP详解》笔记----第四章 ARP协议
- 关于SpringBoot中的多数据源集成
- python安装beautifulsoup失败_Win10环境下python36安装BeautifulSoup出现错误的解决办法
- go 语言链接服务器上的mysql数据库
- 计算广告 pdf_他创业20年死磕PDF一项业务,如今上市身家76亿:所有的突然牛逼,背后都是玩命死磕...
- 天天打无人车是怎样一种体验?
- Focal Loss for Dense Object Detection解读
- Qt加载gif动态图
- 高校学生社团管理系统
- 2020计算机二级c语言答案,2020年全国计算机二级C语言考试试题分析
- 互联网快讯:龙佰集团冲刺港交所;极米Z6X Pro、极米H3S持续热销;京东物流调集3246人增援上海
- Plsql ORA-00054的解决方法
- 白硕:区块链技术与数据隐私(附视频)
- 功耗大好还是小好_热设计功耗高好还是低好 - 卡饭网
- dcloud 5+ 监听安卓前后台切换状态 并后台运行程序
- python matlab 多条曲线 单位_【基础篇】MATLAB科研制图常用代码命令
- Unity新手必备5款宝藏插件--价值上千元白嫖最新版