Skip to content

Commit a8c8b6e

Browse files
AlexBalothagikura
authored andcommitted
Take CompoundButtons drawables min size into account during measurements (#528)
CompoundButtons such as Checkboxes, when are allowed to shrink (flexShrink = 1f), during the recalculation can end up being shrunk more than what I would consider their "minimum size" since the negative space is calculated for all FlexItems in the shrinkFlexItems() method. In this PR I try to prevent these components to get completely shrunk by considering the minimum size of their button drawable. In this way, the drawable size will determine the minimum size of the widget unless a minWidth or minHeight are specified by the user.
1 parent 9f9d07d commit a8c8b6e

File tree

3 files changed

+100
-5
lines changed

3 files changed

+100
-5
lines changed

flexbox/src/androidTest/java/com/google/android/flexbox/FlexboxHelperTest.kt

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package com.google.android.flexbox
1818

1919
import android.view.View
20+
import android.widget.CheckBox
2021
import android.widget.TextView
2122
import androidx.test.rule.ActivityTestRule
2223
import androidx.test.runner.AndroidJUnit4
@@ -399,6 +400,74 @@ class FlexboxHelperTest {
399400
assertThat(view3.measuredWidth, `is`(100))
400401
}
401402

403+
@Test
404+
@Throws(Throwable::class)
405+
fun testDetermineMainSize_directionRow_considerCompoundButtonImplicitMinSizeWhenNotSpecified() {
406+
val containerWidth = 500
407+
val activity = activityRule.activity
408+
val lp1 = FlexboxLayout.LayoutParams(
409+
FlexboxLayout.LayoutParams.WRAP_CONTENT,
410+
FlexboxLayout.LayoutParams.WRAP_CONTENT)
411+
val view1 = CheckBox(activity)
412+
view1.layoutParams = lp1
413+
val lp2 = FlexboxLayout.LayoutParams(
414+
FlexboxLayout.LayoutParams.WRAP_CONTENT,
415+
FlexboxLayout.LayoutParams.WRAP_CONTENT)
416+
val view2 = TextView(activity)
417+
view2.layoutParams = lp2
418+
view2.text = LONG_TEXT
419+
flexContainer.addView(view1)
420+
flexContainer.addView(view2)
421+
flexContainer.flexWrap = FlexWrap.NOWRAP
422+
val widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(containerWidth, View.MeasureSpec.AT_MOST)
423+
val heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.UNSPECIFIED)
424+
val result = FlexboxHelper.FlexLinesResult()
425+
flexboxHelper.calculateHorizontalFlexLines(result, widthMeasureSpec, heightMeasureSpec)
426+
flexContainer.flexLines = result.mFlexLines
427+
flexboxHelper.determineMainSize(widthMeasureSpec, heightMeasureSpec)
428+
429+
// CompoundButton will use its ButtonDrawable minWidth to determine its size when
430+
// no minimum width is set on it.
431+
val drawableMinWidth = view1.buttonDrawable!!.minimumWidth
432+
val expectedTextWidth = containerWidth - drawableMinWidth
433+
assertThat(view1.measuredWidth, `is`(drawableMinWidth))
434+
assertThat(view2.measuredWidth, `is`(expectedTextWidth))
435+
}
436+
437+
@Test
438+
@Throws(Throwable::class)
439+
fun testDetermineMainSize_directionRow_considerCompoundButtonExplicitMinSizeWhenSpecified() {
440+
val containerWidth = 500
441+
val compoundButtonMinWidth = 150
442+
val activity = activityRule.activity
443+
val lp1 = FlexboxLayout.LayoutParams(
444+
FlexboxLayout.LayoutParams.WRAP_CONTENT,
445+
FlexboxLayout.LayoutParams.WRAP_CONTENT)
446+
lp1.minWidth = compoundButtonMinWidth
447+
val view1 = CheckBox(activity)
448+
view1.layoutParams = lp1
449+
val lp2 = FlexboxLayout.LayoutParams(
450+
FlexboxLayout.LayoutParams.WRAP_CONTENT,
451+
FlexboxLayout.LayoutParams.WRAP_CONTENT)
452+
val view2 = TextView(activity)
453+
view2.layoutParams = lp2
454+
view2.text = LONG_TEXT
455+
flexContainer.addView(view1)
456+
flexContainer.addView(view2)
457+
flexContainer.flexWrap = FlexWrap.NOWRAP
458+
val widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(containerWidth, View.MeasureSpec.AT_MOST)
459+
val heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.UNSPECIFIED)
460+
val result = FlexboxHelper.FlexLinesResult()
461+
flexboxHelper.calculateHorizontalFlexLines(result, widthMeasureSpec, heightMeasureSpec)
462+
flexContainer.flexLines = result.mFlexLines
463+
flexboxHelper.determineMainSize(widthMeasureSpec, heightMeasureSpec)
464+
465+
// CompoundButton will be measured based on its explicitly specified minWidth.
466+
val expectedTextWidth = containerWidth - compoundButtonMinWidth
467+
assertThat(view1.measuredWidth, `is`(compoundButtonMinWidth))
468+
assertThat(view2.measuredWidth, `is`(expectedTextWidth))
469+
}
470+
402471
@Test
403472
@Throws(Throwable::class)
404473
fun testDetermineCrossSize_direction_row_alignContent_stretch() {

flexbox/src/main/java/com/google/android/flexbox/FlexboxHelper.java

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,15 @@
1919
import static com.google.android.flexbox.FlexContainer.NOT_SET;
2020
import static com.google.android.flexbox.FlexItem.FLEX_BASIS_PERCENT_DEFAULT;
2121
import static com.google.android.flexbox.FlexItem.FLEX_GROW_DEFAULT;
22-
import static com.google.android.flexbox.FlexItem.FLEX_SHRINK_DEFAULT;
2322
import static com.google.android.flexbox.FlexItem.FLEX_SHRINK_NOT_SET;
2423

2524
import static androidx.recyclerview.widget.RecyclerView.NO_POSITION;
2625

26+
import android.graphics.drawable.Drawable;
2727
import android.util.SparseIntArray;
2828
import android.view.View;
2929
import android.view.ViewGroup;
30+
import android.widget.CompoundButton;
3031

3132
import java.util.ArrayList;
3233
import java.util.Arrays;
@@ -37,6 +38,7 @@
3738
import androidx.annotation.Nullable;
3839
import androidx.annotation.VisibleForTesting;
3940
import androidx.core.view.MarginLayoutParamsCompat;
41+
import androidx.core.widget.CompoundButtonCompat;
4042

4143
/**
4244
* Offers various calculations for Flexbox to use the common logic between the classes such as
@@ -438,6 +440,8 @@ void calculateFlexLines(FlexLinesResult result, int mainMeasureSpec,
438440
addFlexLine(flexLines, flexLine, i, sumCrossSize);
439441
}
440442
continue;
443+
} else if (child instanceof CompoundButton) {
444+
evaluateMinimumSizeForCompoundButton((CompoundButton) child);
441445
}
442446

443447
FlexItem flexItem = (FlexItem) child.getLayoutParams();
@@ -628,6 +632,28 @@ void calculateFlexLines(FlexLinesResult result, int mainMeasureSpec,
628632
result.mChildState = childState;
629633
}
630634

635+
/**
636+
* Compound buttons (ex. {{@link android.widget.CheckBox}}, {@link android.widget.ToggleButton})
637+
* have a button drawable with minimum height and width specified for them.
638+
* To align the behavior with CSS Flexbox we want to respect these minimum measurement to avoid
639+
* these drawables from being cut off during calculation. When the compound button has a minimum
640+
* width or height already specified we will not make any change since we assume those were
641+
* voluntarily set by the user.
642+
*
643+
* @param compoundButton the compound button that need to be evaluated
644+
*/
645+
private void evaluateMinimumSizeForCompoundButton(CompoundButton compoundButton) {
646+
FlexItem flexItem = (FlexItem) compoundButton.getLayoutParams();
647+
int minWidth = flexItem.getMinWidth();
648+
int minHeight = flexItem.getMinHeight();
649+
650+
Drawable drawable = CompoundButtonCompat.getButtonDrawable(compoundButton);
651+
int drawableMinWidth = drawable == null ? 0 : drawable.getMinimumWidth();
652+
int drawableMinHeight = drawable == null ? 0 : drawable.getMinimumHeight();
653+
flexItem.setMinWidth(minWidth == NOT_SET ? drawableMinWidth : minWidth);
654+
flexItem.setMinHeight(minHeight == NOT_SET ? drawableMinHeight : minHeight);
655+
}
656+
631657
/**
632658
* Returns the container's start padding in the main axis. Either start or top.
633659
*

flexbox/src/main/java/com/google/android/flexbox/FlexboxLayout.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1598,12 +1598,12 @@ public static class LayoutParams extends ViewGroup.MarginLayoutParams implements
15981598
/**
15991599
* @see FlexItem#getMinWidth()
16001600
*/
1601-
private int mMinWidth;
1601+
private int mMinWidth = NOT_SET;
16021602

16031603
/**
16041604
* @see FlexItem#getMinHeight()
16051605
*/
1606-
private int mMinHeight;
1606+
private int mMinHeight = NOT_SET;
16071607

16081608
/**
16091609
* @see FlexItem#getMaxWidth()
@@ -1636,9 +1636,9 @@ public LayoutParams(Context context, AttributeSet attrs) {
16361636
.getFraction(R.styleable.FlexboxLayout_Layout_layout_flexBasisPercent, 1, 1,
16371637
FLEX_BASIS_PERCENT_DEFAULT);
16381638
mMinWidth = a
1639-
.getDimensionPixelSize(R.styleable.FlexboxLayout_Layout_layout_minWidth, 0);
1639+
.getDimensionPixelSize(R.styleable.FlexboxLayout_Layout_layout_minWidth, NOT_SET);
16401640
mMinHeight = a
1641-
.getDimensionPixelSize(R.styleable.FlexboxLayout_Layout_layout_minHeight, 0);
1641+
.getDimensionPixelSize(R.styleable.FlexboxLayout_Layout_layout_minHeight, NOT_SET);
16421642
mMaxWidth = a.getDimensionPixelSize(R.styleable.FlexboxLayout_Layout_layout_maxWidth,
16431643
MAX_SIZE);
16441644
mMaxHeight = a.getDimensionPixelSize(R.styleable.FlexboxLayout_Layout_layout_maxHeight,

0 commit comments

Comments
 (0)