Slide 1

Slide 1 text

From Right to Left and Back @ahmedre | helw.net

Slide 2

Slide 2 text

Why Support RTL?

Slide 3

Slide 3 text

Potential Reach • Arabic: 295 million native speakers • Urdu: 66 million native speakers • Persian: 45 million native speakers Nationalencyklopedin, 2010

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

–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.”

Slide 6

Slide 6 text

RTL Overview

Slide 7

Slide 7 text

Scripts • Arabic script ةيبرعلا • Hebrew script תירִבִע • Other scripts (Syriac, Thaana, etc)

Slide 8

Slide 8 text

RTL • Font • Reshaper support • Layout mirroring • Bidirectional text support (Bidi) د م ح ا دمحأ

Slide 9

Slide 9 text

Android

Slide 10

Slide 10 text

Eclair and Froyo • layout-{ar,…} • no Arabic font, no reshaping

Slide 11

Slide 11 text

Eclair and Froyo

Slide 12

Slide 12 text

Gingerbread • Arabic font support • No reshaping

Slide 13

Slide 13 text

Gingerbread

Slide 14

Slide 14 text

Ice Cream Sandwich • Arabic reshaper! • Arabic localization

Slide 15

Slide 15 text

Ice Cream Sandwich

Slide 16

Slide 16 text

Jellybean • Bidi support - 4.1 • Layout mirroring support, improved fonts - 4.2 • Improved Bidi support - 4.3

Slide 17

Slide 17 text

No content

Slide 18

Slide 18 text

Kitkat • Drawable Mirroring • Force RTL

Slide 19

Slide 19 text

No content

Slide 20

Slide 20 text

RTL on API 17+

Slide 21

Slide 21 text

android:supportsRTL otherwise, getLayoutDirection will always return LTR

Slide 22

Slide 22 text

android:layoutDirection • LAYOUT_DIRECTION_LTR • LAYOUT_DIRECTION_RTL • LAYOUT_DIRECTION_INHERIT • LAYOUT_DIRECTION_LOCALE

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

Mirroring Layouts

Slide 26

Slide 26 text

Slide 27

Slide 27 text

Slide 28

Slide 28 text

Slide 29

Slide 29 text

Slide 30

Slide 30 text

Mirroring Layouts • RelativeLayout parameters • layout_alignParentStart, layout_alignParentEnd • layout_alignStart,layout_alignEnd • layout_toStartOf, layout_toEndOf

Slide 31

Slide 31 text

Mirroring Layouts • ldrtl / ldltr resource qualifiers • lower priority than -lang • useful for mirroring drawables

Slide 32

Slide 32 text

Building Android Components

Slide 33

Slide 33 text

Supporting Gravity in ViewGroups

Slide 34

Slide 34 text

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 }

Slide 35

Slide 35 text

Gravity.getAbsoluteGravity( gravity, layoutDirection);

Slide 36

Slide 36 text

android:gravity start end right left absolute gravity left right LTR

Slide 37

Slide 37 text

android:gravity start end right left absolute gravity left right RTL

Slide 38

Slide 38 text

gravity = Gravity.getAbsoluteGravity( gravity, getLayoutDirection()); switch (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) { case Gravity.RIGHT: break; case Gravity.LEFT: default: break; }

Slide 39

Slide 39 text

Top / Start

Slide 40

Slide 40 text

Bottom / End

Slide 41

Slide 41 text

Center

Slide 42

Slide 42 text

Center Vertical / Start

Slide 43

Slide 43 text

Gravity.apply( gravity, w, h, container, outRect, layoutDirection);

Slide 44

Slide 44 text

Gravity.apply( gravity, w, h, container, outRect, layoutDirection); gravity of whatever we want to position, ex Gravity.START | Gravity.TOP

Slide 45

Slide 45 text

Gravity.apply( gravity, w, h, container, outRect, layoutDirection); width and height of whatever we’re trying to position

Slide 46

Slide 46 text

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.

Slide 47

Slide 47 text

Gravity.apply( gravity, w, h, container, outRect, layoutDirection); the frame of the item we’re trying to place

Slide 48

Slide 48 text

Gravity.apply( gravity, w, h, container, outRect, layoutDirection); the layout direction, for translating start and end

Slide 49

Slide 49 text

Gravity.apply( gravity, w, h, container, outRect, layoutDirection); child.layout(outRect.left, outRect.top, outRect.right, outRect.bottom);

Slide 50

Slide 50 text

Gravity.apply( gravity, w, h, container, outRect, layoutDirection); drawable.setBounds(outRect);

Slide 51

Slide 51 text

Reverse ordering of Children for RTL

Slide 52

Slide 52 text

No content

Slide 53

Slide 53 text

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); // ... }

Slide 54

Slide 54 text

Handling RTL for Views

Slide 55

Slide 55 text

No content

Slide 56

Slide 56 text

@Override protected void onDraw(Canvas canvas) { canvas.drawRect(0, 0, 100, 100, paint); }

Slide 57

Slide 57 text

No content

Slide 58

Slide 58 text

@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(); } }

Slide 59

Slide 59 text

Framework

Slide 60

Slide 60 text

setAutoMirrored • Introduced in Kitkat (19+) • setAutoMirrored / android:autoMirrored • BitmapDrawable uses it to flip the bitmap

Slide 61

Slide 61 text

ViewPager • no RTL support • reverse the order of the pages and map indices

Slide 62

Slide 62 text

Supporting RTL pre-4.2

Slide 63

Slide 63 text

Older APIs • no layout-ldrtl / layout-ldltr • have to fallback to layout-ar, layout-he, …

Slide 64

Slide 64 text

Example

Slide 65

Slide 65 text

4.1 4.2

Slide 66

Slide 66 text

4.1 4.2

Slide 67

Slide 67 text

layout-ar

Slide 68

Slide 68 text

4.1 4.2

Slide 69

Slide 69 text

Solutions • android:layoutDirection=“ltr” • alternative: resource mirrors layout-ar-v17

Slide 70

Slide 70 text

layout-ar

Slide 71

Slide 71 text

No content

Slide 72

Slide 72 text

• for certain text pre-17, may need to explicitly set the gravity. • GravityCompat and ViewCompat are your friends

Slide 73

Slide 73 text

Older APIs - pre-14 • include a font capable of rendering the language • bundle your own reshaper • be wary of custom vendor solutions

Slide 74

Slide 74 text

No content

Slide 75

Slide 75 text

Common Problems

Slide 76

Slide 76 text

RelativeLayout 
 


Slide 77

Slide 77 text

4.3 - api 18 4.2 - api 17

Slide 78

Slide 78 text

4.3 - api 18 4.2 - api 17

Slide 79

Slide 79 text

RelativeLayout • same problem with right/end • Fixed in 4.3 (api 18) • layout-v17 with android:alignParentStart

Slide 80

Slide 80 text

Text

Slide 81

Slide 81 text

Character Types • Strong a b ت ب • Weak + - : . , • Neutral whitespace • Explicit Formatting Characters

Slide 82

Slide 82 text

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

Slide 83

Slide 83 text

Text Direction • default is TEXT_DIRECTION_INHERIT • ViewRootImpl sets this to TEXT_DIRECTION_FIRST_STRONG

Slide 84

Slide 84 text

No content

Slide 85

Slide 85 text

Slide 86

Slide 86 text

@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)); }

Slide 87

Slide 87 text

No content

Slide 88

Slide 88 text

@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)); }

Slide 89

Slide 89 text

No content

Slide 90

Slide 90 text

@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)); }

Slide 91

Slide 91 text

No content

Slide 92

Slide 92 text

Tips

Slide 93

Slide 93 text

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

Slide 94

Slide 94 text

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

Slide 95

Slide 95 text

No content

Slide 96

Slide 96 text

Thanks! @ahmedre / helw.net