YorkShen closed pull request #1664: [WEEX-653][android][iOS][core] Total 
platform support rtl direction by CSS "direction:rtl"
URL: https://github.com/apache/incubator-weex/pull/1664
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git 
a/android/playground/app/src/main/java/com/alibaba/weex/extend/component/WXMask.java
 
b/android/playground/app/src/main/java/com/alibaba/weex/extend/component/WXMask.java
index 778404c3cc..252de1c56f 100644
--- 
a/android/playground/app/src/main/java/com/alibaba/weex/extend/component/WXMask.java
+++ 
b/android/playground/app/src/main/java/com/alibaba/weex/extend/component/WXMask.java
@@ -124,7 +124,9 @@ protected void setHostLayoutParams(View host, int width, 
int height, int left, i
     top = get(TOP);
     bottom = get(BOTTOM);
     FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(width, 
height);
-    params.setMargins(left, top, right, bottom);
+
+    this.setMarginsSupportRTL(params, left, top, right, bottom);
+
     getHostView().setLayoutParams(params);
   }
 
diff --git a/android/sdk/libs/armeabi-v7a/libweexcore.so 
b/android/sdk/libs/armeabi-v7a/libweexcore.so
old mode 100755
new mode 100644
index 08a3751a9c..4b1cac43ff
Binary files a/android/sdk/libs/armeabi-v7a/libweexcore.so and 
b/android/sdk/libs/armeabi-v7a/libweexcore.so differ
diff --git a/android/sdk/libs/armeabi/libweexcore.so 
b/android/sdk/libs/armeabi/libweexcore.so
old mode 100755
new mode 100644
index a88a0fffb8..1be8d2be56
Binary files a/android/sdk/libs/armeabi/libweexcore.so and 
b/android/sdk/libs/armeabi/libweexcore.so differ
diff --git a/android/sdk/libs/x86/libJavaScriptCore.so 
b/android/sdk/libs/x86/libJavaScriptCore.so
new file mode 100644
index 0000000000..0e0f9f5ed7
Binary files /dev/null and b/android/sdk/libs/x86/libJavaScriptCore.so differ
diff --git a/android/sdk/libs/x86/libweexcore.so 
b/android/sdk/libs/x86/libweexcore.so
old mode 100755
new mode 100644
index 9955dc3fca..e4c3a3b8fa
Binary files a/android/sdk/libs/x86/libweexcore.so and 
b/android/sdk/libs/x86/libweexcore.so differ
diff --git a/android/sdk/libs/x86/libweexjss.so 
b/android/sdk/libs/x86/libweexjss.so
old mode 100755
new mode 100644
index ac0dd7f261..3733a2a383
Binary files a/android/sdk/libs/x86/libweexjss.so and 
b/android/sdk/libs/x86/libweexjss.so differ
diff --git a/android/sdk/libs/x86/libweexjst.so 
b/android/sdk/libs/x86/libweexjst.so
new file mode 100644
index 0000000000..84add1ebda
Binary files /dev/null and b/android/sdk/libs/x86/libweexjst.so differ
diff --git a/android/sdk/src/main/java/com/taobao/weex/WXEnvironment.java 
b/android/sdk/src/main/java/com/taobao/weex/WXEnvironment.java
index 13564ff86b..cf4c7b2adb 100644
--- a/android/sdk/src/main/java/com/taobao/weex/WXEnvironment.java
+++ b/android/sdk/src/main/java/com/taobao/weex/WXEnvironment.java
@@ -157,6 +157,13 @@
     configs.put(WXConfig.sysModel, SYS_MODEL);
     configs.put(WXConfig.weexVersion, String.valueOf(WXSDK_VERSION));
     configs.put(WXConfig.logLevel,sLogLevel.getName());
+
+    try {
+      configs.put(WXConfig.layoutDirection, isLayoutDirectionRTL() ? "rtl" : 
"ltr");
+    } catch (Exception e) {
+      configs.put(WXConfig.layoutDirection, "ltr");
+    }
+
     try {
       if (isApkDebugable()) {
         options.put(WXConfig.debugMode, "true");
@@ -225,6 +232,13 @@ public static boolean isSupport() {
     return isHardwareSupport() && isInitialized;
   }
 
+  public static boolean isLayoutDirectionRTL() {
+    // support RTL
+    if (android.os.Build.VERSION.SDK_INT >= 
android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
+      return 
sApplication.getApplicationContext().getResources().getBoolean(R.bool.weex_is_right_to_left);
+    }
+    return false;
+  }
   /**
    * Tell whether Weex can run on current hardware.
    * @return true if weex can run on current hardware, otherwise false.
diff --git a/android/sdk/src/main/java/com/taobao/weex/WXSDKInstance.java 
b/android/sdk/src/main/java/com/taobao/weex/WXSDKInstance.java
index cfa87b7e66..0962db545f 100644
--- a/android/sdk/src/main/java/com/taobao/weex/WXSDKInstance.java
+++ b/android/sdk/src/main/java/com/taobao/weex/WXSDKInstance.java
@@ -1664,6 +1664,13 @@ public int getRenderContainerPaddingLeft() {
     return 0;
   }
 
+  public int getRenderContainerPaddingRight() {
+    if(mRenderContainer != null) {
+      return mRenderContainer.getPaddingRight();
+    }
+    return 0;
+  }
+
   public int getRenderContainerPaddingTop() {
     if(mRenderContainer != null) {
       return mRenderContainer.getPaddingTop();
diff --git 
a/android/sdk/src/main/java/com/taobao/weex/bridge/WXBridgeManager.java 
b/android/sdk/src/main/java/com/taobao/weex/bridge/WXBridgeManager.java
index 531b601133..cbe68aebff 100644
--- a/android/sdk/src/main/java/com/taobao/weex/bridge/WXBridgeManager.java
+++ b/android/sdk/src/main/java/com/taobao/weex/bridge/WXBridgeManager.java
@@ -1975,6 +1975,7 @@ private WXParams assembleDefaultOptions() {
     wxParams.setDeviceModel(config.get(WXConfig.sysModel));
     wxParams.setShouldInfoCollect(config.get("infoCollect"));
     wxParams.setLogLevel(config.get(WXConfig.logLevel));
+    wxParams.setLayoutDirection(config.get(WXConfig.layoutDirection));
     wxParams.setUseSingleProcess(isUseSingleProcess ? "true" : "false");
     
wxParams.setCrashFilePath(WXEnvironment.getCrashFilePath(WXEnvironment.getApplication().getApplicationContext()));
     wxParams.setLibJssPath(WXEnvironment.getLibJssRealPath());
diff --git a/android/sdk/src/main/java/com/taobao/weex/bridge/WXParams.java 
b/android/sdk/src/main/java/com/taobao/weex/bridge/WXParams.java
index 8b41a8fae2..657b21aafd 100644
--- a/android/sdk/src/main/java/com/taobao/weex/bridge/WXParams.java
+++ b/android/sdk/src/main/java/com/taobao/weex/bridge/WXParams.java
@@ -42,6 +42,7 @@
   private String useSingleProcess;
   private String crashFilePath;
   private String libJssPath;
+  private String layoutDirection;
 
   private String libJscPath;
   private String libIcuPath;
@@ -120,6 +121,11 @@ public void setDeviceModel(String deviceModel) {
     this.deviceModel = deviceModel;
   }
 
+  @CalledByNative
+  public String getLayoutDirection() {return layoutDirection;}
+
+  public void setLayoutDirection(String direction) { this.layoutDirection = 
direction; }
+
   @CalledByNative
   public String getAppName() {
     return appName;
@@ -246,6 +252,7 @@ public void setLibLdPath(String libLdPath) {
     map.put("deviceHeight", deviceHeight);
     map.put("deviceModel", deviceModel);
     map.put("deviceWidth", deviceWidth);
+    map.put("layoutDirection", layoutDirection);
     map.put("libJssPath", libJssPath);
     map.put("logLevel", logLevel);
     map.put("needInitV8", needInitV8);
diff --git a/android/sdk/src/main/java/com/taobao/weex/common/WXConfig.java 
b/android/sdk/src/main/java/com/taobao/weex/common/WXConfig.java
index 11dc82090c..7bf525c048 100644
--- a/android/sdk/src/main/java/com/taobao/weex/common/WXConfig.java
+++ b/android/sdk/src/main/java/com/taobao/weex/common/WXConfig.java
@@ -33,5 +33,6 @@
   String externalUserAgent="externalUserAgent";
   String logLevel="logLevel";
   String scale = "scale";
+  String layoutDirection = "layoutDirection";
   String debugMode = "debugMode";
 }
diff --git a/android/sdk/src/main/java/com/taobao/weex/dom/WXStyle.java 
b/android/sdk/src/main/java/com/taobao/weex/dom/WXStyle.java
index a401090370..17f549e64b 100644
--- a/android/sdk/src/main/java/com/taobao/weex/dom/WXStyle.java
+++ b/android/sdk/src/main/java/com/taobao/weex/dom/WXStyle.java
@@ -178,13 +178,17 @@ public static String getFontFamily(Map<String, Object> 
style) {
   }
 
   public static Layout.Alignment getTextAlignment(Map<String, Object> style){
-    Layout.Alignment alignment= Layout.Alignment.ALIGN_NORMAL;
+    return getTextAlignment(style, false);
+  }
+
+  public static Layout.Alignment getTextAlignment(Map<String, Object> style, 
boolean isRTL){
+    Layout.Alignment alignment= isRTL ? Layout.Alignment.ALIGN_OPPOSITE : 
Layout.Alignment.ALIGN_NORMAL;
     String textAlign= (String) style.get(Constants.Name.TEXT_ALIGN);
     if(TextUtils.equals(Constants.Value.LEFT,textAlign)){
       alignment= Layout.Alignment.ALIGN_NORMAL;
     }
     else if(TextUtils.equals(Constants.Value.CENTER,textAlign)){
-      alignment=Layout.Alignment.ALIGN_CENTER;
+      alignment= Layout.Alignment.ALIGN_CENTER;
     }
     else if(TextUtils.equals(Constants.Value.RIGHT,textAlign)){
       alignment= Layout.Alignment.ALIGN_OPPOSITE;
diff --git 
a/android/sdk/src/main/java/com/taobao/weex/layout/measurefunc/TextContentBoxMeasurement.java
 
b/android/sdk/src/main/java/com/taobao/weex/layout/measurefunc/TextContentBoxMeasurement.java
index 66d0faeb46..ef0ccb094a 100644
--- 
a/android/sdk/src/main/java/com/taobao/weex/layout/measurefunc/TextContentBoxMeasurement.java
+++ 
b/android/sdk/src/main/java/com/taobao/weex/layout/measurefunc/TextContentBoxMeasurement.java
@@ -232,7 +232,7 @@ private void updateStyleImp(Map<String, Object> style) {
       if (style.containsKey(Constants.Name.FONT_FAMILY)) {
         mFontFamily = WXStyle.getFontFamily(style);
       }
-      mAlignment = WXStyle.getTextAlignment(style);
+      mAlignment = WXStyle.getTextAlignment(style, 
mComponent.isNativeLayoutRTL());
       textOverflow = WXStyle.getTextOverflow(style);
       int lineHeight = WXStyle.getLineHeight(style, 
mComponent.getViewPortWidth());
       if (lineHeight != UNSET) {
diff --git 
a/android/sdk/src/main/java/com/taobao/weex/ui/action/ActionGetLayoutDirection.java
 
b/android/sdk/src/main/java/com/taobao/weex/ui/action/ActionGetLayoutDirection.java
new file mode 100644
index 0000000000..039bdfce08
--- /dev/null
+++ 
b/android/sdk/src/main/java/com/taobao/weex/ui/action/ActionGetLayoutDirection.java
@@ -0,0 +1,117 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package com.taobao.weex.ui.action;
+
+import android.support.annotation.NonNull;
+import android.text.TextUtils;
+import android.view.View;
+
+import com.taobao.weex.WXSDKInstance;
+import com.taobao.weex.WXSDKManager;
+import com.taobao.weex.bridge.JSCallback;
+import com.taobao.weex.bridge.SimpleJSCallback;
+import com.taobao.weex.ui.component.WXComponent;
+import com.taobao.weex.ui.component.list.template.jni.NativeRenderObjectUtils;
+import com.taobao.weex.utils.WXViewUtils;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Created by listen on 18/09/12.
+ */
+public class ActionGetLayoutDirection extends BasicGraphicAction {
+
+  private final String mCallback;
+
+  public ActionGetLayoutDirection(WXSDKInstance instance, String ref, String 
callback) {
+    super(instance, ref);
+    this.mCallback = callback;
+  }
+
+  @Override
+  public void executeAction() {
+    WXSDKInstance instance = getWXSDKIntance();
+    if (instance == null || instance.isDestroy()) {
+      return;
+    }
+
+    JSCallback jsCallback = new SimpleJSCallback(instance.getInstanceId(), 
mCallback);
+
+    if (TextUtils.isEmpty(getRef())) {
+      Map<String, Object> options = new HashMap<>();
+      options.put("result", false);
+      options.put("errMsg", "Illegal parameter");
+      jsCallback.invoke(options);
+    } else if ("viewport".equalsIgnoreCase(getRef())) {
+      callbackViewport(instance, jsCallback);
+    } else {
+      WXComponent component = 
WXSDKManager.getInstance().getWXRenderManager().getWXComponent(getPageId(), 
getRef());
+      if (component == null) {
+        return;
+      }
+
+      String directionRet = "ltr";
+      if (component != null) {
+        int direction = 
NativeRenderObjectUtils.nativeRenderObjectGetLayoutDirectionFromPathNode(component.getRenderObjectPtr());
+        switch (direction) {
+          case 0: {
+            directionRet = "inherit";
+            break;
+          }
+          case 1: {
+            directionRet = "ltr";
+            break;
+          }
+          case 2: {
+            directionRet = "rtl";
+            break;
+          }
+          default: {
+            directionRet = "ltr";
+            break;
+          }
+
+        }
+      }
+      jsCallback.invoke(directionRet);
+    }
+  }
+
+  private void callbackViewport(WXSDKInstance instance, JSCallback jsCallback) 
{
+    View container;
+    if ((container = instance.getContainerView()) != null) {
+      Map<String, Object> options = new HashMap<>();
+      options.put("direction", "ltr");
+      options.put("result", true);
+      jsCallback.invoke(options);
+    } else {
+      Map<String, Object> options = new HashMap<>();
+      options.put("result", false);
+      options.put("errMsg", "Component does not exist");
+      jsCallback.invoke(options);
+    }
+  }
+
+  @NonNull
+  private float getWebPxValue(int value,int viewport) {
+    return WXViewUtils.getWebPxByWidth(value, viewport);
+  }
+
+}
diff --git 
a/android/sdk/src/main/java/com/taobao/weex/ui/component/WXComponent.java 
b/android/sdk/src/main/java/com/taobao/weex/ui/component/WXComponent.java
index 7840b9a394..fbf8bc3279 100644
--- a/android/sdk/src/main/java/com/taobao/weex/ui/component/WXComponent.java
+++ b/android/sdk/src/main/java/com/taobao/weex/ui/component/WXComponent.java
@@ -46,6 +46,7 @@
 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
 import android.text.TextUtils;
 import android.util.Pair;
+import android.view.Gravity;
 import android.view.Menu;
 import android.view.View;
 import android.view.ViewGroup;
@@ -87,6 +88,7 @@
 import com.taobao.weex.ui.component.basic.WXBasicComponent;
 import com.taobao.weex.ui.component.binding.Statements;
 import com.taobao.weex.ui.component.list.WXCell;
+import 
com.taobao.weex.ui.component.list.template.jni.NativeRenderLayoutDirection;
 import com.taobao.weex.ui.component.list.template.jni.NativeRenderObjectUtils;
 import com.taobao.weex.ui.component.pesudo.OnActivePseudoListner;
 import com.taobao.weex.ui.component.pesudo.PesudoStatus;
@@ -153,6 +155,7 @@
   private int mPreRealWidth = 0;
   private int mPreRealHeight = 0;
   private int mPreRealLeft = 0;
+  private int mPreRealRight = 0;
   private int mPreRealTop = 0;
   private int mStickyOffset = 0;
   protected WXGesture mGesture;
@@ -233,6 +236,44 @@ protected final void setContentBoxMeasurement(final 
ContentBoxMeasurement conten
     
WXBridgeManager.getInstance().bindMeasurementToRenderObject(getRenderObjectPtr());
   }
 
+
+  public void setMarginsSupportRTL(ViewGroup.MarginLayoutParams lp, int left, 
int top, int right, int bottom) {
+      lp.setMargins(left, top, right, bottom);
+      if (lp instanceof FrameLayout.LayoutParams) {
+          FrameLayout.LayoutParams lp_frameLayout = (FrameLayout.LayoutParams) 
lp;
+          lp_frameLayout.gravity = Gravity.LEFT | Gravity.TOP;
+      }
+  }
+
+  public boolean isNativeLayoutRTL() {
+      return 
NativeRenderObjectUtils.nativeRenderObjectGetLayoutDirectionFromPathNode(this.getRenderObjectPtr())
 == NativeRenderLayoutDirection.rtl;
+  }
+
+  public static boolean isLayoutRTL(WXComponent cmp) {
+    if (cmp == null) return false;
+
+    View view = cmp.getHostView();
+    if (ViewCompat.isLayoutDirectionResolved(view)) {
+      return ViewCompat.getLayoutDirection(view) == View.LAYOUT_DIRECTION_RTL;
+    } else if (cmp.getParent() != null){
+      return isLayoutRTL(cmp.getParent());
+    } else {
+      return isLayoutRTL((ViewGroup) view.getParent());
+    }
+  }
+
+  public static boolean isLayoutRTL(ViewGroup viewGroup) {
+    if (viewGroup == null) return false;
+
+    if (ViewCompat.isLayoutDirectionResolved(viewGroup)) {
+      return ViewCompat.getLayoutDirection(viewGroup) == 
View.LAYOUT_DIRECTION_RTL;
+    } else if (viewGroup.getParent() instanceof ViewGroup) {
+      return isLayoutRTL((ViewGroup) viewGroup.getParent());
+    } else {
+      return false;
+    }
+  }
+
   public void updateStyles(WXComponent component) {
     if (component != null) {
       updateProperties(component.getStyles());
@@ -881,8 +922,7 @@ protected BorderDrawable getOrCreateBorder() {
   /**
    * layout view
    */
-  public final void setLayout(WXComponent component) {
-
+  public void setLayout(WXComponent component) {
     if (TextUtils.isEmpty(component.getComponentType())
             || TextUtils.isEmpty(component.getRef()) || 
component.getLayoutPosition() == null
             || component.getLayoutSize() == null) {
@@ -910,6 +950,7 @@ public final void setLayout(WXComponent component) {
 
     int realLeft = 0;
     int realTop = 0;
+    int realRight = 0;
 
     if (isFixed()) {
       realLeft = (int) (getLayoutPosition().getLeft() - 
getInstance().getRenderContainerPaddingLeft());
@@ -921,14 +962,14 @@ public final void setLayout(WXComponent component) {
               parentPadding.get(CSSShorthand.EDGE.TOP) - 
parentBorder.get(CSSShorthand.EDGE.TOP)) + siblingOffset;
     }
 
-    int realRight = (int) getMargin().get(CSSShorthand.EDGE.RIGHT);
+    realRight = (int) getMargin().get(CSSShorthand.EDGE.RIGHT);
     int realBottom = (int) getMargin().get(CSSShorthand.EDGE.BOTTOM);
 
     Point rawOffset = new Point(
             (int) getLayoutPosition().getLeft(),
             (int) getLayoutPosition().getTop());
 
-    if (mPreRealWidth == realWidth && mPreRealHeight == realHeight && 
mPreRealLeft == realLeft && mPreRealTop == realTop) {
+    if (mPreRealWidth == realWidth && mPreRealHeight == realHeight && 
mPreRealLeft == realLeft && mPreRealRight == realRight && mPreRealTop == 
realTop) {
       return;
     }
 
@@ -994,6 +1035,7 @@ private void setComponentLayoutParams(int realWidth, int 
realHeight, int realLef
       mPreRealWidth = realWidth;
       mPreRealHeight = realHeight;
       mPreRealLeft = realLeft;
+      mPreRealRight = realRight;
       mPreRealTop = realTop;
       onFinishLayout();
       // restore box shadow
@@ -1047,14 +1089,14 @@ public int getLayoutTopOffsetForSibling() {
   protected void setHostLayoutParams(T host, int width, int height, int left, 
int right, int top, int bottom) {
     ViewGroup.LayoutParams lp;
     if (mParent == null) {
-      FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(width, 
height);
-      params.setMargins(left, top, right, bottom);
-      lp = params;
+        FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(width, 
height);
+        this.setMarginsSupportRTL(params, left, top, right, bottom);
+        lp = params;
     } else {
-      lp = mParent.getChildLayoutParams(this, host, width, height, left, 
right, top, bottom);
+        lp = mParent.getChildLayoutParams(this, host, width, height, left, 
right, top, bottom);
     }
     if (lp != null) {
-      host.setLayoutParams(lp);
+        host.setLayoutParams(lp);
     }
   }
 
@@ -1063,7 +1105,9 @@ private void setFixedHostLayoutParams(T host, int width, 
int height, int left, i
 
     params.width = width;
     params.height = height;
-    params.setMargins(left, top, right, bottom);
+
+    this.setMarginsSupportRTL(params, left, top, right, bottom);
+
     host.setLayoutParams(params);
     mInstance.moveFixedView(host);
 
@@ -1844,6 +1888,7 @@ public boolean isDestoryed() {
   public View detachViewAndClearPreInfo() {
     View original = mHost;
     mPreRealLeft = 0;
+    mPreRealRight = 0;
     mPreRealWidth = 0;
     mPreRealHeight = 0;
     mPreRealTop = 0;
@@ -1853,6 +1898,7 @@ public View detachViewAndClearPreInfo() {
 
   public void clearPreLayout() {
     mPreRealLeft = 0;
+    mPreRealRight = 0;
     mPreRealWidth = 0;
     mPreRealHeight = 0;
     mPreRealTop = 0;
diff --git 
a/android/sdk/src/main/java/com/taobao/weex/ui/component/WXIndicator.java 
b/android/sdk/src/main/java/com/taobao/weex/ui/component/WXIndicator.java
index e8422cadd4..449c12ee25 100644
--- a/android/sdk/src/main/java/com/taobao/weex/ui/component/WXIndicator.java
+++ b/android/sdk/src/main/java/com/taobao/weex/ui/component/WXIndicator.java
@@ -54,7 +54,7 @@ public WXIndicator(WXSDKInstance instance, WXVContainer 
parent, boolean isLazy,
   @Override
   protected void setHostLayoutParams(WXCircleIndicator host, int width, int 
height, int left, int right, int top, int bottom) {
       FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(width, 
height);
-      params.setMargins(left, top, right, bottom);
+      this.setMarginsSupportRTL(params, left, top, right, bottom);
       host.setLayoutParams(params);
   }
 
diff --git 
a/android/sdk/src/main/java/com/taobao/weex/ui/component/WXScroller.java 
b/android/sdk/src/main/java/com/taobao/weex/ui/component/WXScroller.java
index 81c0e177a0..bfef86ff94 100644
--- a/android/sdk/src/main/java/com/taobao/weex/ui/component/WXScroller.java
+++ b/android/sdk/src/main/java/com/taobao/weex/ui/component/WXScroller.java
@@ -33,7 +33,11 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+import android.support.v4.view.ViewCompat;
 import android.text.TextUtils;
+import android.view.Gravity;
+import android.util.Log;
 import android.view.GestureDetector;
 import android.view.MotionEvent;
 import android.view.View;
@@ -377,6 +381,42 @@ public void destroy() {
     }
   }
 
+  @Override
+  public void setMarginsSupportRTL(ViewGroup.MarginLayoutParams lp, int left, 
int top, int right, int bottom) {
+    if (android.os.Build.VERSION.SDK_INT >= 
android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
+      lp.setMargins(left, top, right, bottom);
+      lp.setMarginStart(left);
+      lp.setMarginEnd(right);
+    } else {
+      if (lp instanceof FrameLayout.LayoutParams) {
+        FrameLayout.LayoutParams lp_frameLayout = (FrameLayout.LayoutParams) 
lp;
+        if (this.isNativeLayoutRTL()) {
+          lp_frameLayout.gravity = Gravity.RIGHT | Gravity.TOP;
+          lp.setMargins(right, top, left, bottom);
+        } else {
+          lp_frameLayout.gravity = Gravity.LEFT | Gravity.TOP;
+          lp.setMargins(left, top, right, bottom);
+        }
+      } else {
+        lp.setMargins(left, top, right, bottom);
+      }
+    }
+  }
+
+  @Override
+  public void setLayout(WXComponent component) {
+    if (TextUtils.isEmpty(component.getComponentType())
+            || TextUtils.isEmpty(component.getRef()) || 
component.getLayoutPosition() == null
+            || component.getLayoutSize() == null) {
+      return;
+    }
+    if (component.getHostView() != null) {
+      int layoutDirection = component.isNativeLayoutRTL() ? 
View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR;
+      ViewCompat.setLayoutDirection(component.getHostView(), layoutDirection);
+    }
+    super.setLayout(component);
+  }
+
   @Override
   protected MeasureOutput measure(int width, int height) {
     MeasureOutput measureOutput = new MeasureOutput();
@@ -437,6 +477,37 @@ public void onScrollChanged(WXHorizontalScrollView 
scrollView, int x, int y, int
       scrollView.addView(mRealView, layoutParams);
       scrollView.setHorizontalScrollBarEnabled(false);
 
+        final WXScroller component = this;
+        final View.OnLayoutChangeListener listener = new 
View.OnLayoutChangeListener() {
+          @Override
+          public void onLayoutChange(View view, int left, int top, int right, 
int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
+            final View frameLayout = view;
+            scrollView.post(new Runnable() {
+              @Override
+              public void run() {
+                if (isNativeLayoutRTL()) {
+                  int mw = frameLayout.getMeasuredWidth();
+                  scrollView.scrollTo(mw, component.getScrollY());
+                } else {
+                  scrollView.scrollTo(0, component.getScrollY());
+                }
+              }
+            });
+          }
+        };
+        mRealView.addOnAttachStateChangeListener(new 
View.OnAttachStateChangeListener() {
+          @Override
+          public void onViewAttachedToWindow(View view) {
+            view.addOnLayoutChangeListener(listener);
+          }
+
+          @Override
+          public void onViewDetachedFromWindow(View view) {
+            view.removeOnLayoutChangeListener(listener);
+          }
+        });
+
+
       if(pageEnable) {
         mGestureDetector = new GestureDetector(new 
MyGestureDetector(scrollView));
         scrollView.setOnTouchListener(new View.OnTouchListener() {
@@ -706,9 +777,21 @@ public void scrollTo(WXComponent component, Map<String, 
Object> options) {
       mActiveFeature = mChildren.indexOf(component);
     }
 
-
-    int viewYInScroller=component.getAbsoluteY() - getAbsoluteY();
-    int viewXInScroller=component.getAbsoluteX() - getAbsoluteX();
+    int viewYInScroller = component.getAbsoluteY() - getAbsoluteY();
+    int viewXInScroller = 0;
+    if (this.isNativeLayoutRTL()) {
+      // if layout direction is rtl, we need calculate rtl scroll x;
+      if (getInnerView().getChildCount() > 0) {
+        int totalWidth = getInnerView().getChildAt(0).getWidth();
+        int displayWidth = getInnerView().getMeasuredWidth();
+        viewXInScroller = totalWidth - (component.getAbsoluteX() - 
getAbsoluteX()) - displayWidth;
+      } else {
+        viewXInScroller = component.getAbsoluteX() - getAbsoluteX();
+      }
+      offsetFloat = - offsetFloat;
+    } else {
+      viewXInScroller = component.getAbsoluteX() - getAbsoluteX();
+    }
 
     scrollBy(viewXInScroller - getScrollX() + (int) offsetFloat, 
viewYInScroller - getScrollY() + (int) offsetFloat, smooth);
   }
diff --git 
a/android/sdk/src/main/java/com/taobao/weex/ui/component/WXSlider.java 
b/android/sdk/src/main/java/com/taobao/weex/ui/component/WXSlider.java
index 196c839e11..8e9474069f 100644
--- a/android/sdk/src/main/java/com/taobao/weex/ui/component/WXSlider.java
+++ b/android/sdk/src/main/java/com/taobao/weex/ui/component/WXSlider.java
@@ -145,9 +145,9 @@ public LayoutParams getChildLayoutParams(WXComponent 
child,View childView, int w
     if (lp instanceof ViewGroup.MarginLayoutParams) {
       //expect indicator .
       if (child instanceof WXIndicator) {
-        ((ViewGroup.MarginLayoutParams) lp).setMargins(left, top, right, 
bottom);
+        this.setMarginsSupportRTL((ViewGroup.MarginLayoutParams) lp, left, 
top, right, bottom);
       } else {
-        ((ViewGroup.MarginLayoutParams) lp).setMargins(0, 0, 0, 0);
+        this.setMarginsSupportRTL((ViewGroup.MarginLayoutParams) lp, 0, 0, 0, 
0);
       }
     }
     return lp;
@@ -193,7 +193,7 @@ public void addSubView(View view, int index) {
           @Override
           public void run() {
             initIndex = getInitIndex();
-            mViewPager.setCurrentItem(initIndex);
+            mViewPager.setCurrentItem(getRealIndex(initIndex));
             initIndex = -1;
             initRunnable = null;
           }
@@ -203,7 +203,7 @@ public void run() {
       mViewPager.postDelayed(initRunnable, 50);
     } else {
       if (!keepIndex) {
-        mViewPager.setCurrentItem(0);
+        mViewPager.setCurrentItem(getRealIndex(0));
       }
     }
     if (mIndicator != null) {
@@ -212,6 +212,12 @@ public void run() {
     }
   }
 
+  @Override
+  public void setLayout(WXComponent component) {
+    mAdapter.setLayoutDirectionRTL(this.isNativeLayoutRTL());
+    super.setLayout(component);
+  }
+
   @Override
   public void remove(WXComponent child, boolean destroy) {
     if (child == null || child.getHostView() == null || mAdapter == null) {
@@ -274,9 +280,22 @@ private int getInitIndex(){
     if(select >= mAdapter.getRealCount()){
       select = select%mAdapter.getRealCount();
     }
+
     return select;
   }
 
+  private int getRealIndex(int idx) {
+    int retIdx = idx;
+
+    if (mAdapter.getRealCount() > 0) {
+      if(idx >= mAdapter.getRealCount()) retIdx = mAdapter.getRealCount() - 1;
+      if (isNativeLayoutRTL()) {
+        retIdx = mAdapter.getRealCount() - 1 - retIdx;
+      }
+    }
+    retIdx = retIdx + 0;
+    return retIdx;
+  }
 
   @Override
   protected boolean setProperty(String key, Object param) {
@@ -383,6 +402,8 @@ public void setIndex(int index) {
         initIndex = index;
         return;
       }
+
+      index = getRealIndex(index);
       mViewPager.setCurrentItem(index);
       if (mIndicator != null && mIndicator.getHostView() != null
               && mIndicator.getHostView().getRealCurrentItem() != index) {
diff --git 
a/android/sdk/src/main/java/com/taobao/weex/ui/component/WXVContainer.java 
b/android/sdk/src/main/java/com/taobao/weex/ui/component/WXVContainer.java
index c9880b5754..3d797ed1ac 100644
--- a/android/sdk/src/main/java/com/taobao/weex/ui/component/WXVContainer.java
+++ b/android/sdk/src/main/java/com/taobao/weex/ui/component/WXVContainer.java
@@ -131,7 +131,7 @@ public void applyLayoutAndEvent(WXComponent component) {
       lp.width = width;
       lp.height = height;
       if(lp instanceof ViewGroup.MarginLayoutParams){
-        ((ViewGroup.MarginLayoutParams) lp).setMargins(left,top,right,bottom);
+        this.setMarginsSupportRTL((ViewGroup.MarginLayoutParams) lp, left, 
top, right, bottom);
       }
     }
     return lp;
@@ -588,8 +588,7 @@ View getBoxShadowHost(boolean isClear) {
           int bottom = (int) (padding.get(CSSShorthand.EDGE.BOTTOM) + 
border.get(CSSShorthand.EDGE.BOTTOM));
 
           ViewGroup.MarginLayoutParams layoutParams = new 
ViewGroup.MarginLayoutParams(hostView.getLayoutParams()) ;
-          layoutParams.setMargins(-left, -top, -right, -bottom);
-
+          this.setMarginsSupportRTL(layoutParams, -left, -top, -right, 
-bottom);
           mBoxShadowHost.setLayoutParams(layoutParams);
 
           hostView.addView(mBoxShadowHost);
diff --git 
a/android/sdk/src/main/java/com/taobao/weex/ui/component/list/BasicListComponent.java
 
b/android/sdk/src/main/java/com/taobao/weex/ui/component/list/BasicListComponent.java
index e377ad9164..a9a2c67910 100644
--- 
a/android/sdk/src/main/java/com/taobao/weex/ui/component/list/BasicListComponent.java
+++ 
b/android/sdk/src/main/java/com/taobao/weex/ui/component/list/BasicListComponent.java
@@ -35,6 +35,7 @@
 import android.support.v7.widget.StaggeredGridLayoutManager;
 import android.text.TextUtils;
 import android.util.SparseArray;
+import android.view.Gravity;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
@@ -173,6 +174,42 @@ public BasicListComponent(WXSDKInstance instance, 
WXVContainer parent, BasicComp
     stickyHelper = new WXStickyHelper(this);
   }
 
+  @Override
+  public void setMarginsSupportRTL(ViewGroup.MarginLayoutParams lp, int left, 
int top, int right, int bottom) {
+    if (android.os.Build.VERSION.SDK_INT >= 
android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
+      lp.setMargins(left, top, right, bottom);
+      lp.setMarginStart(left);
+      lp.setMarginEnd(right);
+    } else {
+      if (lp instanceof FrameLayout.LayoutParams) {
+        FrameLayout.LayoutParams lp_frameLayout = (FrameLayout.LayoutParams) 
lp;
+        if (this.isNativeLayoutRTL()) {
+          lp_frameLayout.gravity = Gravity.RIGHT | Gravity.TOP;
+          lp.setMargins(right, top, left, bottom);
+        } else {
+          lp_frameLayout.gravity = Gravity.LEFT | Gravity.TOP;
+          lp.setMargins(left, top, right, bottom);
+        }
+      } else {
+        lp.setMargins(left, top, right, bottom);
+      }
+    }
+  }
+
+  @Override
+  public void setLayout(WXComponent component) {
+    if (TextUtils.isEmpty(component.getComponentType())
+            || TextUtils.isEmpty(component.getRef()) || 
component.getLayoutPosition() == null
+            || component.getLayoutSize() == null) {
+      return;
+    }
+    if (component.getHostView() != null) {
+      int layoutDirection = component.isNativeLayoutRTL() ? 
View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR;
+      ViewCompat.setLayoutDirection(component.getHostView(), layoutDirection);
+    }
+    super.setLayout(component);
+  }
+
   @Override
   protected void onHostViewInitialized(T host) {
     super.onHostViewInitialized(host);
@@ -248,7 +285,8 @@ public void destroy() {
     } else {
       params.width = width;
       params.height = height;
-      params.setMargins(left, 0, right, 0);
+
+      this.setMarginsSupportRTL(params, left, 0, right, 0);
     }
     return params;
   }
@@ -331,8 +369,6 @@ public void onScrolled(RecyclerView recyclerView, int dx, 
int dy) {
       }
     });
 
-
-
     bounceRecyclerView.getViewTreeObserver().addOnGlobalLayoutListener(new 
ViewTreeObserver.OnGlobalLayoutListener() {
       @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
       @Override
diff --git 
a/android/sdk/src/main/java/com/taobao/weex/ui/component/list/GapItemDecoration.java
 
b/android/sdk/src/main/java/com/taobao/weex/ui/component/list/GapItemDecoration.java
index 821d7ce4ab..aebb6c3fb6 100644
--- 
a/android/sdk/src/main/java/com/taobao/weex/ui/component/list/GapItemDecoration.java
+++ 
b/android/sdk/src/main/java/com/taobao/weex/ui/component/list/GapItemDecoration.java
@@ -64,10 +64,17 @@ public void getItemOffsets(Rect outRect, View view, 
RecyclerView parent, Recycle
                 if (params.getSpanIndex() >= spanOffsets.length) {
                     return;
                 }
-                float spanOffset = 
listComponent.getSpanOffsets()[params.getSpanIndex()];
+
+                int index = listComponent.isNativeLayoutRTL() ? 
spanOffsets.length - params.getSpanIndex() - 1 : params.getSpanIndex();
+                float spanOffset = listComponent.getSpanOffsets()[index];
                 int   spanOffsetPx =  
Math.round(WXViewUtils.getRealPxByWidth(spanOffset, 
listComponent.getViewPortWidth()));
-                outRect.left =  spanOffsetPx;
-                outRect.right = -spanOffsetPx;
+                if (listComponent.isNativeLayoutRTL()) {
+                    outRect.left = -spanOffsetPx;
+                    outRect.right = spanOffsetPx;
+                } else {
+                    outRect.left = spanOffsetPx;
+                    outRect.right = -spanOffsetPx;
+                }
             }
         }
     }
diff --git 
a/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/WXRecyclerTemplateList.java
 
b/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/WXRecyclerTemplateList.java
index 916d895367..8efcc2f512 100644
--- 
a/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/WXRecyclerTemplateList.java
+++ 
b/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/WXRecyclerTemplateList.java
@@ -1283,7 +1283,8 @@ private void removeFooterOrHeader(WXComponent child) {
         } else {
             params.width = width;
             params.height = height;
-            params.setMargins(left, 0, right, 0);
+
+            this.setMarginsSupportRTL(params, left, 0, right, 0);
         }
         return params;
     }
diff --git 
a/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/jni/NativeRenderLayoutDirection.java
 
b/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/jni/NativeRenderLayoutDirection.java
new file mode 100644
index 0000000000..443dcf6db7
--- /dev/null
+++ 
b/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/jni/NativeRenderLayoutDirection.java
@@ -0,0 +1,25 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package com.taobao.weex.ui.component.list.template.jni;
+
+public class NativeRenderLayoutDirection {
+    public static final int inherit = 0;
+    public static final int ltr = 1;
+    public static final int rtl = 2;
+}
diff --git 
a/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/jni/NativeRenderObjectUtils.java
 
b/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/jni/NativeRenderObjectUtils.java
index c477413b6d..f665e8b18f 100644
--- 
a/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/jni/NativeRenderObjectUtils.java
+++ 
b/android/sdk/src/main/java/com/taobao/weex/ui/component/list/template/jni/NativeRenderObjectUtils.java
@@ -51,6 +51,7 @@
      * */
     public static native int nativeLayoutRenderObject(long ptr, float width, 
float height);
 
+    public static native int 
nativeRenderObjectGetLayoutDirectionFromPathNode(long ptr);
     /**
      * get child length
      * */
diff --git 
a/android/sdk/src/main/java/com/taobao/weex/ui/module/WXDomModule.java 
b/android/sdk/src/main/java/com/taobao/weex/ui/module/WXDomModule.java
index 787ae53522..8f8c957380 100644
--- a/android/sdk/src/main/java/com/taobao/weex/ui/module/WXDomModule.java
+++ b/android/sdk/src/main/java/com/taobao/weex/ui/module/WXDomModule.java
@@ -26,6 +26,7 @@
 import com.taobao.weex.dom.binding.JSONUtils;
 import com.taobao.weex.ui.action.ActionAddRule;
 import com.taobao.weex.ui.action.ActionGetComponentRect;
+import com.taobao.weex.ui.action.ActionGetLayoutDirection;
 import com.taobao.weex.ui.action.ActionInvokeMethod;
 import com.taobao.weex.ui.action.GraphicActionBatchBegin;
 import com.taobao.weex.ui.action.GraphicActionBatchEnd;
@@ -47,6 +48,7 @@
   public static final String SCROLL_TO_ELEMENT = "scrollToElement";
   public static final String ADD_RULE = "addRule";
   public static final String GET_COMPONENT_RECT = "getComponentRect";
+  public static final String GET_COMPONENT_DIRECTION = "getLayoutDirection";
   public static final String WXDOM = "dom";
   public static final String INVOKE_METHOD = "invokeMethod";
 
@@ -59,7 +61,7 @@
    * Methods expose to js. Every method which will be called in js should add 
to this array.
    */
   public static final String[] METHODS = {SCROLL_TO_ELEMENT, ADD_RULE, 
GET_COMPONENT_RECT,
-      INVOKE_METHOD, BATCH_BEGIN, BATCH_END};
+      INVOKE_METHOD, GET_COMPONENT_DIRECTION, BATCH_BEGIN, BATCH_END};
 
   public WXDomModule(WXSDKInstance instance){
     mWXSDKInstance = instance;
@@ -82,6 +84,14 @@ public Object callDomMethod(String method, JSONArray args, 
long... parseNanos) {
 
     try {
       switch (method) {
+        case GET_COMPONENT_DIRECTION: {
+          if(args == null){
+            return null;
+          }
+          new ActionGetLayoutDirection(mWXSDKInstance, args.getString(0), 
args.getString(1))
+                  .executeActionOnRender();
+          break;
+        }
         case SCROLL_TO_ELEMENT:{
           if (args == null) {
             return null;
diff --git 
a/android/sdk/src/main/java/com/taobao/weex/ui/view/WXCirclePageAdapter.java 
b/android/sdk/src/main/java/com/taobao/weex/ui/view/WXCirclePageAdapter.java
index 281c4e2ba9..1b5b219d83 100644
--- a/android/sdk/src/main/java/com/taobao/weex/ui/view/WXCirclePageAdapter.java
+++ b/android/sdk/src/main/java/com/taobao/weex/ui/view/WXCirclePageAdapter.java
@@ -26,6 +26,7 @@
 import com.taobao.weex.utils.WXLogUtils;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 public class WXCirclePageAdapter extends PagerAdapter {
@@ -37,12 +38,26 @@
   private List<View> shadow = new ArrayList<>();
   private boolean needLoop = true;
 
+  public boolean isRTL = false;
+  private List<View> originalViews = new ArrayList<>();
+
   public WXCirclePageAdapter(List<View> views, boolean needLoop) {
     super();
     this.views = new ArrayList<>(views);
+    this.originalViews = new ArrayList<>(views);
     this.needLoop = needLoop;
   }
 
+  public void setLayoutDirectionRTL(boolean isRTL) {
+    if (isRTL == this.isRTL) return;
+    this.isRTL = isRTL;
+    this.views = new ArrayList<>(this.originalViews);
+    if (isRTL) {
+      Collections.reverse(this.views);
+    }
+    ensureShadow();
+  }
+
   public WXCirclePageAdapter() {
     this(true);
   }
@@ -56,7 +71,13 @@ public void addPageView(View view) {
     if (WXEnvironment.isApkDebugable()) {
       WXLogUtils.d("onPageSelected >>>> addPageView");
     }
-    views.add(view);
+
+    originalViews.add(view);
+    if (this.isRTL) {
+      views.add(0, view);
+    } else {
+      views.add(view);
+    }
     ensureShadow();
   }
 
@@ -65,6 +86,7 @@ public void removePageView(View view) {
       WXLogUtils.d("onPageSelected >>>> removePageView");
     }
     views.remove(view);
+    originalViews.remove(view);
     ensureShadow();
   }
 
@@ -77,6 +99,10 @@ public void replacePageView(View oldView, View newView) {
     views.remove(index);
     views.add(index, newView);
     ensureShadow();
+
+    index = originalViews.indexOf(oldView);
+    originalViews.remove(index);
+    originalViews.add(index, newView);
   }
 
   @Override
diff --git 
a/android/sdk/src/main/java/com/taobao/weex/ui/view/WXCircleViewPager.java 
b/android/sdk/src/main/java/com/taobao/weex/ui/view/WXCircleViewPager.java
index 5c79e5ba73..6c19615def 100644
--- a/android/sdk/src/main/java/com/taobao/weex/ui/view/WXCircleViewPager.java
+++ b/android/sdk/src/main/java/com/taobao/weex/ui/view/WXCircleViewPager.java
@@ -317,13 +317,24 @@ public void setScrollable(boolean scrollable) {
   }
 
   private void showNextItem() {
-    if (!needLoop && superGetCurrentItem() == getRealCount() - 1) {
-      return;
-    }
-    if (getRealCount() == 2 && superGetCurrentItem() == 1) {
-      superSetCurrentItem(0, true);
+    if (this.getCirclePageAdapter() != null && 
this.getCirclePageAdapter().isRTL) {
+      if (!needLoop && superGetCurrentItem() == 0) {
+        return;
+      }
+      if (getRealCount() == 2 && superGetCurrentItem() == 0) {
+        superSetCurrentItem(1, true);
+      } else {
+        superSetCurrentItem(superGetCurrentItem() - 1, true);
+      }
     } else {
-      superSetCurrentItem(superGetCurrentItem() + 1, true);
+      if (!needLoop && superGetCurrentItem() == getRealCount() - 1) {
+        return;
+      }
+      if (getRealCount() == 2 && superGetCurrentItem() == 1) {
+        superSetCurrentItem(0, true);
+      } else {
+        superSetCurrentItem(superGetCurrentItem() + 1, true);
+      }
     }
   }
 }
diff --git 
a/android/sdk/src/main/java/com/taobao/weex/utils/StaticLayoutProxy.java 
b/android/sdk/src/main/java/com/taobao/weex/utils/StaticLayoutProxy.java
index a3584ce164..ac1d660ac1 100644
--- a/android/sdk/src/main/java/com/taobao/weex/utils/StaticLayoutProxy.java
+++ b/android/sdk/src/main/java/com/taobao/weex/utils/StaticLayoutProxy.java
@@ -31,6 +31,7 @@
  */
 
 public class StaticLayoutProxy {
+  private static Constructor<StaticLayout> layoutConstructor;
   public static StaticLayout create(CharSequence source, TextPaint paint,
                                     int width,
                                     Layout.Alignment align, float spacingmult, 
float spacingadd,
@@ -40,6 +41,8 @@ public static StaticLayout create(CharSequence source, 
TextPaint paint,
       StaticLayout rtlLayout =  createInternal(source, paint, width, align, 
textDir, spacingmult, spacingadd, includepad);
       if (rtlLayout != null) {
         return rtlLayout;
+      } else {
+        return new StaticLayout(source, paint, width, align, spacingmult, 
spacingadd, includepad);
       }
     }
     return new StaticLayout(source, paint, width, align, spacingmult, 
spacingadd, includepad);
@@ -53,16 +56,19 @@ private static StaticLayout createInternal(CharSequence 
source, TextPaint paint,
       return null;
     } else {
       try {
-        Class<StaticLayout> clazz = StaticLayout.class;
-        Constructor<StaticLayout> constructor = 
clazz.getConstructor(CharSequence.class, TextPaint.class,
-                int.class, Layout.Alignment.class, 
TextDirectionHeuristic.class,
-                float.class, float.class,
-                boolean.class);
-
-        if (constructor != null) {
-          return constructor.newInstance(source, paint, width,
+        if (layoutConstructor == null) {
+          Class<StaticLayout> clazz = StaticLayout.class;
+          Constructor<StaticLayout> constructor = 
clazz.getConstructor(CharSequence.class, TextPaint.class,
+                  int.class, Layout.Alignment.class, 
TextDirectionHeuristic.class,
+                  float.class, float.class,
+                  boolean.class);
+          layoutConstructor = constructor;
+        }
+        if (layoutConstructor != null) {
+          return layoutConstructor.newInstance(source, paint, width,
                   align, textDir, spacingmult, spacingadd, includepad);
         }
+
       } catch (Throwable e) {
         e.printStackTrace();
       }
diff --git a/android/sdk/src/main/res/values-ldltr/isrtl.xml 
b/android/sdk/src/main/res/values-ldltr/isrtl.xml
new file mode 100644
index 0000000000..5008feadd9
--- /dev/null
+++ b/android/sdk/src/main/res/values-ldltr/isrtl.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+-->
+<resources>
+    <bool name="weex_is_right_to_left">false</bool>
+</resources>
\ No newline at end of file
diff --git a/android/sdk/src/main/res/values-ldrtl/istrtl.xml 
b/android/sdk/src/main/res/values-ldrtl/istrtl.xml
new file mode 100644
index 0000000000..47d9b1a8dc
--- /dev/null
+++ b/android/sdk/src/main/res/values-ldrtl/istrtl.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+-->
+<resources>
+    <bool name="weex_is_right_to_left">true</bool>
+</resources>
\ No newline at end of file
diff --git a/ios/sdk/WeexSDK/Sources/Bridge/WXBridgeContext.m 
b/ios/sdk/WeexSDK/Sources/Bridge/WXBridgeContext.m
index dd186de1ea..6d42333e75 100644
--- a/ios/sdk/WeexSDK/Sources/Bridge/WXBridgeContext.m
+++ b/ios/sdk/WeexSDK/Sources/Bridge/WXBridgeContext.m
@@ -850,13 +850,17 @@ - (void)executeJsService:(NSString *)script 
withName:(NSString *)name
 {
     if(self.frameworkLoadFinished) {
         WXAssert(script, @"param script required!");
-        NSDictionary* funcInfo = @{
-                                   @"func":@"executeJsService",
-                                   @"arg":name?:@"unsetScriptName"
-                                   };
-        self.jsBridge.javaScriptContext[@"wxExtFuncInfo"] = funcInfo;
+        if ([self.jsBridge respondsToSelector:@selector(javaScriptContext)]) {
+            NSDictionary* funcInfo = @{
+                                       @"func":@"executeJsService",
+                                       @"arg":name?:@"unsetScriptName"
+                                       };
+            self.jsBridge.javaScriptContext[@"wxExtFuncInfo"] = funcInfo;
+        }
         [self.jsBridge executeJavascript:script];
-        self.jsBridge.javaScriptContext[@"wxExtFuncInfo"] = nil;
+        if ([self.jsBridge respondsToSelector:@selector(javaScriptContext)]) {
+            self.jsBridge.javaScriptContext[@"wxExtFuncInfo"] = nil;
+        }
         
         if ([self.jsBridge exception]) {
             NSString *exception = [[self.jsBridge exception] toString];
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXComponent_internal.h 
b/ios/sdk/WeexSDK/Sources/Component/WXComponent_internal.h
index fc7658d826..3aef1dc13d 100644
--- a/ios/sdk/WeexSDK/Sources/Component/WXComponent_internal.h
+++ b/ios/sdk/WeexSDK/Sources/Component/WXComponent_internal.h
@@ -283,6 +283,9 @@ typedef id (^WXDataBindingBlock)(NSDictionary *data, BOOL 
*needUpdate);
 
 - (void)_buildViewHierarchyLazily;
 
+
+- (void)_adjustForRTL;
+
 - (BOOL)_isAffineTypeAs:(NSString *)type;
 
 @end
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXCycleSliderComponent.mm 
b/ios/sdk/WeexSDK/Sources/Component/WXCycleSliderComponent.mm
index 402e592c7a..b8a25fa634 100644
--- a/ios/sdk/WeexSDK/Sources/Component/WXCycleSliderComponent.mm
+++ b/ios/sdk/WeexSDK/Sources/Component/WXCycleSliderComponent.mm
@@ -458,6 +458,39 @@ - (void)layoutDidFinish
     _recycleSliderView.currentIndex = _index;
 }
 
+- (void)_buildViewHierarchyLazily {
+    [super _buildViewHierarchyLazily];
+}
+
+- (void)adjustForRTL
+{
+    if (![WXUtility enableRTLLayoutDirection]) return;
+    
+    // this is scroll rtl solution.
+    // scroll layout not use direction, use self tranform
+    if (self.view && _flexCssNode && 
_flexCssNode->getLayoutDirectionFromPathNode() == WeexCore::kDirectionRTL
+        ) {
+        WXRecycleSliderView *slider = (WXRecycleSliderView *)self.view;
+        CATransform3D transform = CATransform3DScale(CATransform3DIdentity, 
-1, 1, 1);
+        slider.scrollView.layer.transform = transform ;
+    } else {
+        WXRecycleSliderView *slider = (WXRecycleSliderView *)self.view;
+        slider.scrollView.layer.transform = CATransform3DIdentity ;
+    }
+
+}
+
+- (void)_adjustForRTL {
+    if (![WXUtility enableRTLLayoutDirection]) return;
+    
+    [super _adjustForRTL];
+    [self adjustForRTL];
+}
+
+- (BOOL)shouldTransformSubviewsWhenRTL {
+    return YES;
+}
+
 - (void)viewDidUnload
 {
     [_childrenView removeAllObjects];
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXScrollerComponent.mm 
b/ios/sdk/WeexSDK/Sources/Component/WXScrollerComponent.mm
index a27820222b..50256abfb5 100644
--- a/ios/sdk/WeexSDK/Sources/Component/WXScrollerComponent.mm
+++ b/ios/sdk/WeexSDK/Sources/Component/WXScrollerComponent.mm
@@ -19,6 +19,7 @@
 
 #import "WXScrollerComponent.h"
 #import "WXComponent_internal.h"
+#import "WXSDKInstance_private.h"
 #import "WXComponent.h"
 #import "WXDefine.h"
 #import "WXConvert.h"
@@ -415,6 +416,33 @@ - (void)removeStickyComponent:(WXComponent *)sticky
     }
 }
 
+- (void)adjustForRTL
+{
+    if (![WXUtility enableRTLLayoutDirection]) return;
+    
+    // this is scroll rtl solution.
+    // scroll layout not use direction, use self tranform
+    if (self.view && _flexCssNode && 
_flexCssNode->getLayoutDirectionFromPathNode() == WeexCore::kDirectionRTL
+        ) {
+        if (_transform) {
+            self.view.layer.transform = 
CATransform3DConcat(self.view.layer.transform, 
CATransform3DScale(CATransform3DIdentity, -1, 1, 1));
+        } else {
+            self.view.layer.transform = 
CATransform3DScale(CATransform3DIdentity, -1, 1, 1);
+        }
+    } else {
+        if (!_transform) {
+            self.view.layer.transform = CATransform3DIdentity;
+        }
+    }
+}
+
+- (void)_adjustForRTL {
+    if (![WXUtility enableRTLLayoutDirection]) return;
+    
+    [super _adjustForRTL];
+    [self adjustForRTL];
+}
+
 - (void)adjustSticky
 {
     if (![self isViewLoaded]) {
@@ -1044,7 +1072,7 @@ - (void)_layoutPlatform
             float left = _flexCssNode->getLayoutPositionLeft();
             float width = _flexCssNode->getLayoutWidth();
             float height = _flexCssNode->getLayoutHeight();
-            
+
             if (_scrollDirection == WXScrollDirectionVertical) {
                 _flexCssNode->setFlexDirection(WeexCore::kFlexDirectionColumn, 
NO);
                 _flexCssNode->setStyleWidth(_flexCssNode->getLayoutWidth(), 
NO);
@@ -1054,7 +1082,14 @@ - (void)_layoutPlatform
                 _flexCssNode->setStyleHeight(_flexCssNode->getLayoutHeight());
                 _flexCssNode->setStyleWidth(FlexUndefined, NO);
             }
+
             _flexCssNode->markAllDirty();
+            
+            // this is scroll rtl solution.
+            // scroll layout not use direction, use self tranform
+            // but we need inherit direction in CSS, so we set children layout 
diretion manually
+            
_flexCssNode->determineChildLayoutDirection(_flexCssNode->getLayoutDirectionFromPathNode());
+            
             std::pair<float, float> renderPageSize;
             renderPageSize.first = self.weexInstance.frame.size.width;
             renderPageSize.second = self.weexInstance.frame.size.height;
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.mm 
b/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.mm
index e8683aac9c..f4c21a9527 100644
--- a/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.mm
+++ b/ios/sdk/WeexSDK/Sources/Component/WXTextComponent.mm
@@ -139,7 +139,6 @@ @implementation WXTextComponent
     WXTextStyle _fontStyle;
     NSUInteger _lines;
     NSTextAlignment _textAlign;
-    NSString *_direction;
     WXTextDecoration _textDecoration;
     NSString *_textOverflow;
     CGFloat _lineHeight;
@@ -273,7 +272,6 @@ - (void)fillCSSStyles:(NSDictionary *)styles
     WX_STYLE_FILL_TEXT_PIXEL(lineHeight, lineHeight, YES)
     WX_STYLE_FILL_TEXT_PIXEL(letterSpacing, letterSpacing, YES)
     WX_STYLE_FILL_TEXT(wordWrap, wordWrap, NSString, YES);
-    WX_STYLE_FILL_TEXT(direction, direction, NSString, YES)
     if (_fontFamily && !_observerIconfont) {
         // notification received when custom icon font file download finish
         [[NSNotificationCenter defaultCenter] addObserver:self 
selector:@selector(repaintText:) name:WX_ICONFONT_DOWNLOAD_NOTIFICATION 
object:nil];
@@ -515,7 +513,7 @@ - (NSMutableAttributedString *)buildCTAttributeString
     NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
     
     // handle text direction style, default ltr
-    BOOL isRtl = [_direction isEqualToString:@"rtl"];
+    BOOL isRtl = [self isDirectionRTL];
     if (isRtl) {
         if (0 == _textAlign) {
             //force text right-align if don't specified any align.
@@ -598,7 +596,7 @@ - (NSAttributedString *)buildAttributeString
     NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
 
     // handle text direction style, default ltr
-    BOOL isRtl = [_direction isEqualToString:@"rtl"];
+    BOOL isRtl = [self isDirectionRTL];
     if (isRtl) {
         if (0 == _textAlign) {
             //force text right-align if don't specified any align.
diff --git a/ios/sdk/WeexSDK/Sources/Layout/WXComponent+Layout.h 
b/ios/sdk/WeexSDK/Sources/Layout/WXComponent+Layout.h
index a33884769c..518a163fae 100644
--- a/ios/sdk/WeexSDK/Sources/Layout/WXComponent+Layout.h
+++ b/ios/sdk/WeexSDK/Sources/Layout/WXComponent+Layout.h
@@ -43,6 +43,7 @@ extern "C" {
     CGRect _calculatedFrame;
     CGPoint _absolutePosition;
     WXPositionType _positionType;
+    BOOL _isLastLayoutDirectionRTL;
 }
 
 /**
@@ -69,4 +70,13 @@ extern "C" {
  */
 - (void)removeSubcomponentCssNode:(WXComponent *)subcomponent;
 
+#pragma mark - RTL
+
+@property (nonatomic, assign, readonly) BOOL isDirectionRTL;
+
+// Now we scrollView RTL solution is tranform
+// so scrollView need tranform subviews when RTL by default
+// if your component view is not scrollView but also implement RTL layout by 
tranform,you need return YES
+- (BOOL)shouldTransformSubviewsWhenRTL;
+
 @end
diff --git a/ios/sdk/WeexSDK/Sources/Layout/WXComponent+Layout.mm 
b/ios/sdk/WeexSDK/Sources/Layout/WXComponent+Layout.mm
index 18cb24dbb1..14246e7a37 100644
--- a/ios/sdk/WeexSDK/Sources/Layout/WXComponent+Layout.mm
+++ b/ios/sdk/WeexSDK/Sources/Layout/WXComponent+Layout.mm
@@ -153,11 +153,26 @@ - (void)_frameDidCalculated:(BOOL)isChanged
                 [strongSelf->_transform applyTransformForView:strongSelf.view];
             }
             
+            [self _adjustForRTL];
+            
             if (strongSelf->_backgroundImage) {
                 [strongSelf setGradientLayer];
             }
             [strongSelf setNeedsDisplay];
         }];
+    } else {
+        // if frame is not change, we still need check was layoutDirection 
changed
+        if ([self isDirectionRTL] != _isLastLayoutDirectionRTL) {
+            _isLastLayoutDirectionRTL = [self isDirectionRTL];
+            __weak typeof(self) weakSelf = self;
+            [self.weexInstance.componentManager _addUITask:^{
+                __strong typeof(weakSelf) strongSelf = weakSelf;
+                if (strongSelf->_transform) {
+                    [strongSelf->_transform 
applyTransformForView:strongSelf.view];
+                }
+                [strongSelf _adjustForRTL];
+            }];
+        }
     }
 }
 
@@ -168,6 +183,7 @@ - (void)_layoutDidFinish
     if (_positionType == WXPositionTypeSticky) {
         [self.ancestorScroller adjustSticky];
     }
+
     [self layoutDidFinish];
 }
 
@@ -183,9 +199,15 @@ - (void)_fillCSSNode:(NSDictionary *)styles 
isUpdate:(BOOL)isUpdate
     if (_flexCssNode == nullptr) {
         return;
     }
-    
+
     BOOL needLayout = NO;
     
+    // CSS direction for RTL Layout
+    if (styles[@"direction"]) {
+        _flexCssNode->setDirection([self fxDirection:styles[@"direction"]], 
isUpdate);
+        needLayout = YES;
+    }
+    
     // flex
     if (styles[@"flex"]) {
         _flexCssNode->set_flex([WXConvert CGFloat:styles[@"flex"]]);
@@ -453,7 +475,9 @@ - (void)_resetCSSNode:(NSArray *)styles
         if (styles.count<=0) {
             return;
         }
-        
+    
+        WX_FLEX_STYLE_RESET_CSS_NODE(direction, @(WeexCore::kDirectionInherit))
+    
         WX_FLEX_STYLE_RESET_CSS_NODE(flex, @0.0)
         WX_FLEX_STYLE_RESET_CSS_NODE(flexDirection, 
@(WeexCore::kFlexDirectionColumn))
         WX_FLEX_STYLE_RESET_CSS_NODE(alignItems, 
@(WeexCore::kAlignItemsStretch))
@@ -563,6 +587,18 @@ - (void)_resetCSSNode:(NSArray *)styles
     return WeexCore::kRelative;
 }
 
+- (WeexCore::WXCoreDirection)fxDirection:(id)value
+{
+    if([value isKindOfClass:[NSString class]]){
+        if ([value isEqualToString:@"rtl"]) {
+            return WeexCore::kDirectionRTL;
+        } else if ([value isEqualToString:@"ltr"]) {
+            return WeexCore::kDirectionLTR;
+        }
+    }
+    return WeexCore::kDirectionInherit;
+}
+
 - (WeexCore::WXCoreFlexDirection)fxFlexDirection:(id)value
 {
     if([value isKindOfClass:[NSString class]]){
@@ -677,4 +713,39 @@ - (void)removeSubcomponentCssNode:(WXComponent 
*)subcomponent
     }
 }
 
+#pragma mark - RTL
+
+- (BOOL)isDirectionRTL {
+    if (![WXUtility enableRTLLayoutDirection]) return NO;
+    
+    WeexCore::WXCoreDirection direction = _flexCssNode == nullptr ? 
WeexCore::WEEXCORE_CSS_DEFAULT_DIRECTION : 
_flexCssNode->getLayoutDirectionFromPathNode();
+    if (direction != WeexCore::kDirectionInherit) return direction == 
WeexCore::kDirectionRTL;
+    return NO;
+}
+
+- (void)_adjustForRTL {
+    if (![WXUtility enableRTLLayoutDirection]) return;
+    
+    if (self->_positionType == WXPositionTypeFixed) return;
+    
+    if (self.supercomponent && self.supercomponent->_flexCssNode && 
self.supercomponent->_flexCssNode->getLayoutDirectionFromPathNode() == 
WeexCore::kDirectionRTL && [self.supercomponent 
shouldTransformSubviewsWhenRTL]) {
+        if (_transform) {
+            self.view.layer.transform = 
CATransform3DConcat(self.view.layer.transform, 
CATransform3DScale(CATransform3DIdentity, -1, 1, 1));
+        } else {
+            self.view.layer.transform = 
CATransform3DScale(CATransform3DIdentity, -1, 1, 1);
+        }
+    } else {
+        if (!_transform) {
+            self.view.layer.transform = CATransform3DIdentity;
+        }
+    }
+}
+
+// Now we scrollView RTL solution is tranform
+// so scrollView need tranform subviews when RTL by default
+// if your component view is not scrollView but also implement RTL layout by 
tranform,you need return YES
+- (BOOL)shouldTransformSubviewsWhenRTL {
+    return [self.view isKindOfClass:[UIScrollView class]];
+}
+
 @end
diff --git a/ios/sdk/WeexSDK/Sources/Model/WXComponent.mm 
b/ios/sdk/WeexSDK/Sources/Model/WXComponent.mm
index 43fda8b476..f69f506a4e 100644
--- a/ios/sdk/WeexSDK/Sources/Model/WXComponent.mm
+++ b/ios/sdk/WeexSDK/Sources/Model/WXComponent.mm
@@ -383,11 +383,13 @@ - (UIView *)view
         if (_backgroundImage) {
             [self setGradientLayer];
         }
-        
+
         if (_transform) {
             [_transform applyTransformForView:_view];
         }
         
+        [self _adjustForRTL];
+        
         if (_boxShadow) {
             [self configBoxShadow:_boxShadow];
         }
@@ -801,6 +803,7 @@ - (void)setNativeTransform:(CGAffineTransform)transform
     self.transform = [[WXTransform alloc] 
initWithNativeTransform:CATransform3DMakeAffineTransform(transform) 
instance:self.weexInstance];
     if (!CGRectEqualToRect(self.calculatedFrame, CGRectZero)) {
         [_transform applyTransformForView:_view];
+        [self _adjustForRTL];
         [_layer setNeedsDisplay];
     }
 }
diff --git a/ios/sdk/WeexSDK/Sources/Model/WXSDKInstance.m 
b/ios/sdk/WeexSDK/Sources/Model/WXSDKInstance.m
index e0cc3b8368..4a04c1443b 100644
--- a/ios/sdk/WeexSDK/Sources/Model/WXSDKInstance.m
+++ b/ios/sdk/WeexSDK/Sources/Model/WXSDKInstance.m
@@ -447,6 +447,9 @@ - (BOOL)_handleConfigCenter
         
         BOOL useJSCApiForCreateInstance = [[configCenter 
configForKey:@"iOS_weex_ext_config.useJSCApiForCreateInstance" 
defaultValue:@(YES) isDefault:NULL] boolValue];
         [WXUtility setUseJSCApiForCreateInstance:useJSCApiForCreateInstance];
+               
+        BOOL enableRTLLayoutDirection = [[configCenter 
configForKey:@"iOS_weex_ext_config.enableRTLLayoutDirection" 
defaultValue:@(YES) isDefault:NULL] boolValue];
+        [WXUtility setEnableRTLLayoutDirection:enableRTLLayoutDirection];
 
         BOOL shoudMultiContext = NO;
         shoudMultiContext = [[configCenter 
configForKey:@"iOS_weex_ext_config.createInstanceUsingMutliContext" 
defaultValue:@(YES) isDefault:NULL] boolValue];
diff --git a/ios/sdk/WeexSDK/Sources/Module/WXAnimationModule.m 
b/ios/sdk/WeexSDK/Sources/Module/WXAnimationModule.m
index 75b09d8d01..3b52110ab9 100644
--- a/ios/sdk/WeexSDK/Sources/Module/WXAnimationModule.m
+++ b/ios/sdk/WeexSDK/Sources/Module/WXAnimationModule.m
@@ -93,6 +93,7 @@ -(void)applyTransform
     if ([_animationInfo.propertyName hasPrefix:@"transform"]) {
         WXTransform *transform = _animationInfo.target->_transform;
         [transform applyTransformForView:_animationInfo.target.view];
+        [_animationInfo.target _adjustForRTL];
     } else if ([_animationInfo.propertyName 
isEqualToString:@"backgroundColor"]) {
         _animationInfo.target.view.layer.backgroundColor = (__bridge 
CGColorRef _Nullable)(_animationInfo.toValue);
     } else if ([_animationInfo.propertyName isEqualToString:@"opacity"]) {
diff --git a/ios/sdk/WeexSDK/Sources/Module/WXDomModule.m 
b/ios/sdk/WeexSDK/Sources/Module/WXDomModule.m
index 312d6f879e..a3de95b683 100644
--- a/ios/sdk/WeexSDK/Sources/Module/WXDomModule.m
+++ b/ios/sdk/WeexSDK/Sources/Module/WXDomModule.m
@@ -18,6 +18,7 @@
  */
 
 #import "WXDomModule.h"
+#import "WXComponent+Layout.h"
 #import "WXDefine.h"
 #import "WXSDKManager.h"
 #import "WXComponentManager.h"
@@ -54,6 +55,7 @@ @implementation WXDomModule
 WX_EXPORT_METHOD(@selector(updateAttrs:attrs:))
 WX_EXPORT_METHOD(@selector(addRule:rule:))
 WX_EXPORT_METHOD(@selector(getComponentRect:callback:))
+WX_EXPORT_METHOD(@selector(getLayoutDirection:callback:))
 WX_EXPORT_METHOD(@selector(updateComponentData:componentData:callback:))
 WX_EXPORT_METHOD(@selector(beginBatchMark))
 WX_EXPORT_METHOD(@selector(endBatchMark))
@@ -261,6 +263,30 @@ - (void)getComponentRect:(NSString*)ref 
callback:(WXModuleKeepAliveCallback)call
     }];
 }
 
+- (void)getLayoutDirection:(NSString*)ref 
callback:(WXModuleKeepAliveCallback)callback {
+    [self performBlockOnComponentManager:^(WXComponentManager * manager) {
+        if ([ref isEqualToString:@"viewport"]) {
+            dispatch_async(dispatch_get_main_queue(), ^{
+                NSString *direction = [WXUtility getEnvLayoutDirection] == 
WXLayoutDirectionRTL ? @"rtl" : @"ltr";
+                if (callback) {
+                    callback(direction, false);
+                }
+            });
+        } else {
+            WXComponent *component = [manager componentForRef:ref];
+            dispatch_async(dispatch_get_main_queue(), ^{
+                NSString *direction = @"unknow";
+                if (component) {
+                    direction = [component isDirectionRTL] ? @"rtl" : @"ltr";
+                }
+                if (callback) {
+                    callback(direction, false);
+                }
+            });
+        }
+    }];
+}
+
 - (void)updateComponentData:(NSString*)componentDataId 
componentData:(NSDictionary*)componentData callback:(NSString*)callbackId
 {
     NSString *recycleListComponentRef = [[componentDataId 
componentsSeparatedByString:@"@"] objectAtIndex:0];
diff --git a/ios/sdk/WeexSDK/Sources/Protocol/WXScrollerProtocol.h 
b/ios/sdk/WeexSDK/Sources/Protocol/WXScrollerProtocol.h
index 6cbb245e31..9b72ba3f2f 100644
--- a/ios/sdk/WeexSDK/Sources/Protocol/WXScrollerProtocol.h
+++ b/ios/sdk/WeexSDK/Sources/Protocol/WXScrollerProtocol.h
@@ -73,7 +73,13 @@
 - (WXScrollDirection)scrollDirection;
 
 @optional
+
 - (NSString*)refreshType;
 - (BOOL)requestGestureShouldStopPropagation:(UIGestureRecognizer 
*)gestureRecognizer shouldReceiveTouch:(UITouch *)touch;
+/**
+ * @abstract adjust for RTL
+ */
+- (void)adjustForRTL;
+
 @end
 
diff --git a/ios/sdk/WeexSDK/Sources/Utility/WXType.h 
b/ios/sdk/WeexSDK/Sources/Utility/WXType.h
index a33b9cc307..2ef5c84ac3 100644
--- a/ios/sdk/WeexSDK/Sources/Utility/WXType.h
+++ b/ios/sdk/WeexSDK/Sources/Utility/WXType.h
@@ -20,6 +20,12 @@
 #import <Foundation/Foundation.h>
 #import <UIKit/UIKit.h>
 
+typedef NS_ENUM(NSUInteger, WXLayoutDirection) {
+    WXLayoutDirectionLTR,
+    WXLayoutDirectionRTL,
+    WXLayoutDirectionAuto,
+};
+
 typedef NS_ENUM(NSUInteger, WXComponentType) {
     WXComponentTypeCommon = 0,
     WXComponentTypeVirtual
diff --git a/ios/sdk/WeexSDK/Sources/Utility/WXUtility.h 
b/ios/sdk/WeexSDK/Sources/Utility/WXUtility.h
index 601727a6eb..730c56080b 100644
--- a/ios/sdk/WeexSDK/Sources/Utility/WXUtility.h
+++ b/ios/sdk/WeexSDK/Sources/Utility/WXUtility.h
@@ -131,6 +131,8 @@ _Nonnull SEL WXSwizzledSelectorForSelector(_Nonnull SEL 
selector);
 
 + (NSDictionary *_Nonnull)getDebugEnvironment;
 
++ (WXLayoutDirection)getEnvLayoutDirection;
+
 /**
  * @abstract UserAgent Generation
  *
@@ -489,6 +491,10 @@ BOOL WXFloatGreaterThanWithPrecision(CGFloat a,CGFloat 
b,double precision);
 
 + (BOOL)useJSCApiForCreateInstance;
 
++ (void)setEnableRTLLayoutDirection:(BOOL)value;
+
++ (BOOL)enableRTLLayoutDirection;
+
 + (long) getUnixFixTimeMillis;
 
 + (NSArray<NSString *> *_Nullable)extractPropertyNamesOfJSValueObject:(JSValue 
*_Nullable)jsvalue;
diff --git a/ios/sdk/WeexSDK/Sources/Utility/WXUtility.m 
b/ios/sdk/WeexSDK/Sources/Utility/WXUtility.m
index 121a73c9c2..b070a87baf 100644
--- a/ios/sdk/WeexSDK/Sources/Utility/WXUtility.m
+++ b/ios/sdk/WeexSDK/Sources/Utility/WXUtility.m
@@ -44,6 +44,7 @@
 
 static BOOL unregisterFontWhenCollision = NO;
 static BOOL useJSCApiForCreateInstance = YES;
+static BOOL enableRTLLayoutDirection = YES;
 
 void WXPerformBlockOnMainThread(void (^ _Nonnull block)(void))
 {
@@ -173,6 +174,18 @@ + (void)_performBlock:(void (^)(void))block
     block();
 }
 
++ (WXLayoutDirection)getEnvLayoutDirection {
+    // We not use the below technique, because your app maybe not support the 
first preferredLanguages
+    // _sysLayoutDirection = [NSLocale 
characterDirectionForLanguage:[[NSLocale preferredLanguages] objectAtIndex:0]] 
== NSLocaleLanguageDirectionRightToLeft ? WXLayoutDirectionRTL : 
WXLayoutDirectionLTR;
+    if (@available(iOS 9.0, *)) {
+        // The view is shown in right-to-left mode right now.
+        return [UIView 
userInterfaceLayoutDirectionForSemanticContentAttribute:UISemanticContentAttributeUnspecified]
 == UIUserInterfaceLayoutDirectionRightToLeft ? WXLayoutDirectionRTL : 
WXLayoutDirectionLTR;
+    } else {
+        // Use the previous technique
+        return [[UIApplication sharedApplication] 
userInterfaceLayoutDirection] == UIUserInterfaceLayoutDirectionRightToLeft ? 
WXLayoutDirectionRTL : WXLayoutDirectionLTR;
+    }
+}
+
 + (NSDictionary *)getEnvironment
 {
     NSString *platform = @"iOS";
@@ -197,7 +210,8 @@ + (NSDictionary *)getEnvironment
                                     @"deviceWidth":@(deviceWidth * scale),
                                     @"deviceHeight":@(deviceHeight * scale),
                                     @"scale":@(scale),
-                                    @"logLevel":[WXLog logLevelString] ?: 
@"error"
+                                    @"logLevel":[WXLog logLevelString] ?: 
@"error",
+                                    @"layoutDirection": [self 
getEnvLayoutDirection] == WXLayoutDirectionRTL ? @"rtl" : @"ltr"
                                 }];
     
     if ([[[UIDevice currentDevice] systemVersion] integerValue] >= 11) {
@@ -736,6 +750,17 @@ + (CGFloat)defaultPixelScaleFactor
     return defaultScaleFactor;
 }
 
+#pragma mark - RTL
+
++ (void)setEnableRTLLayoutDirection:(BOOL)value
+{
+    enableRTLLayoutDirection = value;
+}
+
++ (BOOL)enableRTLLayoutDirection
+{
+    return enableRTLLayoutDirection;
+}
 
 #pragma mark - get deviceID
 + (NSString *)getDeviceID {
diff --git a/ios/sdk/WeexSDK/Sources/View/WXComponent+ViewManagement.mm 
b/ios/sdk/WeexSDK/Sources/View/WXComponent+ViewManagement.mm
index ad178903bc..98cf737f02 100644
--- a/ios/sdk/WeexSDK/Sources/View/WXComponent+ViewManagement.mm
+++ b/ios/sdk/WeexSDK/Sources/View/WXComponent+ViewManagement.mm
@@ -263,6 +263,7 @@ - (void)_updateViewStyles:(NSDictionary *)styles
         WXTransform* transform = [[WXTransform alloc] 
initWithCSSValue:[WXConvert NSString:styles[@"transform"]] origin:[WXConvert 
NSString:transformOrigin] instance:self.weexInstance];
         if (!CGRectEqualToRect(self.calculatedFrame, CGRectZero)) {
             [transform applyTransformForView:_view];
+            [self _adjustForRTL];
             [_layer setNeedsDisplay];
         }
         self.transform = transform;
@@ -270,9 +271,14 @@ - (void)_updateViewStyles:(NSDictionary *)styles
         [_transform setTransformOrigin:[WXConvert 
NSString:styles[@"transformOrigin"]]];
         if (!CGRectEqualToRect(self.calculatedFrame, CGRectZero)) {
             [_transform applyTransformForView:_view];
+            [self _adjustForRTL];
             [_layer setNeedsDisplay];
         }
     }
+    // for RTL
+    if (styles[@"direction"]) {
+        [self _adjustForRTL];
+    }
 }
 
 - (void)resetBorder:(NSArray *)styles
diff --git 
a/weex_core/Source/android/jniprebuild/jniheader/NativeRenderObjectUtils_jni.h 
b/weex_core/Source/android/jniprebuild/jniheader/NativeRenderObjectUtils_jni.h
index 55eefa2760..78c51c7020 100644
--- 
a/weex_core/Source/android/jniprebuild/jniheader/NativeRenderObjectUtils_jni.h
+++ 
b/weex_core/Source/android/jniprebuild/jniheader/NativeRenderObjectUtils_jni.h
@@ -50,6 +50,9 @@ static void AddChildRenderObject(JNIEnv* env, jclass jcaller,
     jlong parent,
     jlong child);
 
+static jint RenderObjectGetLayoutDirectionFromPathNode(JNIEnv* env, jclass 
jcaller,
+    jlong ptr);
+
 static jboolean RenderObjectHasNewLayout(JNIEnv* env, jclass jcaller,
     jlong ptr);
 
@@ -147,6 +150,11 @@ static const JNINativeMethod 
kMethodsNativeRenderObjectUtils[] = {
 "J"
 ")"
 "V", reinterpret_cast<void*>(AddChildRenderObject) },
+    { "nativeRenderObjectGetLayoutDirectionFromPathNode",
+"("
+"J"
+")"
+"I", reinterpret_cast<void*>(RenderObjectGetLayoutDirectionFromPathNode) },
     { "nativeRenderObjectHasNewLayout",
 "("
 "J"
diff --git a/weex_core/Source/android/utils/params_utils.cpp 
b/weex_core/Source/android/utils/params_utils.cpp
index be663eedd0..47651efff2 100644
--- a/weex_core/Source/android/utils/params_utils.cpp
+++ b/weex_core/Source/android/utils/params_utils.cpp
@@ -293,6 +293,22 @@ std::vector<INIT_FRAMEWORK_PARAMS*> initFromParam(
   ADDSTRING(appVersion);
   env->DeleteLocalRef(appVersion);
 
+  jmethodID m_layoutDirection =
+          env->GetMethodID(c_params, "getLayoutDirection", 
"()Ljava/lang/String;");
+  if (m_layoutDirection == nullptr) {
+    ADDSTRING(nullptr);
+    ReportNativeInitStatus("-1012", "get m_layoutDirection failed");
+    return initFrameworkParams;
+  }
+  jobject layoutDirection = env->CallObjectMethod(params, m_layoutDirection);
+  if (layoutDirection == nullptr) {
+    ADDSTRING(nullptr);
+    ReportNativeInitStatus("-1012", "get layoutDirection failed");
+    return initFrameworkParams;
+  }
+  ADDSTRING(layoutDirection);
+  env->DeleteLocalRef(layoutDirection);
+
   jmethodID m_weexVersion =
       env->GetMethodID(c_params, "getWeexVersion", "()Ljava/lang/String;");
   if (m_weexVersion == nullptr) {
diff --git 
a/weex_core/Source/android/wrap/native_render_object_utils_impl_android.cpp 
b/weex_core/Source/android/wrap/native_render_object_utils_impl_android.cpp
index 5565075a01..8b6d9f1f0c 100644
--- a/weex_core/Source/android/wrap/native_render_object_utils_impl_android.cpp
+++ b/weex_core/Source/android/wrap/native_render_object_utils_impl_android.cpp
@@ -128,8 +128,11 @@ static jint LayoutRenderObject(JNIEnv* env, jclass jcaller,
     return (jint)render->getLayoutHeight();
 }
 
-
-
+static jint RenderObjectGetLayoutDirectionFromPathNode(JNIEnv* env, jclass 
jcaller,
+                                   jlong ptr){
+    RenderObject* renderObject = convert_long_to_render_object(ptr);
+    return renderObject->getLayoutDirectionFromPathNode();
+}
 
 static jboolean RenderObjectHasNewLayout(JNIEnv* env, jclass jcaller,
                                      jlong ptr){
diff --git a/weex_core/Source/core/css/constants_name.h 
b/weex_core/Source/core/css/constants_name.h
index 48b8a5910d..fe282ccab2 100644
--- a/weex_core/Source/core/css/constants_name.h
+++ b/weex_core/Source/core/css/constants_name.h
@@ -23,6 +23,7 @@
 #include <string>
 
 namespace WeexCore {
+  constexpr char DIRECTION[] = "direction";
 
   constexpr char FLEX[] = "flex";
   constexpr char HORIZONTAL[] = "horizontal";
diff --git a/weex_core/Source/core/css/constants_value.h 
b/weex_core/Source/core/css/constants_value.h
index d51ebb52ef..b2a18621ce 100644
--- a/weex_core/Source/core/css/constants_value.h
+++ b/weex_core/Source/core/css/constants_value.h
@@ -23,6 +23,10 @@
 #include <string>
 
 namespace WeexCore {
+  // direction
+  constexpr char RTL[] = "rtl";
+  constexpr char LTR[] = "ltr";
+  constexpr char INHERIT[] = "inherit";
 
   // flex-direction
   constexpr char ROW[] = "row";
diff --git a/weex_core/Source/core/css/css_value_getter.cpp 
b/weex_core/Source/core/css/css_value_getter.cpp
index f576245e32..a60529f159 100644
--- a/weex_core/Source/core/css/css_value_getter.cpp
+++ b/weex_core/Source/core/css/css_value_getter.cpp
@@ -24,6 +24,17 @@
 #include "core/layout/style.h"
 
 namespace WeexCore {
+  const WXCoreDirection GetWXCoreDirection(const std::string &value) {
+    const char *c_value = value.c_str();
+    if(strcmp(c_value, INHERIT) == 0) {
+      return WeexCore::kDirectionInherit;
+    } else if (strcmp(c_value, LTR) == 0) {
+      return WeexCore::kDirectionLTR;
+    } else if (strcmp(c_value, RTL) == 0) {
+      return WeexCore::kDirectionRTL;
+    } 
+    return WeexCore::kDirectionLTR;
+  }
 
   const WXCoreFlexDirection GetWXCoreFlexDirection(const std::string &value) {
     const char *c_value = value.c_str();
diff --git a/weex_core/Source/core/css/css_value_getter.h 
b/weex_core/Source/core/css/css_value_getter.h
index f47abc18d0..ec630ca9b9 100644
--- a/weex_core/Source/core/css/css_value_getter.h
+++ b/weex_core/Source/core/css/css_value_getter.h
@@ -23,6 +23,7 @@
 #include <string>
 
 namespace WeexCore {
+  const WXCoreDirection GetWXCoreDirection(const std::string &value);
 
   const WXCoreFlexDirection GetWXCoreFlexDirection(const std::string &value);
 
diff --git a/weex_core/Source/core/layout/flex_enum.h 
b/weex_core/Source/core/layout/flex_enum.h
index 9eb3676354..6f63e99c39 100644
--- a/weex_core/Source/core/layout/flex_enum.h
+++ b/weex_core/Source/core/layout/flex_enum.h
@@ -16,13 +16,23 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+
 #ifdef __cplusplus
 
 #ifndef WEEXCORE_FLEXLAYOUT_WXCOREFLEXENUM_H
 #define WEEXCORE_FLEXLAYOUT_WXCOREFLEXENUM_H
 
-namespace WeexCore {
+#define WEEXCORE_CSS_DEFAULT_DIRECTION kDirectionLTR
 
+namespace WeexCore {
+    /**
+     * MainAxis direction
+     */
+    enum WXCoreDirection {
+        kDirectionInherit,
+        kDirectionLTR,
+        kDirectionRTL
+    };
   /**
    * MainAxis direction
    */
diff --git a/weex_core/Source/core/layout/layout.cpp 
b/weex_core/Source/core/layout/layout.cpp
index 6b7a2900b4..8ffdf48b28 100644
--- a/weex_core/Source/core/layout/layout.cpp
+++ b/weex_core/Source/core/layout/layout.cpp
@@ -645,21 +645,39 @@ namespace WeexCore {
 
   void WXCoreLayoutNode::onLayout(const float left, const float top, const 
float right, const float bottom,
                                   WXCoreLayoutNode *const absoulteItem, 
WXCoreFlexLine *const flexLine) {
-    switch (mCssStyle->mFlexDirection) {
-      case kFlexDirectionRow:
-        layoutHorizontal(false, left, top, right, bottom, absoulteItem, 
flexLine);
-        break;
-      case kFlexDirectionRowReverse:
-        layoutHorizontal(true, left, top, right, bottom, absoulteItem, 
flexLine);
-        break;
-      case kFlexDirectionColumnReverse:
-        layoutVertical(mCssStyle->mFlexWrap == kWrapReverse, true, left, top, 
right, bottom, absoulteItem, flexLine);
-        break;
-      case kFlexDirectionColumn:
-      default:
-        layoutVertical(mCssStyle->mFlexWrap == kWrapReverse, false, left, top, 
right, bottom, absoulteItem, flexLine);
-        break;
-    }
+      // determin direction
+      if (mLayoutResult->mLayoutDirection == kDirectionInherit) {
+          if(mCssStyle->mDirection == kDirectionInherit) {
+              // default direction in css is inherit, inherit direction from 
parent node
+              mLayoutResult->mLayoutDirection = NULL == mParent ? 
WEEXCORE_CSS_DEFAULT_DIRECTION : mParent->getLayoutDirection();
+          } else {
+              // specific direction in current Node's style
+              mLayoutResult->mLayoutDirection = mCssStyle->mDirection;
+          }
+      }
+      
+      bool verticalRTL = false;
+      if (mCssStyle->mFlexWrap != kNoWrap && mLayoutResult->mLayoutDirection 
== kDirectionRTL) {
+          verticalRTL = mCssStyle->mFlexWrap != kWrapReverse;
+      } else {
+          verticalRTL = mCssStyle->mFlexWrap == kWrapReverse;
+      }
+      
+      switch (mCssStyle->mFlexDirection) {
+        case kFlexDirectionRow:
+            layoutHorizontal(mLayoutResult->mLayoutDirection == kDirectionRTL, 
left, top, right, bottom, absoulteItem, flexLine);
+            break;
+        case kFlexDirectionRowReverse:
+            layoutHorizontal(mLayoutResult->mLayoutDirection != kDirectionRTL, 
left, top, right, bottom, absoulteItem, flexLine);
+            break;
+          case kFlexDirectionColumnReverse:
+              layoutVertical(verticalRTL, true, left, top, right, bottom, 
absoulteItem, flexLine);
+              break;
+          case kFlexDirectionColumn:
+          default:
+              layoutVertical(verticalRTL, false, left, top, right, bottom, 
absoulteItem, flexLine);
+              break;
+      }
   }
 
   /**
@@ -1083,6 +1101,34 @@ namespace WeexCore {
         break;
     }
   }
+    void WXCoreLayoutNode::determineChildLayoutDirection(const WXCoreDirection 
direction) {
+        for (Index i = 0; i < getChildCount(kBFC); ++i) {
+            WXCoreLayoutNode *child = getChildAt(kBFC, i);
+            // determin direction
+            if (child->mLayoutResult->mLayoutDirection == kDirectionInherit) {
+                if(child->mCssStyle->mDirection == kDirectionInherit) {
+                    // default direction in css is inherit, inherit direction 
from parent node
+                    child->mLayoutResult->mLayoutDirection = direction;
+                } else {
+                    // specific direction in current Node's style
+                    child->mLayoutResult->mLayoutDirection = 
child->mCssStyle->mDirection;
+                }
+            }
+        }
+    }
+    
+    WXCoreDirection WXCoreLayoutNode::getLayoutDirectionFromPathNode() {
+        WXCoreLayoutNode *node = this;
+        if (node->getLayoutDirection() != kDirectionInherit) return 
node->getLayoutDirection();
+        if (node->getDirection() != kDirectionInherit) {
+            node->mLayoutResult->mLayoutDirection = node->getDirection();
+            return node->getLayoutDirection();
+        } else if (nullptr != node->mParent) {
+            node->mLayoutResult->mLayoutDirection = 
node->mParent->getLayoutDirectionFromPathNode();
+            return node->getLayoutDirection();
+        }
+        return WEEXCORE_CSS_DEFAULT_DIRECTION;
+    }
 }
 
 
diff --git a/weex_core/Source/core/layout/layout.h 
b/weex_core/Source/core/layout/layout.h
index 9434932700..bd748e46e1 100644
--- a/weex_core/Source/core/layout/layout.h
+++ b/weex_core/Source/core/layout/layout.h
@@ -72,9 +72,10 @@ namespace WeexCore {
   };
 
   /**
-   * layout-result:layout-height、layout-width、position(left、right、top、bottom)
+   * 
layout-result:layout-height、layout-width、position(left、right、top、bottom)、direction
    */
   struct WXCorelayoutResult {
+    WXCoreDirection mLayoutDirection;
     WXCoreSize mLayoutSize;
     WXCorePosition mLayoutPosition;
 
@@ -83,8 +84,9 @@ namespace WeexCore {
     }
 
     inline void reset() {
-      mLayoutSize.reset();
-      mLayoutPosition.reset();
+        mLayoutSize.reset();
+        mLayoutPosition.reset();
+        mLayoutDirection = kDirectionInherit;
     }
   };
 
@@ -151,7 +153,6 @@ namespace WeexCore {
         mLayoutResult = new WXCorelayoutResult();
       }
 
-
       virtual ~WXCoreLayoutNode() {
         mIsDestroy = true;
         mHasNewLayout = true;
@@ -257,20 +258,22 @@ namespace WeexCore {
     inline void setContext(void * const context) {
       this->context = context;
     }
-
+      
     inline void copyStyle(WXCoreLayoutNode *srcNode) {
-      if (memcmp(mCssStyle, srcNode->mCssStyle, sizeof(WXCoreCSSStyle)) != 0) {
+      if (srcNode != nullptr && memcmp(mCssStyle, srcNode->mCssStyle, 
sizeof(WXCoreCSSStyle)) != 0) {
         memcpy(mCssStyle, srcNode->mCssStyle, sizeof(WXCoreCSSStyle));
         markDirty();
       }
     }
-
+      
     void copyFrom(WXCoreLayoutNode* srcNode){
-      memcpy(mCssStyle, srcNode->mCssStyle, sizeof(WXCoreCSSStyle));
+        if (srcNode == nullptr) return;
+        
+        memcpy(mCssStyle, srcNode->mCssStyle, sizeof(WXCoreCSSStyle));
     }
 
     inline void copyMeasureFunc(WXCoreLayoutNode *srcNode) {
-      if (memcmp(&measureFunc, &srcNode->measureFunc, 
sizeof(WXCoreMeasureFunc)) != 0) {
+      if (srcNode != nullptr && memcmp(&measureFunc, &srcNode->measureFunc, 
sizeof(WXCoreMeasureFunc)) != 0) {
         memcpy(&measureFunc, &srcNode->measureFunc, sizeof(WXCoreMeasureFunc));
         markDirty();
       }
@@ -607,8 +610,6 @@ namespace WeexCore {
 
     void positionAbsoluteFlexItem(float &left, float &top, float &right, float 
&bottom);
 
-    void onLayout(float left, float top, float right, float bottom, 
WXCoreLayoutNode* = nullptr, WXCoreFlexLine *const flexLine = nullptr);
-
     void layoutHorizontal(bool isRtl, float left, float top, float right, 
float bottom,
                           WXCoreLayoutNode*, WXCoreFlexLine *const flexLine);
 
@@ -668,7 +669,7 @@ namespace WeexCore {
 
 
   public:
-
+    virtual void onLayout(float left, float top, float right, float bottom, 
WXCoreLayoutNode* = nullptr, WXCoreFlexLine *const flexLine = nullptr);
     /** ================================ tree 
=================================== **/
 
     inline Index getChildCount(FormattingContext formattingContext) const {
@@ -972,7 +973,33 @@ namespace WeexCore {
       return mCssStyle->mMaxHeight;
     }
 
+      inline void setDirection(const WXCoreDirection direction, const bool 
updating) {
+          if (nullptr == mCssStyle) return;
+          
+          if (mCssStyle->mDirection != direction) {
+              mCssStyle->mDirection = direction;
+              markDirty();
+              if (updating) {
+                  for (auto it = ChildListIterBegin(); it != 
ChildListIterEnd(); it++) {
+                      (*it)->markInheritableDirty();
+                  }
+              }
+          }
+      }
 
+    inline WXCoreDirection getDirection() const {
+        if (mCssStyle == nullptr) {
+            return WEEXCORE_CSS_DEFAULT_DIRECTION;
+        }
+        return mCssStyle->mDirection;
+    }
+    
+    /** ================================ CSS direction For RTL 
=================================== **/
+      
+    void determineChildLayoutDirection(const WXCoreDirection direction);
+      
+    WXCoreDirection getLayoutDirectionFromPathNode();
+      
     /** ================================ flex-style 
=================================== **/
 
     inline void setFlexDirection(const WXCoreFlexDirection flexDirection, 
const bool updating) {
@@ -1070,7 +1097,18 @@ namespace WeexCore {
     inline float getLayoutPositionRight() const  {
       return mLayoutResult->mLayoutPosition.getPosition(kPositionEdgeRight);
     }
+      
+    virtual inline WXCoreDirection getLayoutDirection() const {
+      if (nullptr == mLayoutResult) {
+        return WEEXCORE_CSS_DEFAULT_DIRECTION;
+      }
+      return mLayoutResult->mLayoutDirection;
+    }
 
+    inline void setLayoutDirection(WXCoreDirection direction) {
+        if (nullptr == mLayoutResult) return;
+        mLayoutResult->mLayoutDirection = direction;
+    }
     inline bool hasNewLayout() const {
       return mHasNewLayout;
     }
@@ -1114,6 +1152,38 @@ namespace WeexCore {
       return ret;
     }
 
+    void markInheritableDirty() {
+        if (resetInheritableSet()) {
+            // if some style was inherited from parent, reset those styles
+            // then mark self dirty
+            markDirty(false);
+            
+            // traverse children to mark dirty
+            if(getChildCount() == 0){
+                return;
+            }
+            else {
+                for (auto it = ChildListIterBegin(); it != ChildListIterEnd(); 
it++) {
+                    (*it)->markInheritableDirty();
+                }
+            }
+        }
+    }
+      
+    /**
+    * if some style was inherited from parent, reset those styles, then return 
true, eles return false
+    */
+    bool resetInheritableSet() {
+      if (mCssStyle == nullptr || mLayoutResult == nullptr) return false;
+        
+      bool hasInheritedStyle = false;
+      if (mCssStyle->mDirection == kDirectionInherit) {
+          mLayoutResult->mLayoutDirection = kDirectionInherit;
+          hasInheritedStyle = true;
+      }
+      return hasInheritedStyle;
+    }
+      
     inline void setHasNewLayout(const bool hasNewLayout) {
       this->mHasNewLayout = hasNewLayout;
     }
diff --git a/weex_core/Source/core/layout/style.h 
b/weex_core/Source/core/layout/style.h
index a5c5f7eaa8..5c9ca89a24 100644
--- a/weex_core/Source/core/layout/style.h
+++ b/weex_core/Source/core/layout/style.h
@@ -201,6 +201,8 @@ namespace WeexCore {
     WXCoreAlignSelf mAlignSelf;
 
     WXCorePositionType mPositionType;
+      
+      WXCoreDirection mDirection;
 
     float mFlexGrow;
 
@@ -242,7 +244,8 @@ namespace WeexCore {
 
     constexpr static WXCorePositionType kWXCorePositionTypeDefault = kRelative;
 
-    WXCoreCSSStyle() : mFlexDirection(kFlexDirectionDefault),
+    WXCoreCSSStyle() : mDirection(kDirectionInherit),
+                       mFlexDirection(kFlexDirectionDefault),
                        mFlexWrap(kFlexWrapDefault),
                        mJustifyContent(kFlexJustifyContentDefault),
                        mAlignItems(kFlexAlignItemsDefault),
@@ -257,6 +260,7 @@ namespace WeexCore {
     }
 
     ~WXCoreCSSStyle() {
+      mDirection = kDirectionInherit;
       mFlexDirection = kFlexDirectionDefault;
       mFlexWrap = kFlexWrapDefault;
       mJustifyContent = kFlexJustifyContentDefault;
diff --git a/weex_core/Source/core/render/node/render_object.cpp 
b/weex_core/Source/core/render/node/render_object.cpp
index ffc8388f2b..ba547a79dd 100644
--- a/weex_core/Source/core/render/node/render_object.cpp
+++ b/weex_core/Source/core/render/node/render_object.cpp
@@ -160,6 +160,13 @@ StyleType RenderObject::ApplyStyle(const std::string &key,
       }
     }
     return kTypeLayout;
+  } else if (key == DIRECTION) {
+    WeexCore::WXCoreDirection direction = GetWXCoreDirection(value);
+    if (direction ==  WeexCore::kDirectionInherit && this->is_root_render_ ) {
+        direction = WeexCore::kDirectionLTR;
+    } 
+    setDirection(direction, updating);
+    return kTypeInheritableLayout;
   } else if (key == FLEX_DIRECTION) {
     setFlexDirection(GetWXCoreFlexDirection(value), updating);
     return kTypeLayout;
diff --git a/weex_core/Source/core/render/node/render_object.h 
b/weex_core/Source/core/render/node/render_object.h
index 5242a641ca..9522d9d7fe 100644
--- a/weex_core/Source/core/render/node/render_object.h
+++ b/weex_core/Source/core/render/node/render_object.h
@@ -44,7 +44,8 @@ typedef enum StyleType {
   kTypeLayout,
   kTypeMargin,
   kTypePadding,
-  kTypeBorder
+  kTypeBorder,
+  kTypeInheritableLayout
 } StyleType;
 
 class RenderObject : public IRenderObject {
diff --git a/weex_core/Source/core/render/node/render_scroller.cpp 
b/weex_core/Source/core/render/node/render_scroller.cpp
index 05e67e2e17..03d0b8d4a5 100644
--- a/weex_core/Source/core/render/node/render_scroller.cpp
+++ b/weex_core/Source/core/render/node/render_scroller.cpp
@@ -52,4 +52,11 @@ void RenderScroller::set_flex(const float flex) {
   this->is_set_flex_ = true;
   WXCoreLayoutNode::set_flex(flex);
 }
+    
+    void RenderScroller::onLayout(const float left, const float top, const 
float right, const float bottom,
+                                  WXCoreLayoutNode *const absoulteItem, 
WXCoreFlexLine *const flexLine) {
+        // In scroller only use left to right direction to caculate children 
frame
+        this->setLayoutDirection(kDirectionLTR);
+        RenderObject::onLayout(left, top, right, bottom, absoulteItem, 
flexLine);
+    }
 }  // namespace WeexCore
diff --git a/weex_core/Source/core/render/node/render_scroller.h 
b/weex_core/Source/core/render/node/render_scroller.h
index 59a35dff41..1cf6fc6389 100644
--- a/weex_core/Source/core/render/node/render_scroller.h
+++ b/weex_core/Source/core/render/node/render_scroller.h
@@ -37,6 +37,24 @@ class RenderScroller : public RenderObject {
       const float &current_length) const override {
     return NAN;
   }
+
+protected:
+    void onLayout(const float left, const float top, const float right, const 
float bottom,
+                                  WXCoreLayoutNode *const absoulteItem, 
WXCoreFlexLine *const flexLine) override;
+
+    // Since scroll only use ltr to layout children actually,
+    // so we need override this method to return calculated inherit direction 
as normal render_object do
+    inline WXCoreDirection getLayoutDirection() const override {
+        WXCoreDirection styleDirection = this->getDirection();
+        if (styleDirection != kDirectionInherit) {
+            return styleDirection;
+        } else if (this->getParent() != nullptr) {
+            WXCoreLayoutNode *parent = this->getParent();
+            return parent->getLayoutDirection();
+        }
+        return WEEXCORE_CSS_DEFAULT_DIRECTION;
+    }
+
 };
 }  // namespace WeexCore
 #endif  // CORE_RENDER_NODE_RENDER_SCROLLER_H_
diff --git a/weex_core/Source/core/render/page/render_page.cpp 
b/weex_core/Source/core/render/page/render_page.cpp
index d54fadee35..875cd3de09 100644
--- a/weex_core/Source/core/render/page/render_page.cpp
+++ b/weex_core/Source/core/render/page/render_page.cpp
@@ -221,7 +221,8 @@ bool RenderPage::UpdateStyle(
   std::vector<std::pair<std::string, std::string>> *margin = nullptr;
   std::vector<std::pair<std::string, std::string>> *padding = nullptr;
   std::vector<std::pair<std::string, std::string>> *border = nullptr;
-
+  bool inheriableLayout = false;
+    
   bool flag = false;
   int result =
       WeexCoreManager::Instance()
@@ -277,13 +278,16 @@ bool RenderPage::UpdateStyle(
                   flag = true;
               });
           break;
+          case kTypeInheritableLayout:
+              inheriableLayout = true;
+              break;
         default: break;
       }
     }
   }
 
   if (style != nullptr || margin != nullptr || padding != nullptr ||
-      border != nullptr)
+      border != nullptr || inheriableLayout)
     SendUpdateStyleAction(render, style, margin, padding, border);
 
   Batch();
diff --git a/weex_core/release.sh b/weex_core/release.sh
old mode 100644
new mode 100755


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


With regards,
Apache Git Services

Reply via email to