From 0ee3b273a003c99d837df1debdb72106c684eae8 Mon Sep 17 00:00:00 2001 From: Alex Stetson Date: Thu, 16 Dec 2021 11:07:25 -0800 Subject: [PATCH] Fixed divider rendering in RTL languages In horizontal layouts, when elements are rendered from LTR, the definition of "before" and "after" gets flipped, causing the divider spacing to be allocated to the wrong locations. By flipping the check in RTL layouts, the dividers can be placed correctly. Additionally, it is necessary to add divider measurements to all layout width measurements to allow for dividers in right gravity to layout properly. Bug: 208927871 Test: atest android.widget.cts.LinearLayoutTest Change-Id: If6e917873571484a6fb1dfc28b356289e2cc1ffa --- core/java/android/widget/LinearLayout.java | 64 +++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java index fa84407c5c4db..7314ad83bd0cf 100644 --- a/core/java/android/widget/LinearLayout.java +++ b/core/java/android/widget/LinearLayout.java @@ -732,6 +732,10 @@ public class LinearLayout extends ViewGroup { * @hide Pending API consideration. Currently only used internally by the system. */ protected boolean hasDividerBeforeChildAt(int childIndex) { + if (mShowDividers == SHOW_DIVIDER_NONE) { + // Short-circuit to save iteration over child views. + return false; + } if (childIndex == getVirtualChildCount()) { // Check whether the end divider should draw. return (mShowDividers & SHOW_DIVIDER_END) != 0; @@ -745,6 +749,24 @@ public class LinearLayout extends ViewGroup { } } + /** + * Determines whether or not there's a divider after a specified child index. + * + * @param childIndex Index of child to check for following divider + * @return true if there should be a divider after the child at childIndex + */ + private boolean hasDividerAfterChildAt(int childIndex) { + if (mShowDividers == SHOW_DIVIDER_NONE) { + // Short-circuit to save iteration over child views. + return false; + } + if (allViewsAreGoneAfter(childIndex)) { + // This is the last view that's not gone, check if end divider is enabled. + return (mShowDividers & SHOW_DIVIDER_END) != 0; + } + return (mShowDividers & SHOW_DIVIDER_MIDDLE) != 0; + } + /** * Checks whether all (virtual) child views before the given index are gone. */ @@ -758,6 +780,20 @@ public class LinearLayout extends ViewGroup { return true; } + /** + * Checks whether all (virtual) child views after the given index are gone. + */ + private boolean allViewsAreGoneAfter(int childIndex) { + final int count = getVirtualChildCount(); + for (int i = childIndex + 1; i < count; i++) { + final View child = getVirtualChildAt(i); + if (child != null && child.getVisibility() != GONE) { + return false; + } + } + return true; + } + /** * Measures the children when the orientation of this LinearLayout is set * to {@link #VERTICAL}. @@ -1295,6 +1331,7 @@ public class LinearLayout extends ViewGroup { if (useLargestChild && (widthMode == MeasureSpec.AT_MOST || widthMode == MeasureSpec.UNSPECIFIED)) { mTotalLength = 0; + nonSkippedChildCount = 0; for (int i = 0; i < count; ++i) { final View child = getVirtualChildAt(i); @@ -1308,6 +1345,11 @@ public class LinearLayout extends ViewGroup { continue; } + nonSkippedChildCount++; + if (hasDividerBeforeChildAt(i)) { + mTotalLength += mDividerWidth; + } + final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams(); if (isExactly) { @@ -1319,6 +1361,10 @@ public class LinearLayout extends ViewGroup { lp.leftMargin + lp.rightMargin + getNextLocationOffset(child)); } } + + if (nonSkippedChildCount > 0 && hasDividerBeforeChildAt(count)) { + mTotalLength += mDividerWidth; + } } // Add in our padding @@ -1347,6 +1393,7 @@ public class LinearLayout extends ViewGroup { maxHeight = -1; mTotalLength = 0; + nonSkippedChildCount = 0; for (int i = 0; i < count; ++i) { final View child = getVirtualChildAt(i); @@ -1354,6 +1401,11 @@ public class LinearLayout extends ViewGroup { continue; } + nonSkippedChildCount++; + if (hasDividerBeforeChildAt(i)) { + mTotalLength += mDividerWidth; + } + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); final float childWeight = lp.weight; if (childWeight > 0) { @@ -1423,6 +1475,10 @@ public class LinearLayout extends ViewGroup { } } + if (nonSkippedChildCount > 0 && hasDividerBeforeChildAt(count)) { + mTotalLength += mDividerWidth; + } + // Add in our padding mTotalLength += mPaddingLeft + mPaddingRight; // TODO: Should we update widthSize with the new total length? @@ -1810,7 +1866,13 @@ public class LinearLayout extends ViewGroup { break; } - if (hasDividerBeforeChildAt(childIndex)) { + if (isLayoutRtl) { + // Because rtl rendering occurs in the reverse direction, we need to check + // after the child rather than before (since after=left in this context) + if (hasDividerAfterChildAt(childIndex)) { + childLeft += mDividerWidth; + } + } else if (hasDividerBeforeChildAt(childIndex)) { childLeft += mDividerWidth; }