例如:
/**
* 英国(英语)
*/
EN("en"),
/**
* 西班牙(西班牙语)
*/
ES("es"),
/**
* 西班牙(葡萄牙语)
*/
PT("pt"),
/**
* 法国(法语)
*/
FR("fr"),
/**
* 俄罗斯(俄语)
*/
RU("ru"),
/**
* 意大利(意大利语)
*/
IT("it"),
/**
* 德国(德语)
*/
DE("de"),
/**
* 荷兰(荷兰语)
*/
NL("nl"),
/**
* 阿拉伯(阿拉伯语)
*/
AR("ar"),
/**
* 韩国、朝鲜(韩语)
*/
KO("ko"),
/**
* 日本(日语)
*/
JA("ja");
代码切换App语言
更改context语言配置
@JvmStatic
fun 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.resources
val configuration = resources.configuration
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
// apply locale
configuration.setLocale(newLocale)
} else {
// updateConfiguration
configuration.locale = newLocale
val dm = resources.displayMetrics
resources.updateConfiguration(configuration, dm)
}
}
@TargetApi(Build.VERSION_CODES.N)
private fun createConfigurationResources(context: Context, newLocale: Locale): Context {
val resources = context.resources
val configuration = resources.configuration
configuration.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 语言有阿拉伯语,希伯来语等。
从 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">
...
- "editTextStyle"
>@style/AppTheme.EditTextStyle
- "android:textViewStyle"
>@style/AppTheme.TextViewStyle
- "android:autoCompleteTextViewStyle">@style/AppTheme.AutoCompleteTextView
- "textInputStyle">@style/AppTheme.TextInputLayout
...
style>
<style name="AppTheme.EditTextStyle" parent="Widget.AppCompat.EditText">
- "android:textAlignment"
>viewStart
- "android:textDirection"
>locale
style>
<style name="AppTheme.TextViewStyle" parent="Widget.AppCompat.TextView">
- "android:textDirection"
>locale
style>
<style name="AppTheme.AutoCompleteTextView" parent="Widget.AppCompat.AutoCompleteTextView">
- "android:textAlignment"
>viewStart
- "android:textDirection"
>locale
style>
<style name="AppTheme.TextInputLayout" parent="Widget.Design.TextInputLayout">
- "android:textAlignment"
>viewStart
- "android:textDirection"
>locale
- "hintEnabled">false
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">
- "android:layout_width"
>30dp
- "android:layout_height"
>20dp
- "android:layout_alignParentRight">true
- "android:layout_alignParentEnd">true
- "android:clickable">false
- "android:layout_marginEnd">15dp
- "android:layout_marginStart">15dp
- "android:focusable">false
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.left
topMargin = margin.top
marginEnd = margin.right
bottomMargin = 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
Bannerpublic 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;
}
...
@Override
protected 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() {
@Override
public 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>() {
@Override
public 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() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
PointF point = (PointF) valueAnimator.getAnimatedValue();
view.setTranslationX(point.x);
view.setTranslationY(point.y);
}
});
//在动画结束时去掉动画添加的ImageView
va.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
}
@Override
public void onAnimationEnd(Animator animator) {
root.removeView(view);
}
@Override
public void onAnimationCancel(Animator animator) {
}
@Override
public 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以上,光标和输入内容都是根据输入语言,英文居左,阿语居右。 |
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)