diff --git a/flexbox/src/androidTest/java/com/google/android/flexbox/FlexboxHelperTest.kt b/flexbox/src/androidTest/java/com/google/android/flexbox/FlexboxHelperTest.kt index fe22b058c2aa59c0574bc752b974b2d74dcd9104..bb72385b1f53f91e63b9ca8a1bf38f96f26ae266 100644 --- a/flexbox/src/androidTest/java/com/google/android/flexbox/FlexboxHelperTest.kt +++ b/flexbox/src/androidTest/java/com/google/android/flexbox/FlexboxHelperTest.kt @@ -17,6 +17,7 @@ package com.google.android.flexbox import android.view.View +import android.widget.CheckBox import android.widget.TextView import androidx.test.rule.ActivityTestRule import androidx.test.runner.AndroidJUnit4 @@ -399,6 +400,74 @@ class FlexboxHelperTest { assertThat(view3.measuredWidth, `is`(100)) } + @Test + @Throws(Throwable::class) + fun testDetermineMainSize_directionRow_considerCompoundButtonImplicitMinSizeWhenNotSpecified() { + val containerWidth = 500 + val activity = activityRule.activity + val lp1 = FlexboxLayout.LayoutParams( + FlexboxLayout.LayoutParams.WRAP_CONTENT, + FlexboxLayout.LayoutParams.WRAP_CONTENT) + val view1 = CheckBox(activity) + view1.layoutParams = lp1 + val lp2 = FlexboxLayout.LayoutParams( + FlexboxLayout.LayoutParams.WRAP_CONTENT, + FlexboxLayout.LayoutParams.WRAP_CONTENT) + val view2 = TextView(activity) + view2.layoutParams = lp2 + view2.text = LONG_TEXT + flexContainer.addView(view1) + flexContainer.addView(view2) + flexContainer.flexWrap = FlexWrap.NOWRAP + val widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(containerWidth, View.MeasureSpec.AT_MOST) + val heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.UNSPECIFIED) + val result = FlexboxHelper.FlexLinesResult() + flexboxHelper.calculateHorizontalFlexLines(result, widthMeasureSpec, heightMeasureSpec) + flexContainer.flexLines = result.mFlexLines + flexboxHelper.determineMainSize(widthMeasureSpec, heightMeasureSpec) + + // CompoundButton will use its ButtonDrawable minWidth to determine its size when + // no minimum width is set on it. + val drawableMinWidth = view1.buttonDrawable!!.minimumWidth + val expectedTextWidth = containerWidth - drawableMinWidth + assertThat(view1.measuredWidth, `is`(drawableMinWidth)) + assertThat(view2.measuredWidth, `is`(expectedTextWidth)) + } + + @Test + @Throws(Throwable::class) + fun testDetermineMainSize_directionRow_considerCompoundButtonExplicitMinSizeWhenSpecified() { + val containerWidth = 500 + val compoundButtonMinWidth = 150 + val activity = activityRule.activity + val lp1 = FlexboxLayout.LayoutParams( + FlexboxLayout.LayoutParams.WRAP_CONTENT, + FlexboxLayout.LayoutParams.WRAP_CONTENT) + lp1.minWidth = compoundButtonMinWidth + val view1 = CheckBox(activity) + view1.layoutParams = lp1 + val lp2 = FlexboxLayout.LayoutParams( + FlexboxLayout.LayoutParams.WRAP_CONTENT, + FlexboxLayout.LayoutParams.WRAP_CONTENT) + val view2 = TextView(activity) + view2.layoutParams = lp2 + view2.text = LONG_TEXT + flexContainer.addView(view1) + flexContainer.addView(view2) + flexContainer.flexWrap = FlexWrap.NOWRAP + val widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(containerWidth, View.MeasureSpec.AT_MOST) + val heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.UNSPECIFIED) + val result = FlexboxHelper.FlexLinesResult() + flexboxHelper.calculateHorizontalFlexLines(result, widthMeasureSpec, heightMeasureSpec) + flexContainer.flexLines = result.mFlexLines + flexboxHelper.determineMainSize(widthMeasureSpec, heightMeasureSpec) + + // CompoundButton will be measured based on its explicitly specified minWidth. + val expectedTextWidth = containerWidth - compoundButtonMinWidth + assertThat(view1.measuredWidth, `is`(compoundButtonMinWidth)) + assertThat(view2.measuredWidth, `is`(expectedTextWidth)) + } + @Test @Throws(Throwable::class) fun testDetermineCrossSize_direction_row_alignContent_stretch() { diff --git a/flexbox/src/main/java/com/google/android/flexbox/FlexboxHelper.java b/flexbox/src/main/java/com/google/android/flexbox/FlexboxHelper.java index fb0fdb8fdba81b03af5923f168b8f835331e0423..03225fc3e41bfbe0cbf611a3cd9a6dd401b982bf 100644 --- a/flexbox/src/main/java/com/google/android/flexbox/FlexboxHelper.java +++ b/flexbox/src/main/java/com/google/android/flexbox/FlexboxHelper.java @@ -19,14 +19,15 @@ package com.google.android.flexbox; import static com.google.android.flexbox.FlexContainer.NOT_SET; import static com.google.android.flexbox.FlexItem.FLEX_BASIS_PERCENT_DEFAULT; import static com.google.android.flexbox.FlexItem.FLEX_GROW_DEFAULT; -import static com.google.android.flexbox.FlexItem.FLEX_SHRINK_DEFAULT; import static com.google.android.flexbox.FlexItem.FLEX_SHRINK_NOT_SET; import static androidx.recyclerview.widget.RecyclerView.NO_POSITION; +import android.graphics.drawable.Drawable; import android.util.SparseIntArray; import android.view.View; import android.view.ViewGroup; +import android.widget.CompoundButton; import java.util.ArrayList; import java.util.Arrays; @@ -37,6 +38,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.core.view.MarginLayoutParamsCompat; +import androidx.core.widget.CompoundButtonCompat; /** * Offers various calculations for Flexbox to use the common logic between the classes such as @@ -438,6 +440,8 @@ class FlexboxHelper { addFlexLine(flexLines, flexLine, i, sumCrossSize); } continue; + } else if (child instanceof CompoundButton) { + evaluateMinimumSizeForCompoundButton((CompoundButton) child); } FlexItem flexItem = (FlexItem) child.getLayoutParams(); @@ -628,6 +632,28 @@ class FlexboxHelper { result.mChildState = childState; } + /** + * Compound buttons (ex. {{@link android.widget.CheckBox}}, {@link android.widget.ToggleButton}) + * have a button drawable with minimum height and width specified for them. + * To align the behavior with CSS Flexbox we want to respect these minimum measurement to avoid + * these drawables from being cut off during calculation. When the compound button has a minimum + * width or height already specified we will not make any change since we assume those were + * voluntarily set by the user. + * + * @param compoundButton the compound button that need to be evaluated + */ + private void evaluateMinimumSizeForCompoundButton(CompoundButton compoundButton) { + FlexItem flexItem = (FlexItem) compoundButton.getLayoutParams(); + int minWidth = flexItem.getMinWidth(); + int minHeight = flexItem.getMinHeight(); + + Drawable drawable = CompoundButtonCompat.getButtonDrawable(compoundButton); + int drawableMinWidth = drawable == null ? 0 : drawable.getMinimumWidth(); + int drawableMinHeight = drawable == null ? 0 : drawable.getMinimumHeight(); + flexItem.setMinWidth(minWidth == NOT_SET ? drawableMinWidth : minWidth); + flexItem.setMinHeight(minHeight == NOT_SET ? drawableMinHeight : minHeight); + } + /** * Returns the container's start padding in the main axis. Either start or top. * diff --git a/flexbox/src/main/java/com/google/android/flexbox/FlexboxLayout.java b/flexbox/src/main/java/com/google/android/flexbox/FlexboxLayout.java index 10a12f60f9c5f440b3a2a2d7f631cf4aa9165df3..12c3e8ab849ed17dd890c7db6f6fe2ff275e59f6 100644 --- a/flexbox/src/main/java/com/google/android/flexbox/FlexboxLayout.java +++ b/flexbox/src/main/java/com/google/android/flexbox/FlexboxLayout.java @@ -1598,12 +1598,12 @@ public class FlexboxLayout extends ViewGroup implements FlexContainer { /** * @see FlexItem#getMinWidth() */ - private int mMinWidth; + private int mMinWidth = NOT_SET; /** * @see FlexItem#getMinHeight() */ - private int mMinHeight; + private int mMinHeight = NOT_SET; /** * @see FlexItem#getMaxWidth() @@ -1636,9 +1636,9 @@ public class FlexboxLayout extends ViewGroup implements FlexContainer { .getFraction(R.styleable.FlexboxLayout_Layout_layout_flexBasisPercent, 1, 1, FLEX_BASIS_PERCENT_DEFAULT); mMinWidth = a - .getDimensionPixelSize(R.styleable.FlexboxLayout_Layout_layout_minWidth, 0); + .getDimensionPixelSize(R.styleable.FlexboxLayout_Layout_layout_minWidth, NOT_SET); mMinHeight = a - .getDimensionPixelSize(R.styleable.FlexboxLayout_Layout_layout_minHeight, 0); + .getDimensionPixelSize(R.styleable.FlexboxLayout_Layout_layout_minHeight, NOT_SET); mMaxWidth = a.getDimensionPixelSize(R.styleable.FlexboxLayout_Layout_layout_maxWidth, MAX_SIZE); mMaxHeight = a.getDimensionPixelSize(R.styleable.FlexboxLayout_Layout_layout_maxHeight,