From 7575833bda8f38a382e5634931ee8227d30eb194 Mon Sep 17 00:00:00 2001 From: Josh Yaganeh <319444+jyaganeh@users.noreply.github.com> Date: Fri, 28 Jun 2024 15:52:28 -0700 Subject: [PATCH] Fix linear layout size calculation to properly account for item margins (#1469) * Better margin handling in WeightlessLinearLayout * Add margin debugging layouts --- .../linear-layout-margins-horizontal.yml | 197 ++++++++++++++++++ .../linear-layout-margins-vertical.yml | 197 ++++++++++++++++++ .../layout/widget/WeightlessLinearLayout.java | 30 ++- 3 files changed, 414 insertions(+), 10 deletions(-) create mode 100644 urbanairship-layout/playground/sample-layouts/linear-layout-margins-horizontal.yml create mode 100644 urbanairship-layout/playground/sample-layouts/linear-layout-margins-vertical.yml diff --git a/urbanairship-layout/playground/sample-layouts/linear-layout-margins-horizontal.yml b/urbanairship-layout/playground/sample-layouts/linear-layout-margins-horizontal.yml new file mode 100644 index 000000000..7206d3a34 --- /dev/null +++ b/urbanairship-layout/playground/sample-layouts/linear-layout-margins-horizontal.yml @@ -0,0 +1,197 @@ +--- +version: 1 +presentation: + dismiss_on_touch_outside: true + type: modal + default_placement: + size: + width: 95% + height: 30% + shade_color: + default: + hex: '#000000' + alpha: 0.7 +view: + type: container + background_color: + default: + hex: "#ffffff" + alpha: 1 + border: + stroke_color: + default: + hex: "#000000" + alpha: 1 + stroke_width: 3 + items: + # TOP-LEVEL LINEAR LAYOUT + - position: + horizontal: center + vertical: center + size: + height: 100% + width: 100% + view: + type: linear_layout + direction: vertical + background_color: + default: + hex: "#ffffff" + alpha: 0.35 + items: + - size: + width: 100% + height: 50% + view: + type: linear_layout + direction: horizontal + items: + - position: + horizontal: center + vertical: center + size: + width: 100% + height: 100% + margin: + start: 0 + end: 0 + view: + type: label + text: 100% + background_color: + default: + hex: "#FF0000" + alpha: 0.35 + text_appearance: + color: + default: + hex: "#000000" + alpha: 1 + alignment: center + font_size: 12 + + - position: + horizontal: center + vertical: center + size: + width: 100% + height: 100% + margin: + start: 0 + end: 0 + view: + type: label + text: 100% + background_color: + default: + hex: "#00FF00" + alpha: 0.35 + text_appearance: + color: + default: + hex: "#000000" + alpha: 1 + alignment: center + font_size: 12 + + - position: + horizontal: center + vertical: center + size: + width: 100% + height: 100% + margin: + start: 0 + end: 0 + view: + type: label + text: 100% + background_color: + default: + hex: "#0000FF" + alpha: 0.35 + text_appearance: + color: + default: + hex: "#000000" + alpha: 1 + alignment: center + font_size: 12 + - size: + width: 100% + height: 50% + view: + type: linear_layout + direction: horizontal + items: + - position: + horizontal: center + vertical: center + size: + width: 100% + height: 100% + margin: + start: 16 + end: 16 + view: + type: label + text: 100% + background_color: + default: + hex: "#FF0000" + alpha: 0.35 + text_appearance: + color: + default: + hex: "#000000" + alpha: 1 + alignment: center + font_size: 12 + + - position: + horizontal: center + vertical: center + size: + width: 100% + height: 100% + margin: + start: 16 + end: 16 + view: + type: label + text: 100% + background_color: + default: + hex: "#00FF00" + alpha: 0.35 + text_appearance: + color: + default: + hex: "#000000" + alpha: 1 + alignment: center + font_size: 12 + + - position: + horizontal: center + vertical: center + size: + width: 100% + height: 100% + margin: + start: 16 + end: 16 + view: + type: label + text: 100% + background_color: + default: + hex: "#0000FF" + alpha: 0.35 + text_appearance: + color: + default: + hex: "#000000" + alpha: 1 + alignment: center + font_size: 12 diff --git a/urbanairship-layout/playground/sample-layouts/linear-layout-margins-vertical.yml b/urbanairship-layout/playground/sample-layouts/linear-layout-margins-vertical.yml new file mode 100644 index 000000000..b9f86f05a --- /dev/null +++ b/urbanairship-layout/playground/sample-layouts/linear-layout-margins-vertical.yml @@ -0,0 +1,197 @@ +--- +version: 1 +presentation: + dismiss_on_touch_outside: true + type: modal + default_placement: + size: + width: 95% + height: 30% + shade_color: + default: + hex: '#000000' + alpha: 0.7 +view: + type: container + background_color: + default: + hex: "#ffffff" + alpha: 1 + border: + stroke_color: + default: + hex: "#000000" + alpha: 1 + stroke_width: 3 + items: + # TOP-LEVEL LINEAR LAYOUT + - position: + horizontal: center + vertical: center + size: + height: 100% + width: 100% + view: + type: linear_layout + direction: horizontal + background_color: + default: + hex: "#ffffff" + alpha: 0.35 + items: + - size: + width: 50% + height: 100% + view: + type: linear_layout + direction: vertical + items: + - position: + horizontal: center + vertical: center + size: + width: 100% + height: 100% + margin: + start: 0 + end: 0 + view: + type: label + text: 100% + background_color: + default: + hex: "#FF0000" + alpha: 0.35 + text_appearance: + color: + default: + hex: "#000000" + alpha: 1 + alignment: center + font_size: 12 + + - position: + horizontal: center + vertical: center + size: + width: 100% + height: 100% + margin: + start: 0 + end: 0 + view: + type: label + text: 100% + background_color: + default: + hex: "#00FF00" + alpha: 0.35 + text_appearance: + color: + default: + hex: "#000000" + alpha: 1 + alignment: center + font_size: 12 + + - position: + horizontal: center + vertical: center + size: + width: 100% + height: 100% + margin: + start: 0 + end: 0 + view: + type: label + text: 100% + background_color: + default: + hex: "#0000FF" + alpha: 0.35 + text_appearance: + color: + default: + hex: "#000000" + alpha: 1 + alignment: center + font_size: 12 + - size: + width: 50% + height: 100% + view: + type: linear_layout + direction: vertical + items: + - position: + horizontal: center + vertical: center + size: + width: 100% + height: 100% + margin: + top: 16 + bottom: 16 + view: + type: label + text: 100% + background_color: + default: + hex: "#FF0000" + alpha: 0.35 + text_appearance: + color: + default: + hex: "#000000" + alpha: 1 + alignment: center + font_size: 12 + + - position: + horizontal: center + vertical: center + size: + width: 100% + height: 100% + margin: + top: 16 + bottom: 16 + view: + type: label + text: 100% + background_color: + default: + hex: "#00FF00" + alpha: 0.35 + text_appearance: + color: + default: + hex: "#000000" + alpha: 1 + alignment: center + font_size: 12 + + - position: + horizontal: center + vertical: center + size: + width: 100% + height: 100% + margin: + top: 16 + bottom: 16 + view: + type: label + text: 100% + background_color: + default: + hex: "#0000FF" + alpha: 0.35 + text_appearance: + color: + default: + hex: "#000000" + alpha: 1 + alignment: center + font_size: 12 \ No newline at end of file diff --git a/urbanairship-layout/src/main/java/com/urbanairship/android/layout/widget/WeightlessLinearLayout.java b/urbanairship-layout/src/main/java/com/urbanairship/android/layout/widget/WeightlessLinearLayout.java index 7eb593730..85b50bd9d 100644 --- a/urbanairship-layout/src/main/java/com/urbanairship/android/layout/widget/WeightlessLinearLayout.java +++ b/urbanairship-layout/src/main/java/com/urbanairship/android/layout/widget/WeightlessLinearLayout.java @@ -232,6 +232,7 @@ private void measureVertical(int widthMeasureSpec, int heightMeasureSpec) { int count = getChildCount(); List childrenWithMaxPercent = new ArrayList<>(); + int maxPercentChildrenTotalMargins = 0; int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); @@ -253,6 +254,7 @@ private void measureVertical(int widthMeasureSpec, int heightMeasureSpec) { if (lp.maxHeightPercent > 0) { childrenWithMaxPercent.add(child); + maxPercentChildrenTotalMargins += lp.getMarginStart() + lp.getMarginEnd(); } if (heightMode == MeasureSpec.EXACTLY && lp.height == 0 && lp.maxHeightPercent > 0) { @@ -279,7 +281,7 @@ private void measureVertical(int widthMeasureSpec, int heightMeasureSpec) { // Determine how big this child would like to be. measureChildWithMargins(child, widthMeasureSpec, childHorizontalMargins, - heightMeasureSpec, childrenWithMaxPercent.size() > 0 ? totalLength : 0); + heightMeasureSpec, !childrenWithMaxPercent.isEmpty() ? totalLength : 0); if (oldHeight != Integer.MIN_VALUE) { lp.height = oldHeight; @@ -330,7 +332,7 @@ private void measureVertical(int widthMeasureSpec, int heightMeasureSpec) { // Either expand children with percentage dimensions to take up available space or shrink them if they extend // beyond our current bounds. int delta = height - totalLength; - if (skippedMeasure || (delta != 0 && childrenWithMaxPercent.size() > 0)) { + if (skippedMeasure || (delta != 0 && !childrenWithMaxPercent.isEmpty())) { Collections.sort(childrenWithMaxPercent, (v1, v2) -> { float p1 = ((LayoutParams) v1.getLayoutParams()).maxHeightPercent; float p2 = ((LayoutParams) v2.getLayoutParams()).maxHeightPercent; @@ -338,6 +340,9 @@ private void measureVertical(int widthMeasureSpec, int heightMeasureSpec) { }); int maxPercentCount = childrenWithMaxPercent.size(); + // We only want to distribute the available content height, not the total height with margins. + int availableContentHeight = height - maxPercentChildrenTotalMargins; + int lastChildIndex = maxPercentCount - 1; for (int i = 0; i < maxPercentCount; i++) { View child = childrenWithMaxPercent.get(i); @@ -346,13 +351,13 @@ private void measureVertical(int widthMeasureSpec, int heightMeasureSpec) { if (heightMode != MeasureSpec.UNSPECIFIED) { float actualPercent; - if (delta >= (height * lp.maxHeightPercent) * (maxPercentCount - i)) { + if (delta >= (availableContentHeight * lp.maxHeightPercent) * (maxPercentCount - i)) { actualPercent = lp.maxHeightPercent; } else { - actualPercent = ((float) delta) / ((float)(childrenWithMaxPercent.size() - i)) / ((float) height); + actualPercent = ((float) delta) / ((float)(childrenWithMaxPercent.size() - i)) / ((float) availableContentHeight); } - int childHeight = (int) (actualPercent * height) + lp.topMargin + lp.bottomMargin; + int childHeight = (int) (actualPercent * availableContentHeight); if (i == lastChildIndex) { childHeight = Math.min(childHeight, delta); } @@ -460,6 +465,7 @@ private void measureHorizontal(int widthMeasureSpec, int heightMeasureSpec) { int count = getChildCount(); List childrenWithMaxPercent = new ArrayList<>(); + int maxPercentChildrenTotalMargins = 0; int widthMode = MeasureSpec.getMode(widthMeasureSpec); @@ -481,6 +487,7 @@ private void measureHorizontal(int widthMeasureSpec, int heightMeasureSpec) { if (lp.maxWidthPercent > 0) { childrenWithMaxPercent.add(child); + maxPercentChildrenTotalMargins += lp.getMarginStart() + lp.getMarginEnd(); } if (widthMode == MeasureSpec.EXACTLY && lp.width == 0 && lp.maxWidthPercent > 0) { @@ -506,7 +513,7 @@ private void measureHorizontal(int widthMeasureSpec, int heightMeasureSpec) { // Determine how big this child would like to be. measureChildWithMargins(child, - widthMeasureSpec, childrenWithMaxPercent.size() > 0 ? totalLength : 0, + widthMeasureSpec, !childrenWithMaxPercent.isEmpty() ? totalLength : 0, heightMeasureSpec, childVerticalMargins); if (oldWidth != Integer.MIN_VALUE) { @@ -558,7 +565,7 @@ private void measureHorizontal(int widthMeasureSpec, int heightMeasureSpec) { // Either expand children with percentage dimensions to take up available space or shrink them if they extend // beyond our current bounds. int delta = width - totalLength; - if (skippedMeasure || (delta != 0 && childrenWithMaxPercent.size() > 0)) { + if (skippedMeasure || (delta != 0 && !childrenWithMaxPercent.isEmpty())) { Collections.sort(childrenWithMaxPercent, (v1, v2) -> { float p1 = ((LayoutParams) v1.getLayoutParams()).maxWidthPercent; float p2 = ((LayoutParams) v2.getLayoutParams()).maxWidthPercent; @@ -566,6 +573,9 @@ private void measureHorizontal(int widthMeasureSpec, int heightMeasureSpec) { }); int maxPercentCount = childrenWithMaxPercent.size(); + // We only want to distribute the available content width, not the total width with margins. + int availableContentWidth = width - maxPercentChildrenTotalMargins; + int lastChildIndex = maxPercentCount - 1; for (int i = 0; i < maxPercentCount; i++) { View child = childrenWithMaxPercent.get(i); @@ -574,13 +584,13 @@ private void measureHorizontal(int widthMeasureSpec, int heightMeasureSpec) { if (widthMode != MeasureSpec.UNSPECIFIED) { float actualPercent; - if (delta >= (width * lp.maxWidthPercent) * (maxPercentCount - i)) { + if (delta >= (availableContentWidth * lp.maxWidthPercent) * (maxPercentCount - i)) { actualPercent = lp.maxWidthPercent; } else { - actualPercent = ((float) delta) / ((float)(childrenWithMaxPercent.size() - i)) / ((float) width); + actualPercent = ((float) delta) / ((float)(childrenWithMaxPercent.size() - i)) / ((float) availableContentWidth); } - int childWidth = (int) (actualPercent * width) + lp.getMarginStart() + lp.getMarginEnd(); + int childWidth = (int) (actualPercent * availableContentWidth); if (i == lastChildIndex) { childWidth = Math.min(childWidth, delta); }