Android阿拉伯适配rtl

概述

阿拉伯作为一个土豪帝国,android在sdk17(Android4.2)也开始支持RTL(right to left)布局了。镜像view等一些设计原则,都可以从官网找到。给出链接传送门
https://material.io/guidelines/usability/bidirectionality.html#bidirectionality-rtl-mirroring-guidelines

RTL布局

rtl布局的判断来自两个部分,一个是系统语言,还有一个就是AndroidManifest.xml中application结点中的supportRtl属性,只有当为true的时候,app才会支持镜像适配。



布局的判断可以使用LayoutDirection.

总结一个函数就是这样的:

/**
* 判断是否进行阿拉伯镜像,判断取决于两个条件:
* 一丶Androidmanifest.xml中的supportsRtl
* 二、当前系统为阿拉伯语 * * @return 是否为rtl */ public static boolean isRTL() { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) { return false; } ApplicationInfo applicationInfo = BaseApplication.getInstance().getApplicationInfo(); boolean hasRtlSupport = (applicationInfo.flags & FLAG_SUPPORTS_RTL) == FLAG_SUPPORTS_RTL; boolean isRtl = TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()) == View.LAYOUT_DIRECTION_RTL; return hasRtlSupport && isRtl; }

如何适配

在如何适配上,可以分几个方面:图标、xml属性、代码set方法、部分控件、自定义控件。

图标

图标大概是最直白的一个。back按钮,喇叭、进度条等等,都得反一下。新建一个drawable-ldrtl-xxhdpi目录统统扔进去。

Android阿拉伯适配rtl_第1张图片
image

Android阿拉伯适配rtl_第2张图片
image

ps:具体的可以参考之前的官网链接,里面讲的很清楚,只提一个重点,那么就是进度条,并不是所有的进度条都需要镜像,如果是多媒体播放的进度条,那么还是遵循从左往右的,例如视频等等,也可以参考youtube、facebook这样的app来看看。

xml属性

对于UI上的适配,大部分处理都是由布局xml来决定的,这里推荐使用Android Studio自带的“RTL Support”来优化。
鼠标右键编辑区域,Refactor->Add RTL Support Where Possible...

Android阿拉伯适配rtl_第3张图片
image
Android阿拉伯适配rtl_第4张图片
image

点击run后,会适配所有left和right相关的属性,一律换成start和end,值得注意一点的是,如果你的app要支持到17以下,那么写属性的时候,需要把两个方法都写上,这样才不会报错。

这里涉及到的相关属性包括:

LTR RTL
layout_marginLeft /layout_marginRight layout_marginStart/layout_marginEnd
paddingLeft/paddingRight paddingStart/paddingEnd
layout_alignLeft/layout_alignRight layout_alignStart/layout_alignEnd
layout_alignParentLeft/layout_alignParentRight layout_alignParentStart/layout_alignParentEnd
layout_toLeftOf/layout_toRightOf layout_toStartOf/layout_toEndOf
drawableLeft/drawableRight drawableStart/drawableEnd

ps:可能还有某些比较偏的属性,就不列出来了。这里还有一个比较特殊的gravity和layout_gravity,当app支持17以下的时候,可以直接使用start/end代替left/right,官方解释是start/end的字节码要大于left/right,所以你可以直接写成

android:gravity="start"

而不用写成

android:gravity="left|start"

还有一个就是textview,阿拉伯文字都是从右往左的,所以在阿拉伯语言下,如果出现英文中文或者数字,有时候还是会显示在左边。这里介绍一个属性“textDirection”。可以自由定义文字从哪一边开始显示。一般可以设置android:textDirection="locale"让它跟着系统语言走。控制view也有相同属性,叫作“layoutDirection”,可以控制view是否镜像(比如不想某些view发生镜像,可以通过设置这个属性来控制)。

======2018.01.17补充======

今天发现,标签里的属性,不会被Refactor->Add RTL Support Where Possible...适配~

代码set方法

这个就很好理解了,xml属性里用到的,代码设置里也需要适配一下。同样,支持17以下的话,需要适配版本,话不多说,直接上代码

RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) view.getLayoutParams();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
   params.setMarginEnd(0);
} else {
   params.rightMargin = 0;
}

这里涉及到的相关方法包括:

LTR RTL
.leftMargin /.rightMargin setMarginStart/setMarginEnd
setPadding setPaddingRelative
setCompoundDrawablesWithIntrinsicBounds setCompoundDrawablesRelativeWithIntrinsicBounds
setCompoundDrawables setCompoundDrawablesRelative

ps:可能有些比较偏的函数,这里也不列出来了。还有就是给recyclerview设置ItemDecoration的时候,left/right要区分RTL。

部分控件

虽然Android说是支持了RTL,但是还是有一些常用的控件,没能做到适配,这里列举我遇到的两个。viewpager和tablayout(tablayout继承HorizontalScrollView)。

viewpager主要就是手势和view child的交互问题,很简单,网上已经有大神给出了解决方案,大致就是对数据源进行反转,上github链接
RtlViewPager。

HorizontalScrollView的问题主要就是,有时候会发生初始状态不在0位置。这里的解决方案是,计算所有child的宽度总和,然后scroll一下就行了。当然,没那么笨,因为HorizontalScrollView里面只能放一个layout,只要计算这个一个layout的宽度就行了。这个问题的原因暂时还没看源码,但是估计是因为,当HorizontalScrollView add完所有的child的时候,会停留在最后,然后执行一波scrollTo(0),让队列回到最前面,但是对于RTL的布局来讲,0位置恰恰就是最后,因此就会出现初始状态不在0位置的问题。

/**
* @author tong.xu
* fix rtl下,tablayout初始化后,不是停留在初始位置的bug
*/
final int scrollX = mTabLayout.getChildAt(0).getMeasuredWidth();
if (CommonUtil.isRTL() && scrollX != 0) {
  mTabLayout.post(new Runnable() {
      @Override public void run() {
        mTabLayout.scrollTo(scrollX, 0);
    }
  });
}

自定义控件

大概这一部分是最难改的了,为什么这么说呢,因为它大概集成了上述所有的问题,并且还要结合自定义控件的逻辑去修改。如果是当初自己写的还稍微好点儿,要是别人写的,还要去读懂实现的逻辑,那是非常耗时费劲的,还容易出bug,所以建议还是谁挖的坑,谁来补(哈哈_,不厚道地笑了)。

简单说说怎么改的入手点吧。首先弄明白rtl和正常的布局有什么不同,比如getLeft(),getX(),X坐标上的变动基本都是要反的,就像之前的HorizontalScrollView,什么滚动啊之类的,都要注意。我来举个最简单的例子,画一个刻度尺。

Android阿拉伯适配rtl_第5张图片
6.png

一格的刻度大小为

int cell = (Screen.width - 2*x) / 2;

ltr:1的x坐标是x,2是x + cell,3是x + 2 * cell
rtl:(这个时候就要从最右算过来)1的x坐标是Screen.width - x,2是Screen.width - x - cell,3是Screen.width - x - 2 * cell

各个值 LTR下的x坐标 RTL的x坐标
1 x Screen.width - x
2 x + cell Screen.width - x - cell
3 x + 2 * cell Screen.width - x - 2 * cell

看起来是不是一点儿也不麻烦,当然我这个只是一个最简单的例子,当各种逻辑混起来的时候,肯定还会复杂,我这里只给一个思路,具体的还是要靠自己去适配了。

补充

如果在warp_content的布局里,放置一个centerInParent的子view,或者是相对应垂直/水平方向的高宽(例如父布局的宽为warp_content,子view使用了centerInHorizontal),在RTL环境下,会导致子view宽高放大,进而影响相邻的view,可能会挤出去,最后看不到。看了一下源码,warp_content在rtl情况下并没有做适配,查看layout的时候,发现宽高变成了65536。。。具体下次研究透了再告诉各位。

总结

总结一下吧,阿拉伯适配对于一个国际化的公司其实还是很重要的,尤其是阿拉伯石油大亨,用户价值还是很高的。适配的操作步骤,按照上述方法一步步下来,先适配简单的,再适配困难的,个别问题再个别处理,一套下来适配个90%基本都是没有问题的。好了,就讲到这里吧!

你可能感兴趣的:(Android阿拉伯适配rtl)