阿拉伯語RTL適配坑點(diǎn)

適配EditText,TextView的RTL模式主要屬性

image.png

為了更精確地控制應(yīng)用程序在UI上的文字書寫順序(從左到右闪檬,或者從右到左)罐寨,Android 4.2 引入了如下的API:

android:layoutDirection —該屬性設(shè)置組件的布局排列方向

android:textDirection — 該屬性設(shè)置組件的文字排列方向

android:textAlignment — 該屬性設(shè)置文字的對(duì)齊方式

getLayoutDirectionFromLocale() —該方法用于獲取指定地區(qū)的慣用布局方式

android:layoutDirection 參數(shù)詳解

該參數(shù)設(shè)置在ViewGroup中,用于排列子View的方向。

image.png

android:textAlignment 參數(shù)解釋

屬性 變量值 描述
inherit 0 Default
gravity 1 Default for the root view. The gravity determines the alignment, ALIGN_NORMAL, ALIGN_CENTER, or ALIGN_OPPOSITE, which are relative to each paragraph’s text direction
textStart 2 Align to the start of the paragraph, e.g. ALIGN_NORMAL.
textEnd 3 Align to the end of the paragraph, e.g. ALIGN_OPPOSITE.
center 4 Center the paragraph, e.g. ALIGN_CENTER.
viewStart 5 Align to the start of the view, which is ALIGN_LEFT if the view’s resolved layoutDirection is LTR, and ALIGN_RIGHT otherwise.
viewEnd 6 Align to the end of the view, which is ALIGN_RIGHT if the view’s resolved layoutDirection is LTR, and ALIGN_LEFT otherwise
屬性 變量值 描述
inherit 0 默認(rèn)
gravity 1 根視圖的默認(rèn)值。重力確定對(duì)齊,ALIGN_NORMAL盒犹,ALIGN_CENTER或ALIGN_OPPOSITE,它們相對(duì)于每個(gè)段落的文本方向text direction
textStart 2 與段落的開頭對(duì)齊眨业,例如ALIGN_NORMAL
textEnd 3 與段落末尾對(duì)齊急膀,例如ALIGN_OPPOSITE
center 4 與段落居中,例如居中對(duì)齊龄捡。
viewStart 5 與視圖的開頭對(duì)齊卓嫂,如果視圖的已解析layoutDirection為LTR,則為ALIGN_LEFT聘殖,否則為ALIGN_RIGHT
viewEnd 6 對(duì)齊視圖的末尾晨雳,如果視圖的已解析的layoutDirection為LTR,則為ALIGN_RIGHT奸腺,否則為ALIGN_LEFT

android:textDirection 參數(shù)解釋

android:textDirection="rtl" 強(qiáng)制某個(gè)布局如EditText(TextView)的文字排列方向?yàn)閺挠业阶?/p>

textAlignment 和 gravity的區(qū)別

查看TextView的源碼:gravity一般是配合textAlignment一起使用的:

private Layout.Alignment getLayoutAlignment() {
    // ...
    case TEXT_ALIGNMENT_VIEW_START:
        alignment = (getLayoutDirection() == LAYOUT_DIRECTION_RTL)? Layout.Alignment.ALIGN_RIGHT : Layout.Alignment.ALIGN_LEFT;
        break;
    case TEXT_ALIGNMENT_VIEW_END:
        alignment = (getLayoutDirection() == LAYOUT_DIRECTION_RTL)? Layout.Alignment.ALIGN_LEFT : Layout.Alignment.ALIGN_RIGHT;
        break;
}

兩個(gè)屬性共同決定了alignment 對(duì)齊方式餐禁。

image.png

android:textDirection 和 android:textAlignment 的區(qū)別

textDirection是指文字的方向,只有中文和阿拉伯語的時(shí)候才能看出區(qū)別突照,
如:我愛他
阿拉伯:他愛我

EditText 詭異現(xiàn)象的重現(xiàn):

   <EditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="content"
            android:layout_marginStart="20dp"
            android:layout_marginLeft="20dp"
            android:textDirection="rtl"
    />

顯示樣式如下:

image.png

RTL模式判斷方式

通過TextView的源碼找到一個(gè)有用的方法:

alignment = (getLayoutDirection() == LAYOUT_DIRECTION_RTL)? Layout.Alignment.ALIGN_RIGHT : Layout.Alignment.ALIGN_LEFT;

如下:

android.view.View.getLayoutDirection

也可以使用下面的工具方法判斷:

public static boolean isRtlMode(Context context) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
        Configuration config = context.getResources().getConfiguration();
        if (config.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
            return true;
        }
    }
    return false;
}

判斷RTL然后針對(duì)性處理:

if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
  if (DynamicLanguage.getLayoutDirection(getContext()) == View.LAYOUT_DIRECTION_RTL) {
    this.textView.setGravity(Gravity.RIGHT);
  } else {
    this.textView.setGravity(Gravity.LEFT);
  }
}

自定義View的處理:

public class LocaleAwareTextView extends TextView {
    public LocaleAwareTextView(Context context) {
        super(context);
        setGravity(getResources().getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? Gravity.RIGHT : Gravity.LEFT);
    }
    public LocaleAwareTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setGravity(getResources().getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? Gravity.RIGHT : Gravity.LEFT);
    }

    public LocaleAwareTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setGravity(getResources().getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? Gravity.RIGHT : Gravity.LEFT);
    }

}

代碼中動(dòng)態(tài)設(shè)置設(shè)置屬性:

if ("ar" == Locale.getDefault().language) {
            window.decorView.layoutDirection = View.LAYOUT_DIRECTION_RTL
}

開發(fā)建議方案

  1. 從基礎(chǔ)類開始入手帮非,判斷是否是阿拉伯語,如果是需要將界面設(shè)置為從右到左的顯示方式
  2. 分模塊進(jìn)行適配
  3. 復(fù)雜的模塊,可以放到 layout-ldrtl 包下末盔,單獨(dú)做一個(gè)布局來適配阿拉伯語筑舅,例如詳情頁
  4. 創(chuàng)建單獨(dú)的資源文件夾,以’ldrtl’(layout direction right-to-left)為后綴.如layout_ldrtl

如何快捷測試

  1. 開發(fā)人員選項(xiàng)中有一個(gè)方便的工具強(qiáng)制任何語言的RTL布局方向,設(shè)置的名稱和位置因手機(jī)而異庄岖,在我的設(shè)備中稱為“強(qiáng)制RTL布局方向”豁翎。

  2. 代碼控制語言

public class RTLHelper {

    public static void setRTL(Context context) {
        Locale locale = new Locale("ar");
        Resources resources = context.getResources();
        Configuration config = resources.getConfiguration();
        config.locale = locale;
        if (Build.VERSION.SDK_INT >= 17) {
            config.setLayoutDirection(locale);
        }
        resources.updateConfiguration(config, resources.getDisplayMetrics());
    }
}

ConstraintLayout約束布局的坑點(diǎn)一

ConstraintLayout中GuideLine是一個(gè)非常好用的控件,我形容它為“隱身的監(jiān)管者”隅忿。在使用它時(shí)出現(xiàn)了一個(gè)問題心剥,它的app:layout_constraintGuide_percent屬性無法適配rtl,所以當(dāng)我們切換將手機(jī)語言設(shè)置切換阿拉伯語或者強(qiáng)制設(shè)置為TRL時(shí)背桐,UI的變化就不是我們預(yù)期的了优烧。(新版已修復(fù)GuideLine問題)

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ThirdActivity">

    <android.support.constraint.Guideline
        android:id="@+id/guideline"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintGuide_percent="0.4" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="8dp"
        android:layout_marginStart="20dp"
        android:layout_marginTop="8dp"
        android:text="Button"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="@+id/guideline"
        app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>

在屏幕左側(cè)40%處添加一個(gè)豎直GuideLine,在GuideLine右側(cè)20dp處添加一個(gè)button链峭,效果如下:

image.png

當(dāng)切換RTL時(shí)畦娄,結(jié)果為:

image.png

解決方案如下:

既然系統(tǒng)無法為我們自動(dòng)適配,那么就需要手動(dòng)適配RTL:

values/dimens.xml:

 <?xml version="1.0" encoding="utf-8"?>
<resources>
    <item name="guideline_percent" format="float" type="dimen">0.4</item>
</resources>

values-ldrtl/dimens.xml:

 <?xml version="1.0" encoding="utf-8"?>
<resources>
    <item name="guideline_percent" format="float" type="dimen">0.6</item>
</resources>

ConstraintLayout約束布局的坑點(diǎn)二

約束布局【首元素】未設(shè)置約束導(dǎo)致的RTL出現(xiàn)Bug

重現(xiàn)

ConstraintLayout如果使用 layout_constraintLeft_toLeftOf, 那么RTL布局不會(huì)導(dǎo)致子View轉(zhuǎn)向.
且設(shè)置鏈條的時(shí)候發(fā)現(xiàn)
app:layout_constraintHorizontal_chainStyle="packed" 會(huì)導(dǎo)致屬性失效(看起來好像此屬性被反過來一樣, 其實(shí)是恢復(fù)成了默認(rèn)值)

layout_constraintStart_toStartOf 則正常弊仪,且layout_constraintHorizontal_chainStyle也是正常的熙卡,不需要兩頭都設(shè)置一次。

不能偷懶的Bug励饵,第一個(gè)控件必須設(shè)置left左邊的約束驳癌,否則RTL后,因?yàn)樽筮厸]有約束役听,導(dǎo)致布局亂掉颓鲜。其他相對(duì)于第一個(gè)的還按照原來的設(shè)計(jì)即可。

重現(xiàn)方式:

image.png

RTL顯示:


image.png

如何解決

image.png

ConstraintLayout約束布局的坑點(diǎn)三

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layoutDirection="locale"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/tv1"
        android:text="sss1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="16dp"
        android:background="#fcc"
        android:textAppearance="@style/TextAppearance.AppCompat.Display2"

        app:layout_constraintStart_toStartOf="parent"
        />

    <!--
        此種錯(cuò)誤的原因也是: 布局1依賴布局2(其中的layout_constraintEnd_toStartOf約束),
        而布局2又依賴布局1(layout_constraintStart_toEndOf布局2似乎完全懸空), 所以不知道怎么繪制了.
        這個(gè)非常類似View的測量, ViewGroup依賴View的測量,而View又依賴ViewGroup的測量,導(dǎo)致測量不準(zhǔn)確

        錯(cuò)誤1: LTR, 修改width = 0dp, tv2完全消失
        錯(cuò)誤2: RTL, 布局亂掉

        修正: 刪除tv2的約束layout_constraintEnd_toStartOf即可.
    -->

    <Button
        android:id="@+id/tv2"
        android:text="sss2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="16dp"
        android:background="#f00"
        android:textAppearance="@style/TextAppearance.AppCompat.Display2"

        app:layout_constraintStart_toEndOf="@id/tv1"
        app:layout_constraintEnd_toStartOf="@id/tv3"
        />

    <Button
        android:id="@+id/tv3"
        android:text="sss3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#cff"
        android:textAppearance="@style/TextAppearance.AppCompat.Display2"

        app:layout_constraintStart_toEndOf="@id/tv2"
        />

</android.support.constraint.ConstraintLayout>

上面布局的預(yù)覽圖:

image.png

錯(cuò)誤1:重現(xiàn)圖

image.png

錯(cuò)誤2:重現(xiàn)圖

image.png

修正圖:

image.png

英阿混用文案場景說明

  1. 不管是LTR還是RTL, 阿拉伯語在Android的顯示都會(huì)倒置過來.
  2. RTL中, 一系列的英文單詞會(huì)被整體看成一個(gè)阿拉伯語字符看待.
  3. RTL中, 英文標(biāo)點(diǎn)符號(hào)會(huì)被當(dāng)做阿拉伯語字符, 一個(gè)符號(hào)代表一個(gè)字符. 除非英文標(biāo)點(diǎn)符號(hào)位于英文單詞【之間】典予。前后的則仍然當(dāng)做阿拉伯語.
  4. LTR中, 顯示順序和我們閱讀順序一致, 但是阿拉伯語會(huì)被當(dāng)做一個(gè)整體(倒置,在阿拉伯人看來是正確的順序).和上面2同理.

Bidi算法

在雙向字符集語言中甜滨,標(biāo)點(diǎn)符號(hào)的處理是 BiDi 算法中一個(gè)需要特別關(guān)注的地方。在 BiDi 中瘤袖,所有的非標(biāo)點(diǎn)符號(hào)被稱為“強(qiáng)”字符衣摩。而標(biāo)點(diǎn)符號(hào)既可以是從左向右 LTR 也可以是從右向左 RTL。因?yàn)椴缓魏蔚姆较蛐畔⑽娴校员环Q為“弱”字符昭娩。通常是由軟件根據(jù) BiDi 算法來決定標(biāo)點(diǎn)符號(hào)放置的方向。

在 BiDi 算法中黍匾,如果標(biāo)點(diǎn)符號(hào)放在兩段有相同方向文字的中間,標(biāo)點(diǎn)符號(hào)將繼承相同的方向呛梆。如果標(biāo)點(diǎn)符號(hào)放在兩段有不同方向的文字中間锐涯,標(biāo)點(diǎn)符號(hào)將繼承全局方向。

當(dāng)需要輸入英文字符的時(shí)候填物,計(jì)算機(jī)將自動(dòng)處理英文字符的顯示纹腌,將先輸入的字符自動(dòng)向左邊排霎终,后輸入的字符顯示在前面字符的右側(cè),將先輸入的文字頂?shù)搅俗髠?cè)升薯,而錄入光標(biāo)將一直停留在英文錄入的最右側(cè)莱褒,依次處理隨后的文字錄入,并顯示涎劈。這樣錄入者就不用關(guān)心這段英文文字將占據(jù)多大空間广凸,而且英文內(nèi)容保持了從左到右(LTR)的方向。 當(dāng)用戶需要輸入阿拉伯文字的時(shí)候蛛枚,阿拉伯字符將自動(dòng)放置到英文內(nèi)容的左側(cè)谅海,錄入光標(biāo)也跟隨到了阿拉伯字符的左側(cè),開始正常的從右到左(RTL)的錄入蹦浦,并顯示扭吁。

官方指南

https://material.io/design/usability/bidirectionality.html#implementation

參考

https://segmentfault.com/a/1190000003781294#articleHeader2
https://www.cnblogs.com/dojo-lzz/p/4289423.html
https://blog.csdn.net/candyguy242/article/details/8476093

還有很多未知的坑...

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市盲镶,隨后出現(xiàn)的幾起案子侥袜,更是在濱河造成了極大的恐慌,老刑警劉巖溉贿,帶你破解...
    沈念sama閱讀 221,273評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件枫吧,死亡現(xiàn)場離奇詭異,居然都是意外死亡顽照,警方通過查閱死者的電腦和手機(jī)由蘑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來代兵,“玉大人尼酿,你說我怎么就攤上這事≈灿埃” “怎么了裳擎?”我有些...
    開封第一講書人閱讀 167,709評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長思币。 經(jīng)常有香客問我鹿响,道長,這世上最難降的妖魔是什么谷饿? 我笑而不...
    開封第一講書人閱讀 59,520評(píng)論 1 296
  • 正文 為了忘掉前任惶我,我火速辦了婚禮,結(jié)果婚禮上博投,老公的妹妹穿的比我還像新娘绸贡。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,515評(píng)論 6 397
  • 文/花漫 我一把揭開白布听怕。 她就那樣靜靜地躺著捧挺,像睡著了一般。 火紅的嫁衣襯著肌膚如雪尿瞭。 梳的紋絲不亂的頭發(fā)上闽烙,一...
    開封第一講書人閱讀 52,158評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音声搁,去河邊找鬼黑竞。 笑死,一個(gè)胖子當(dāng)著我的面吹牛酥艳,可吹牛的內(nèi)容都是我干的摊溶。 我是一名探鬼主播,決...
    沈念sama閱讀 40,755評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼充石,長吁一口氣:“原來是場噩夢啊……” “哼莫换!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起骤铃,我...
    開封第一講書人閱讀 39,660評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤拉岁,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后惰爬,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體喊暖,經(jīng)...
    沈念sama閱讀 46,203評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,287評(píng)論 3 340
  • 正文 我和宋清朗相戀三年撕瞧,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了陵叽。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,427評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡丛版,死狀恐怖巩掺,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情页畦,我是刑警寧澤胖替,帶...
    沈念sama閱讀 36,122評(píng)論 5 349
  • 正文 年R本政府宣布,位于F島的核電站豫缨,受9級(jí)特大地震影響独令,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜好芭,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,801評(píng)論 3 333
  • 文/蒙蒙 一燃箭、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧舍败,春花似錦招狸、人聲如沸碗硬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至弛说,卻和暖如春挽懦,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背木人。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評(píng)論 1 272
  • 我被黑心中介騙來泰國打工信柿, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人醒第。 一個(gè)月前我還...
    沈念sama閱讀 48,808評(píng)論 3 376
  • 正文 我出身青樓渔嚷,卻偏偏與公主長得像,于是被迫代替她去往敵國和親稠曼。 傳聞我的和親對(duì)象是個(gè)殘疾皇子形病,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,440評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • 該文章已歸納在本人 GitHub 的 Blog 倉庫漠吻,歡迎 star 或者 watch。同時(shí)歡迎訪問我的博客地址 ...
    GoJun閱讀 30,815評(píng)論 6 34
  • iOS,移動(dòng)開發(fā),國際化,RTL,阿拉伯語公司項(xiàng)目因?yàn)橐m配中東市場,也算是做了不少RTL了,但是每次基本上開發(fā)結(jié)...
    DingGa閱讀 10,407評(píng)論 3 10
  • 概述 阿拉伯作為一個(gè)土豪帝國司恳,android在sdk17(Android4.2)也開始支持RTL(right to...
    Android深夜食堂閱讀 9,294評(píng)論 9 7
  • 最近項(xiàng)目適配阿拉伯途乃,記錄一下最近的工作內(nèi)容。在此之前扔傅,我是沒有了解過這方面的知識(shí)耍共。首先說說為什么要適配阿拉伯呢,是...
    ymhlbj閱讀 13,343評(píng)論 6 23
  • Android 在4.2系統(tǒng)中猎塞,已經(jīng)原生支持文字由右到左排序试读,新的AS,也支持預(yù)覽邢享。從右到左的書寫格式主要是阿拉伯...
    Galileo_404閱讀 3,450評(píng)論 0 1