diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollContainerView.java b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollContainerView.java index fcd473f4edd..178007942d0 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollContainerView.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollContainerView.java @@ -64,7 +64,7 @@ public class ReactHorizontalScrollContainerView extends ReactViewGroup { // Call with the present values in order to re-layout if necessary ReactHorizontalScrollView parent = (ReactHorizontalScrollView) getParent(); // Fix the ScrollX position when using RTL language - int offsetX = parent.getScrollX() + getWidth() - mCurrentWidth; + int offsetX = parent.getScrollX() + getWidth() - mCurrentWidth - parent.getWidth(); parent.reactScrollTo(offsetX, parent.getScrollY()); } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java index 80e64aa9b1e..248ec77a808 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactHorizontalScrollView.java @@ -25,7 +25,6 @@ import android.view.accessibility.AccessibilityEvent; import android.widget.HorizontalScrollView; import android.widget.OverScroller; import androidx.annotation.Nullable; -import androidx.core.text.TextUtilsCompat; import androidx.core.view.AccessibilityDelegateCompat; import androidx.core.view.ViewCompat; import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; @@ -34,6 +33,7 @@ import com.facebook.infer.annotation.Assertions; import com.facebook.react.bridge.WritableMap; import com.facebook.react.bridge.WritableNativeMap; import com.facebook.react.common.ReactConstants; +import com.facebook.react.modules.i18nmanager.I18nUtil; import com.facebook.react.uimanager.FabricViewStateManager; import com.facebook.react.uimanager.MeasureSpecAssertions; import com.facebook.react.uimanager.PixelUtil; @@ -45,7 +45,6 @@ import com.facebook.react.views.view.ReactViewBackgroundManager; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; -import java.util.Locale; /** Similar to {@link ReactScrollView} but only supports horizontal scrolling. */ public class ReactHorizontalScrollView extends HorizontalScrollView @@ -55,6 +54,7 @@ public class ReactHorizontalScrollView extends HorizontalScrollView private static boolean sTriedToGetScrollerField = false; private static final String CONTENT_OFFSET_LEFT = "contentOffsetLeft"; private static final String CONTENT_OFFSET_TOP = "contentOffsetTop"; + private int mLayoutDirection; private static final int UNSET_CONTENT_OFFSET = -1; @@ -124,6 +124,10 @@ public class ReactHorizontalScrollView extends HorizontalScrollView }); mScroller = getOverScrollerFromParent(); + mLayoutDirection = + I18nUtil.getInstance().isRTL(context) + ? ViewCompat.LAYOUT_DIRECTION_RTL + : ViewCompat.LAYOUT_DIRECTION_LTR; } @Nullable @@ -401,7 +405,7 @@ public class ReactHorizontalScrollView extends HorizontalScrollView if (getChildCount() > 0) { View currentFocused = findFocus(); View nextFocused = FocusFinder.getInstance().findNextFocus(this, currentFocused, direction); - View rootChild = getChildAt(0); + View rootChild = getContentView(); if (rootChild != null && nextFocused != null && nextFocused.getParent() == rootChild) { if (!isScrolledInView(nextFocused) && !isMostlyScrolledInView(nextFocused)) { smoothScrollToNextPage(direction); @@ -532,7 +536,7 @@ public class ReactHorizontalScrollView extends HorizontalScrollView Assertions.assertNotNull(mClippingRect); ReactClippingViewGroupHelper.calculateClippingRect(this, mClippingRect); - View contentView = getChildAt(0); + View contentView = getContentView(); if (contentView instanceof ReactClippingViewGroup) { ((ReactClippingViewGroup) contentView).updateClippingRect(); } @@ -555,6 +559,11 @@ public class ReactHorizontalScrollView extends HorizontalScrollView return getWidth(); } + private View getContentView() { + View contentView = getChildAt(0); + return contentView; + } + public void setEndFillColor(int color) { if (color != mEndFillColor) { mEndFillColor = color; @@ -608,7 +617,7 @@ public class ReactHorizontalScrollView extends HorizontalScrollView @Override public void draw(Canvas canvas) { if (mEndFillColor != Color.TRANSPARENT) { - final View content = getChildAt(0); + final View content = getContentView(); if (mEndBackground != null && content != null && content.getRight() < getWidth()) { mEndBackground.setBounds(content.getRight(), 0, getWidth(), getHeight()); mEndBackground.draw(canvas); @@ -805,10 +814,7 @@ public class ReactHorizontalScrollView extends HorizontalScrollView int width = getWidth() - ViewCompat.getPaddingStart(this) - ViewCompat.getPaddingEnd(this); // offsets are from the right edge in RTL layouts - boolean isRTL = - TextUtilsCompat.getLayoutDirectionFromLocale(Locale.getDefault()) - == ViewCompat.LAYOUT_DIRECTION_RTL; - if (isRTL) { + if (mLayoutDirection == LAYOUT_DIRECTION_RTL) { targetOffset = maximumOffset - targetOffset; velocityX = -velocityX; } @@ -847,7 +853,7 @@ public class ReactHorizontalScrollView extends HorizontalScrollView // if scrolling after the last snap offset and snapping to the // end of the list is disabled, then we allow free scrolling int currentOffset = getScrollX(); - if (isRTL) { + if (mLayoutDirection == LAYOUT_DIRECTION_RTL) { currentOffset = maximumOffset - currentOffset; } if (!mSnapToEnd && targetOffset >= lastOffset) { @@ -881,7 +887,7 @@ public class ReactHorizontalScrollView extends HorizontalScrollView // Make sure the new offset isn't out of bounds targetOffset = Math.min(Math.max(0, targetOffset), maximumOffset); - if (isRTL) { + if (mLayoutDirection == LAYOUT_DIRECTION_RTL) { targetOffset = maximumOffset - targetOffset; velocityX = -velocityX; } @@ -1041,7 +1047,7 @@ public class ReactHorizontalScrollView extends HorizontalScrollView * @param y */ private void setPendingContentOffsets(int x, int y) { - View child = getChildAt(0); + View child = getContentView(); if (child != null && child.getWidth() != 0 && child.getHeight() != 0) { pendingContentOffsetX = UNSET_CONTENT_OFFSET; pendingContentOffsetY = UNSET_CONTENT_OFFSET; @@ -1063,12 +1069,23 @@ public class ReactHorizontalScrollView extends HorizontalScrollView mLastStateUpdateScrollX = scrollX; mLastStateUpdateScrollY = scrollY; + final int fabricScrollX; + if (mLayoutDirection == LAYOUT_DIRECTION_RTL) { + // getScrollX returns offset from left even when layout direction is RTL. + // The following line calculates offset from right. + View child = getContentView(); + int contentWidth = child != null ? child.getWidth() : 0; + fabricScrollX = -(contentWidth - scrollX - getWidth()); + } else { + fabricScrollX = scrollX; + } + mFabricViewStateManager.setState( new FabricViewStateManager.StateUpdateCallback() { @Override public WritableMap getStateUpdate() { WritableMap map = new WritableNativeMap(); - map.putDouble(CONTENT_OFFSET_LEFT, PixelUtil.toDIPFromPixel(scrollX)); + map.putDouble(CONTENT_OFFSET_LEFT, PixelUtil.toDIPFromPixel(fabricScrollX)); map.putDouble(CONTENT_OFFSET_TOP, PixelUtil.toDIPFromPixel(scrollY)); return map; }