Upgrade to Pro — share decks privately, control downloads, hide ads and more …

From Right to Left and Back

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.

From Right to Left and Back

Talk given at Droidcon SF 2016 about implementing RTL on Android.

Some of the topics discussed:
- overview of RTL
- how to implement RTL pre and post api-17
- implementing RTL in custom Views, ViewGroups, and Drawables
- common pitfalls and problems and how to solve them

credit for images:
- android flavor versions: https://www.android.com/history
- various meems: https://imgflip.com/memegenerator

Avatar for Ahmed El-Helw

Ahmed El-Helw

March 17, 2016
Tweet

More Decks by Ahmed El-Helw

Other Decks in Programming

Transcript

  1. Potential Reach • Arabic: 295 million native speakers • Urdu:

    66 million native speakers • Persian: 45 million native speakers Nationalencyklopedin, 2010
  2. Statistics - Q1 2015 • 95% of phones shipped in

    MEA are iOS / Android • shipments of iOS/Android phones “increased by a combined 67% year on year” IDC Press Release, July 12th, 2015
  3. –IDC Press Release, July 12th, 2015 “In the Middle East,

    Android currently represents 80% of market's volume, while iOS accounts for 17%; in Africa, these figures stand at 89% and 7%, respectively.”
  4. RTL • Font • Reshaper support • Layout mirroring •

    Bidirectional text support (Bidi) د م ح ا دمحأ
  5. Jellybean • Bidi support - 4.1 • Layout mirroring support,

    improved fonts - 4.2 • Improved Bidi support - 4.3
  6. Resolving Layout Direction • android:supportsRTL - if not, return LTR

    • based on android:layoutDirection • LAYOUT_DIRECTION_RTL or LAYOUT_DIRECTION_LTR • LAYOUT_DIRECTION_LOCALE • LAYOUT_DIRECTION_INHERIT
  7. Resolving Layout Direction • for the inherit case, ViewRootImpl sets

    top level direction to use Locale. • resolving Locale happens in TextUtils (17+) • TextUtils gets information from ICU
  8. Mirroring Layouts • ldrtl / ldltr resource qualifiers • lower

    priority than -lang • useful for mirroring drawables
  9. if ((gravity & RELATIVE_LAYOUT_DIRECTION) > 0) { if ((gravity &

    Gravity.START) == Gravity.START) { if (layoutDirection == View.LAYOUT_DIRECTION_RTL) { // start resolving to right } else { // start resolving to left } } else if ((gravity & Gravity.END) == Gravity.END) { if (layoutDirection == View.LAYOUT_DIRECTION_RTL) { // end resolving to left } else { // end resolving to right } } } else if ((gravity & Gravity.LEFT) == Gravity.LEFT) { // left } else if ((gravity & Gravity.RIGHT) == Gravity.RIGHT) { // right }
  10. Gravity.apply( gravity, w, h, container, outRect, layoutDirection); a Rect in

    which the item will be positioned. should be at least as large as the item.
  11. LinearLayout int start = 0; int dir = 1; //

    In case of RTL, start drawing from the last child.
 if (isLayoutRtl) { start = count - 1; dir = -1;
 } 
 for (int i = 0; i < count; i++) { int childIndex = start + dir * i; final View child = getVirtualChildAt(childIndex); // ... }
  12. @Override protected void onDraw(Canvas canvas) { boolean isRtl = getLayoutDirection()

    == LAYOUT_DIRECTION_RTL; if (isRtl) { canvas.save(); canvas.translate(getWidth(), 0); canvas.scale(-1.0f, 1.0f); } canvas.drawRect(0, 0, 100, 100, paint); if (isRtl) { canvas.restore(); } }
  13. Older APIs • no layout-ldrtl / layout-ldltr • have to

    fallback to layout-ar, layout-he, …
  14. Example <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="120dp"> <TextView

    android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:text="@string/two" android:background="#E3F2FD"/> </LinearLayout> <TextView android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:text="@string/one" android:background="#E8F5E9"/>
  15. layout-ar <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="120dp"> <TextView

    android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:text="@string/two" android:background="#E3F2FD"/> </LinearLayout> <TextView android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:text="@string/one" android:background="#E8F5E9"/>
  16. layout-ar <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="120dp" android:layout_direction="ltr">

    <TextView android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:text="@string/one" android:background="#E8F5E9"/> </LinearLayout> <TextView android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:text="@string/two" android:background="#E3F2FD"/>
  17. • for certain text pre-17, may need to explicitly set

    the gravity. • GravityCompat and ViewCompat are your friends
  18. Older APIs - pre-14 • include a font capable of

    rendering the language • bundle your own reshaper • be wary of custom vendor solutions
  19. RelativeLayout <?xml version="1.0" encoding="utf-8"?>
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
 android:layout_height="match_parent"> <Button android:layout_width="wrap_content"

    android:layout_height="wrap_content" android:text="@string/app_name" android:layout_alignParentLeft="true" android:layout_alignParentStart="true"/>
 </RelativeLayout>
  20. RelativeLayout • same problem with right/end • Fixed in 4.3

    (api 18) • layout-v17 with android:alignParentStart
  21. Character Types • Strong a b ت ب • Weak

    + - : . , • Neutral whitespace • Explicit Formatting Characters
  22. Text Direction • TEXT_DIRECTION_INHERIT • TEXT_DIRECTION_FIRST_STRONG • TEXT_DIRECTION_ANY_RTL • TEXT_DIRECTION_LTR

    • TEXT_DIRECTION_RTL • TEXT_DIRECTION_LOCALE • TEXT_DIRECTION_FIRST_STRONG_LTR • TEXT_DIRECTION_FIRST_STRONG_RTL • TEXT_DIRECTION_INHERIT • TEXT_DIRECTION_FIRST_STRONG • TEXT_DIRECTION_ANY_RTL • TEXT_DIRECTION_LTR • TEXT_DIRECTION_RTL • TEXT_DIRECTION_LOCALE • TEXT_DIRECTION_FIRST_STRONG_LTR • TEXT_DIRECTION_FIRST_STRONG_RTL
  23. @Override protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState); setContentView(R.layout.rtl_text); // Ahmed

    in English, ﺪﻤﺣأ in Arabic String name = getString(R.string.name); String s = getString(R.string.name_format); TextView tv = (TextView) findViewById(R.id.first); tv.setText(String.format(s, name, 3)); }
  24. @Override protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState); setContentView(R.layout.rtl_text); // Ahmed

    in Arabic, ﺪﻤﺣأ in English String name = getString(R.string.name_opposite); String s = getString(R.string.name_format); TextView tv = (TextView) findViewById(R.id.first); tv.setText(String.format(s, name, 3)); }
  25. @Override protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState); setContentView(R.layout.rtl_text); // Ahmed

    in Arabic, ﺪﻤﺣأ in English String name = getString(R.string.name_opposite); String s = getString(R.string.name_format); BidiFormatter formatter = BidiFormatter.getInstance();
 name = formatter.unicodeWrap(name); TextView tv = (TextView) findViewById(R.id.first); tv.setText(String.format(s, name, 3)); }
  26. Tips • android:supportsRTL=“true” • for layout parameters, add START and

    END in addition to RIGHT and LEFT • be careful when mixing RTL and LTR text
  27. Tips • Be wary when using the default locale •

    ship RTL to 17+ to save a lot of work • test layouts and views in RTL and LTR on various api versions