提交 b5af2041 编写于 作者: T Takeshi Hagikura 提交者: GitHub

Fix the invalid height in the following situation: (#140)

- flex direction is set to row (or row_reverse)
- FlexboxLayout has a TextView (or other views that expand vertically if
  not enough space is left horizontally)
- TextView's layout_width is set to 0dp, layout_height is set to wrap_content and
  layout_flexGrow is set to positive

In the situation above, the initial height of the TextView is calculated
as bigger than the actual height because at first the width is set to 0dp,
so the TextView is trying to expand vertically, but actually the height
needs to be measured again with the expanded width (by setting the
layout_flexGrow attribute)
上级 952cc074
......@@ -3742,6 +3742,55 @@ public class FlexboxAndroidTest {
assertThat(text3.getRight(), is(text1.getWidth() + text3.getWidth()));
}
@Test
@FlakyTest
public void testZeroWidth_wrapContentHeight_positiveFlexGrow() throws Throwable {
final FlexboxTestActivity activity = mActivityRule.getActivity();
mActivityRule.runOnUiThread(new Runnable() {
@Override
public void run() {
activity.setContentView(R.layout.activity_zero_width_positive_flexgrow);
}
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
FlexboxLayout flexboxLayout = (FlexboxLayout) activity.findViewById(R.id.flexbox_layout);
assertThat(flexboxLayout.getFlexDirection(), is(FlexboxLayout.FLEX_DIRECTION_ROW));
TextView text1 = (TextView) activity.findViewById(R.id.text1);
TextView text2 = (TextView) activity.findViewById(R.id.text2);
// Both text view's layout_width is set to 0dp, layout_height is set to wrap_content and
// layout_flexGrow is set to 1. And the text2 has a longer text than the text1.
// So if the cross size calculation (height) is wrong, the height of two text view do not
// match because text2 is trying to expand vertically.
// This assertion verifies that isn't happening. Finally both text views expand horizontally
// enough to contain their texts in one line.
assertThat(text1.getHeight(), is(text2.getHeight()));
assertThat(text1.getWidth() + text2.getWidth(),
isEqualAllowingError(flexboxLayout.getWidth()));
}
@Test
@FlakyTest
public void testZeroHeight_wrapContentWidth_positiveFlexGrow() throws Throwable {
final FlexboxTestActivity activity = mActivityRule.getActivity();
mActivityRule.runOnUiThread(new Runnable() {
@Override
public void run() {
activity.setContentView(R.layout.activity_zero_height_positive_flexgrow);
}
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
FlexboxLayout flexboxLayout = (FlexboxLayout) activity.findViewById(R.id.flexbox_layout);
assertThat(flexboxLayout.getFlexDirection(), is(FlexboxLayout.FLEX_DIRECTION_COLUMN));
TextView text1 = (TextView) activity.findViewById(R.id.text1);
TextView text2 = (TextView) activity.findViewById(R.id.text2);
assertThat(text1.getWidth(), is(not(text2.getWidth())));
assertThat(text1.getHeight() + text2.getHeight(),
isEqualAllowingError(flexboxLayout.getHeight()));
}
private TextView createTextView(Context context, String text, int order) {
TextView textView = new TextView(context);
textView.setText(text);
......
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2016 Google Inc. All rights reserved.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<com.google.android.flexbox.FlexboxLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/flexbox_layout"
android:layout_width="wrap_content"
android:layout_height="match_parent"
app:flexDirection="column"
app:alignItems="flex_start" >
<TextView
android:id="@+id/text1"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:text="1"
app:layout_flexGrow="1" />
<TextView
android:id="@+id/text2"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:text="2-some long text"
app:layout_flexGrow="1" />
</com.google.android.flexbox.FlexboxLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2016 Google Inc. All rights reserved.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<com.google.android.flexbox.FlexboxLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/flexbox_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:alignItems="flex_start" >
<TextView
android:id="@+id/text1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="1"
app:layout_flexGrow="1" />
<TextView
android:id="@+id/text2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="2-some long text"
app:layout_flexGrow="1" />
</com.google.android.flexbox.FlexboxLayout>
\ No newline at end of file
......@@ -900,11 +900,11 @@ public class FlexboxLayout extends ViewGroup {
int childIndex = 0;
for (FlexLine flexLine : mFlexLines) {
if (flexLine.mMainSize < mainSize) {
childIndex = expandFlexItems(flexLine, flexDirection, mainSize,
paddingAlongMainAxis, childIndex);
childIndex = expandFlexItems(widthMeasureSpec, heightMeasureSpec, flexLine,
flexDirection, mainSize, paddingAlongMainAxis, childIndex);
} else {
childIndex = shrinkFlexItems(flexLine, flexDirection, mainSize,
paddingAlongMainAxis, childIndex);
childIndex = shrinkFlexItems(widthMeasureSpec, heightMeasureSpec, flexLine,
flexDirection, mainSize, paddingAlongMainAxis, childIndex);
}
}
}
......@@ -912,6 +912,8 @@ public class FlexboxLayout extends ViewGroup {
/**
* Expand the flex items along the main axis based on the individual flexGrow attribute.
*
* @param widthMeasureSpec the horizontal space requirements as imposed by the parent
* @param heightMeasureSpec the vertical space requirements as imposed by the parent
* @param flexLine the flex line to which flex items belong
* @param flexDirection the flexDirection value for this FlexboxLayout
* @param maxMainSize the maximum main size. Expanded main size will be this size
......@@ -925,8 +927,9 @@ public class FlexboxLayout extends ViewGroup {
* @see #setFlexDirection(int)
* @see LayoutParams#flexGrow
*/
private int expandFlexItems(FlexLine flexLine, @FlexDirection int flexDirection,
int maxMainSize, int paddingAlongMainAxis, int startIndex) {
private int expandFlexItems(int widthMeasureSpec, int heightMeasureSpec, FlexLine flexLine,
@FlexDirection int flexDirection, int maxMainSize, int paddingAlongMainAxis,
int startIndex) {
int childIndex = startIndex;
if (flexLine.mTotalFlexGrow <= 0 || maxMainSize < flexLine.mMainSize) {
childIndex += flexLine.mItemCount;
......@@ -936,6 +939,17 @@ public class FlexboxLayout extends ViewGroup {
boolean needsReexpand = false;
float unitSpace = (maxMainSize - flexLine.mMainSize) / flexLine.mTotalFlexGrow;
flexLine.mMainSize = paddingAlongMainAxis + flexLine.mDividerLengthInMainSize;
// Setting the cross size of the flex line as the temporal value since the cross size of
// each flex item may be changed from the initial calculation
// (in the measureHorizontal/measureVertical method) even this method is part of the main
// size determination.
// E.g. If a TextView's layout_width is set to 0dp, layout_height is set to wrap_content,
// and layout_flexGrow is set to 1, the TextView is trying to expand to the vertical
// direction to enclose its content (in the measureHorizontal method), but
// the width will be expanded in this method. In that case, the height needs to be measured
// again with the expanded width.
flexLine.mCrossSize = Integer.MIN_VALUE;
float accumulatedRoundError = 0;
for (int i = 0; i < flexLine.mItemCount; i++) {
View child = getReorderedChildAt(childIndex);
......@@ -975,12 +989,12 @@ public class FlexboxLayout extends ViewGroup {
accumulatedRoundError += 1.0;
}
}
int childHeightMeasureSpec = getChildHeightMeasureSpec(heightMeasureSpec, lp);
child.measure(MeasureSpec.makeMeasureSpec(newWidth, MeasureSpec.EXACTLY),
MeasureSpec
.makeMeasureSpec(child.getMeasuredHeight(),
MeasureSpec.EXACTLY));
childHeightMeasureSpec);
}
flexLine.mMainSize += child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
flexLine.mCrossSize = Math.max(flexLine.mCrossSize, child.getMeasuredHeight());
} else {
// The direction of the main axis is vertical
if (!mChildrenFrozen[childIndex]) {
......@@ -1011,11 +1025,12 @@ public class FlexboxLayout extends ViewGroup {
accumulatedRoundError += 1.0;
}
}
child.measure(MeasureSpec.makeMeasureSpec(child.getMeasuredWidth(),
MeasureSpec.EXACTLY),
int childWidthMeasureSpec = getChildWidthMeasureSpec(widthMeasureSpec, lp);
child.measure(childWidthMeasureSpec,
MeasureSpec.makeMeasureSpec(newHeight, MeasureSpec.EXACTLY));
}
flexLine.mMainSize += child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
flexLine.mCrossSize = Math.max(flexLine.mCrossSize, child.getMeasuredWidth());
}
childIndex++;
}
......@@ -1023,7 +1038,8 @@ public class FlexboxLayout extends ViewGroup {
if (needsReexpand && sizeBeforeExpand != flexLine.mMainSize) {
// Re-invoke the method with the same startIndex to distribute the positive free space
// that wasn't fully distributed (because of maximum length constraint)
expandFlexItems(flexLine, flexDirection, maxMainSize, paddingAlongMainAxis, startIndex);
expandFlexItems(widthMeasureSpec, heightMeasureSpec, flexLine, flexDirection,
maxMainSize, paddingAlongMainAxis, startIndex);
}
return childIndex;
}
......@@ -1031,6 +1047,8 @@ public class FlexboxLayout extends ViewGroup {
/**
* Shrink the flex items along the main axis based on the individual flexShrink attribute.
*
* @param widthMeasureSpec the horizontal space requirements as imposed by the parent
* @param heightMeasureSpec the vertical space requirements as imposed by the parent
* @param flexLine the flex line to which flex items belong
* @param flexDirection the flexDirection value for this FlexboxLayout
* @param maxMainSize the maximum main size. Shrank main size will be this size
......@@ -1044,8 +1062,9 @@ public class FlexboxLayout extends ViewGroup {
* @see #setFlexDirection(int)
* @see LayoutParams#flexShrink
*/
private int shrinkFlexItems(FlexLine flexLine, @FlexDirection int flexDirection,
int maxMainSize, int paddingAlongMainAxis, int startIndex) {
private int shrinkFlexItems(int widthMeasureSpec, int heightMeasureSpec, FlexLine flexLine,
@FlexDirection int flexDirection, int maxMainSize, int paddingAlongMainAxis,
int startIndex) {
int childIndex = startIndex;
int sizeBeforeShrink = flexLine.mMainSize;
if (flexLine.mTotalFlexShrink <= 0 || maxMainSize > flexLine.mMainSize) {
......@@ -1056,6 +1075,17 @@ public class FlexboxLayout extends ViewGroup {
float unitShrink = (flexLine.mMainSize - maxMainSize) / flexLine.mTotalFlexShrink;
float accumulatedRoundError = 0;
flexLine.mMainSize = paddingAlongMainAxis + flexLine.mDividerLengthInMainSize;
// Setting the cross size of the flex line as the temporal value since the cross size of
// each flex item may be changed from the initial calculation
// (in the measureHorizontal/measureVertical method) even this method is part of the main
// size determination.
// E.g. If a TextView's layout_width is set to 0dp, layout_height is set to wrap_content,
// and layout_flexGrow is set to 1, the TextView is trying to expand to the vertical
// direction to enclose its content (in the measureHorizontal method), but
// the width will be expanded in this method. In that case, the height needs to be measured
// again with the expanded width.
flexLine.mCrossSize = Integer.MIN_VALUE;
for (int i = 0; i < flexLine.mItemCount; i++) {
View child = getReorderedChildAt(childIndex);
if (child == null) {
......@@ -1095,11 +1125,12 @@ public class FlexboxLayout extends ViewGroup {
accumulatedRoundError += 1;
}
}
int childHeightMeasureSpec = getChildHeightMeasureSpec(heightMeasureSpec, lp);
child.measure(MeasureSpec.makeMeasureSpec(newWidth, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(child.getMeasuredHeight(),
MeasureSpec.EXACTLY));
childHeightMeasureSpec);
}
flexLine.mMainSize += child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
flexLine.mCrossSize = Math.max(flexLine.mCrossSize, child.getMeasuredHeight());
} else {
// The direction of main axis is vertical
if (!mChildrenFrozen[childIndex]) {
......@@ -1126,11 +1157,12 @@ public class FlexboxLayout extends ViewGroup {
accumulatedRoundError += 1;
}
}
child.measure(MeasureSpec.makeMeasureSpec(child.getMeasuredWidth(),
MeasureSpec.EXACTLY),
int childWidthMeasureSpec = getChildWidthMeasureSpec(widthMeasureSpec, lp);
child.measure(childWidthMeasureSpec,
MeasureSpec.makeMeasureSpec(newHeight, MeasureSpec.EXACTLY));
}
flexLine.mMainSize += child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
flexLine.mCrossSize = Math.max(flexLine.mCrossSize, child.getMeasuredWidth());
}
childIndex++;
}
......@@ -1138,11 +1170,42 @@ public class FlexboxLayout extends ViewGroup {
if (needsReshrink && sizeBeforeShrink != flexLine.mMainSize) {
// Re-invoke the method with the same startIndex to distribute the negative free space
// that wasn't fully distributed (because some views length were not enough)
shrinkFlexItems(flexLine, flexDirection, maxMainSize, paddingAlongMainAxis, startIndex);
shrinkFlexItems(widthMeasureSpec, heightMeasureSpec, flexLine, flexDirection,
maxMainSize, paddingAlongMainAxis, startIndex);
}
return childIndex;
}
private int getChildWidthMeasureSpec(int widthMeasureSpec, LayoutParams lp) {
int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
getPaddingLeft() + getPaddingRight() + lp.leftMargin
+ lp.rightMargin, lp.width);
int childWidth = MeasureSpec.getSize(childWidthMeasureSpec);
if (childWidth > lp.maxWidth) {
childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.maxWidth,
MeasureSpec.getMode(childWidthMeasureSpec));
} else if (childWidth < lp.minWidth) {
childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.minWidth,
MeasureSpec.getMode(childWidthMeasureSpec));
}
return childWidthMeasureSpec;
}
private int getChildHeightMeasureSpec(int heightMeasureSpec, LayoutParams lp) {
int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
getPaddingTop() + getPaddingBottom() + lp.topMargin
+ lp.bottomMargin, lp.height);
int childHeight = MeasureSpec.getSize(childHeightMeasureSpec);
if (childHeight > lp.maxHeight) {
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(lp.maxHeight,
MeasureSpec.getMode(childHeightMeasureSpec));
} else if (childHeight < lp.minHeight) {
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(lp.minHeight,
MeasureSpec.getMode(childHeightMeasureSpec));
}
return childHeightMeasureSpec;
}
/**
* Determines the cross size (Calculate the length along the cross axis).
* Expand the cross size only if the height mode is MeasureSpec.EXACTLY, otherwise
......@@ -2751,4 +2814,4 @@ public class FlexboxLayout extends ViewGroup {
'}';
}
}
}
\ No newline at end of file
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册