提交 844d1a0c 编写于 作者: T Takeshi Hagikura 提交者: Takeshi Hagikura

Fix the crash it occasionally happens when scrolled too fast toward the start (#153)

The crash happend because invalid next position is set in the fill method when the RecyclerView scrolls to top with the scroll speed too fast enough to put two or more flex lines in the fill method.
上级 add9faf3
......@@ -95,6 +95,16 @@ public class FlexboxHelperTest {
assertEquals(0, mFlexboxHelper.mIndexToFlexLine[1]);
assertEquals(1, mFlexboxHelper.mIndexToFlexLine[2]);
assertEquals(2, mFlexboxHelper.mIndexToFlexLine[3]);
FlexLine firstLine = result.mFlexLines.get(0);
assertEquals(0, firstLine.mFirstIndex);
assertEquals(1, firstLine.mLastIndex);
FlexLine secondLine = result.mFlexLines.get(1);
assertEquals(2, secondLine.mFirstIndex);
assertEquals(2, secondLine.mLastIndex);
FlexLine thirdLine = result.mFlexLines.get(2);
assertEquals(3, thirdLine.mFirstIndex);
assertEquals(3, thirdLine.mLastIndex);
}
@Test
......@@ -138,6 +148,16 @@ public class FlexboxHelperTest {
assertEquals(0, mFlexboxHelper.mIndexToFlexLine[1]);
assertEquals(1, mFlexboxHelper.mIndexToFlexLine[2]);
assertEquals(2, mFlexboxHelper.mIndexToFlexLine[3]);
FlexLine firstLine = result.mFlexLines.get(0);
assertEquals(0, firstLine.mFirstIndex);
assertEquals(1, firstLine.mLastIndex);
FlexLine secondLine = result.mFlexLines.get(1);
assertEquals(2, secondLine.mFirstIndex);
assertEquals(2, secondLine.mLastIndex);
FlexLine thirdLine = result.mFlexLines.get(2);
assertEquals(3, thirdLine.mFirstIndex);
assertEquals(3, thirdLine.mLastIndex);
}
@Test
......
......@@ -47,6 +47,7 @@ import static com.google.android.flexbox.test.IsEqualAllowingError.isEqualAllowi
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.core.IsNot.not;
import static org.junit.Assert.assertThat;
/**
......@@ -1244,6 +1245,181 @@ public class FlexboxLayoutManagerTest {
isEqualAllowingError(TestUtil.dpToPixel(activity, 50)));
}
@Test
@FlakyTest
public void testLargeItem_scrollFast_direction_row() throws Throwable {
final FlexboxTestActivity activity = mActivityRule.getActivity();
final FlexboxLayoutManager layoutManager = new FlexboxLayoutManager();
final TestAdapter adapter = new TestAdapter();
mActivityRule.runOnUiThread(new Runnable() {
@Override
public void run() {
activity.setContentView(R.layout.recyclerview);
RecyclerView recyclerView = (RecyclerView) activity.findViewById(R.id.recyclerview);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(adapter);
for (int i = 0; i < 200; i++) {
FlexboxLayoutManager.LayoutParams lp = createLayoutParams(activity, 100, 50);
adapter.addItem(lp);
}
// RecyclerView width: 320, height: 240.
}
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
assertThat(layoutManager.getFlexDirection(), is(FlexDirection.ROW));
assertThat(layoutManager.getFlexItemCount(), is(200));
// Only the visible items
assertThat(layoutManager.getChildCount(), is(not(200)));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.BOTTOM_CENTER,
GeneralLocation.TOP_CENTER));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.BOTTOM_CENTER,
GeneralLocation.TOP_CENTER));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.BOTTOM_CENTER,
GeneralLocation.TOP_CENTER));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.BOTTOM_CENTER,
GeneralLocation.TOP_CENTER));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.BOTTOM_CENTER,
GeneralLocation.TOP_CENTER));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.BOTTOM_CENTER,
GeneralLocation.TOP_CENTER));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.BOTTOM_CENTER,
GeneralLocation.TOP_CENTER));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.BOTTOM_CENTER,
GeneralLocation.TOP_CENTER));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.BOTTOM_CENTER,
GeneralLocation.TOP_CENTER));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.BOTTOM_CENTER,
GeneralLocation.TOP_CENTER));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.BOTTOM_CENTER,
GeneralLocation.TOP_CENTER));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.BOTTOM_CENTER,
GeneralLocation.TOP_CENTER));
// Should be scrolled to the bottom by now
assertThat(layoutManager.getFlexItemCount(), is(200));
// Only the visible items
assertThat(layoutManager.getChildCount(), is(not(200)));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.TOP_CENTER,
GeneralLocation.BOTTOM_CENTER));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.TOP_CENTER,
GeneralLocation.BOTTOM_CENTER));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.TOP_CENTER,
GeneralLocation.BOTTOM_CENTER));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.TOP_CENTER,
GeneralLocation.BOTTOM_CENTER));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.TOP_CENTER,
GeneralLocation.BOTTOM_CENTER));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.TOP_CENTER,
GeneralLocation.BOTTOM_CENTER));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.TOP_CENTER,
GeneralLocation.BOTTOM_CENTER));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.TOP_CENTER,
GeneralLocation.BOTTOM_CENTER));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.TOP_CENTER,
GeneralLocation.BOTTOM_CENTER));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.TOP_CENTER,
GeneralLocation.BOTTOM_CENTER));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.TOP_CENTER,
GeneralLocation.BOTTOM_CENTER));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.TOP_CENTER,
GeneralLocation.BOTTOM_CENTER));
// Should be scrolled to the top
assertThat(layoutManager.getFlexItemCount(), is(200));
// Only the visible items
assertThat(layoutManager.getChildCount(), is(not(200)));
}
@Test
@FlakyTest
public void testLargeItem_scrollFast_direction_column() throws Throwable {
final FlexboxTestActivity activity = mActivityRule.getActivity();
final FlexboxLayoutManager layoutManager = new FlexboxLayoutManager();
final TestAdapter adapter = new TestAdapter();
mActivityRule.runOnUiThread(new Runnable() {
@Override
public void run() {
activity.setContentView(R.layout.recyclerview);
RecyclerView recyclerView = (RecyclerView) activity.findViewById(R.id.recyclerview);
layoutManager.setFlexDirection(FlexDirection.COLUMN);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(adapter);
for (int i = 0; i < 200; i++) {
FlexboxLayoutManager.LayoutParams lp = createLayoutParams(activity, 70, 80);
adapter.addItem(lp);
}
// RecyclerView width: 320, height: 240.
}
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
assertThat(layoutManager.getFlexDirection(), is(FlexDirection.COLUMN));
assertThat(layoutManager.getFlexItemCount(), is(200));
// Only the visible items
assertThat(layoutManager.getChildCount(), is(not(200)));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.CENTER_RIGHT,
GeneralLocation.CENTER_LEFT));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.CENTER_RIGHT,
GeneralLocation.CENTER_LEFT));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.CENTER_RIGHT,
GeneralLocation.CENTER_LEFT));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.CENTER_RIGHT,
GeneralLocation.CENTER_LEFT));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.CENTER_RIGHT,
GeneralLocation.CENTER_LEFT));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.CENTER_RIGHT,
GeneralLocation.CENTER_LEFT));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.CENTER_RIGHT,
GeneralLocation.CENTER_LEFT));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.CENTER_RIGHT,
GeneralLocation.CENTER_LEFT));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.CENTER_RIGHT,
GeneralLocation.CENTER_LEFT));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.CENTER_RIGHT,
GeneralLocation.CENTER_LEFT));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.CENTER_RIGHT,
GeneralLocation.CENTER_LEFT));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.CENTER_RIGHT,
GeneralLocation.CENTER_LEFT));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.CENTER_RIGHT,
GeneralLocation.CENTER_LEFT));
// Should be scrolled to the right edge by now
assertThat(layoutManager.getFlexItemCount(), is(200));
// Only the visible items
assertThat(layoutManager.getChildCount(), is(not(200)));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.CENTER_LEFT,
GeneralLocation.CENTER_RIGHT));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.CENTER_LEFT,
GeneralLocation.CENTER_RIGHT));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.CENTER_LEFT,
GeneralLocation.CENTER_RIGHT));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.CENTER_LEFT,
GeneralLocation.CENTER_RIGHT));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.CENTER_LEFT,
GeneralLocation.CENTER_RIGHT));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.CENTER_LEFT,
GeneralLocation.CENTER_RIGHT));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.CENTER_LEFT,
GeneralLocation.CENTER_RIGHT));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.CENTER_LEFT,
GeneralLocation.CENTER_RIGHT));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.CENTER_LEFT,
GeneralLocation.CENTER_RIGHT));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.CENTER_LEFT,
GeneralLocation.CENTER_RIGHT));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.CENTER_LEFT,
GeneralLocation.CENTER_RIGHT));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.CENTER_LEFT,
GeneralLocation.CENTER_RIGHT));
onView(withId(R.id.recyclerview)).perform(swipe(GeneralLocation.CENTER_LEFT,
GeneralLocation.CENTER_RIGHT));
// Should be scrolled to the left edge by now
assertThat(layoutManager.getFlexItemCount(), is(200));
// Only the visible items
assertThat(layoutManager.getChildCount(), is(not(200)));
}
/**
* Creates a new flex item.
......
......@@ -85,6 +85,10 @@ public class FlexLine {
*/
List<Integer> mIndicesAlignSelfStretch = new ArrayList<>();
int mFirstIndex;
int mLastIndex;
/**
* @return the distance in pixels from the top edge of this view's parent
* to the top edge of this FlexLine.
......
......@@ -30,6 +30,7 @@ import java.util.Collections;
import java.util.List;
import static android.support.v7.widget.RecyclerView.NO_POSITION;
import static com.google.android.flexbox.FlexItem.FLEX_BASIS_PERCENT_DEFAULT;
/**
* Offers various calculations for Flexbox to use the common logic between the classes such as
......@@ -103,7 +104,7 @@ class FlexboxHelper {
orderForViewToBeAdded.order = ((FlexItem)
paramsForViewBeforeAdded).getOrder();
} else {
orderForViewToBeAdded.order = FlexboxLayout.LayoutParams.ORDER_DEFAULT;
orderForViewToBeAdded.order = FlexItem.ORDER_DEFAULT;
}
if (indexForViewBeforeAdded == -1 || indexForViewBeforeAdded == childCount) {
......@@ -140,8 +141,7 @@ class FlexboxHelper {
List<Order> orders = new ArrayList<>(childCount);
for (int i = 0; i < childCount; i++) {
View child = mFlexContainer.getFlexItemAt(i);
FlexItem flexItem = (FlexItem) child
.getLayoutParams();
FlexItem flexItem = (FlexItem) child.getLayoutParams();
Order order = new Order();
order.order = flexItem.getOrder();
order.index = i;
......@@ -250,6 +250,7 @@ class FlexboxHelper {
FlexLinesResult result = new FlexLinesResult();
List<FlexLine> flexLines;
FlexLine flexLine = new FlexLine();
flexLine.mFirstIndex = fromIndex;
if (existingLines == null) {
flexLines = new ArrayList<>();
} else {
......@@ -274,14 +275,14 @@ class FlexboxHelper {
View child = mFlexContainer.getReorderedFlexItemAt(i);
if (child == null) {
if (isLastFlexItem(i, childCount, flexLine)) {
addFlexLine(flexLines, flexLine);
addFlexLine(flexLines, flexLine, i);
}
continue;
} else if (child.getVisibility() == View.GONE) {
flexLine.mGoneItemCount++;
flexLine.mItemCount++;
if (isLastFlexItem(i, childCount, flexLine)) {
addFlexLine(flexLines, flexLine);
addFlexLine(flexLines, flexLine, i);
}
continue;
}
......@@ -291,7 +292,7 @@ class FlexboxHelper {
flexLine.mIndicesAlignSelfStretch.add(i);
}
int childWidth = flexItem.getWidth();
if (flexItem.getFlexBasisPercent() != FlexItem.FLEX_BASIS_PERCENT_DEFAULT
if (flexItem.getFlexBasisPercent() != FLEX_BASIS_PERCENT_DEFAULT
&& widthMode == View.MeasureSpec.EXACTLY) {
childWidth = Math.round(widthSize * flexItem.getFlexBasisPercent());
// Use the dimension from the layout_width attribute if the widthMode is not
......@@ -336,12 +337,13 @@ class FlexboxHelper {
child.getMeasuredWidth() + flexItem.getMarginLeft() + flexItem.getMarginRight(),
flexItem, i, indexInFlexLine)) {
if (flexLine.getItemCountNotGone() > 0) {
addFlexLine(flexLines, flexLine);
addFlexLine(flexLines, flexLine, i > 0 ? i - 1 : 0);
}
flexLine = new FlexLine();
flexLine.mItemCount = 1;
flexLine.mMainSize = paddingLeft + paddingRight;
flexLine.mFirstIndex = i;
largestHeightInRow = child.getMeasuredHeight() + flexItem.getMarginTop()
+ flexItem.getMarginBottom();
indexInFlexLine = 0;
......@@ -375,7 +377,7 @@ class FlexboxHelper {
+ flexItem.getMarginBottom());
}
if (isLastFlexItem(i, childCount, flexLine)) {
addFlexLine(flexLines, flexLine);
addFlexLine(flexLines, flexLine, i);
sumCrossSize += flexLine.mCrossSize;
}
......@@ -456,6 +458,7 @@ class FlexboxHelper {
FlexLinesResult result = new FlexLinesResult();
List<FlexLine> flexLines;
FlexLine flexLine = new FlexLine();
flexLine.mFirstIndex = fromIndex;
if (existingLines == null) {
flexLines = new ArrayList<>();
} else {
......@@ -479,14 +482,14 @@ class FlexboxHelper {
View child = mFlexContainer.getReorderedFlexItemAt(i);
if (child == null) {
if (isLastFlexItem(i, childCount, flexLine)) {
addFlexLine(flexLines, flexLine);
addFlexLine(flexLines, flexLine, i);
}
continue;
} else if (child.getVisibility() == View.GONE) {
flexLine.mGoneItemCount++;
flexLine.mItemCount++;
if (isLastFlexItem(i, childCount, flexLine)) {
addFlexLine(flexLines, flexLine);
addFlexLine(flexLines, flexLine, i);
}
continue;
}
......@@ -498,7 +501,7 @@ class FlexboxHelper {
int childHeight = flexItem.getHeight();
if (flexItem.getFlexBasisPercent()
!= FlexboxLayout.LayoutParams.FLEX_BASIS_PERCENT_DEFAULT
!= FlexItem.FLEX_BASIS_PERCENT_DEFAULT
&& heightMode == View.MeasureSpec.EXACTLY) {
childHeight = Math.round(heightSize * flexItem.getFlexBasisPercent());
// Use the dimension from the layout_height attribute if the heightMode is not
......@@ -527,8 +530,8 @@ class FlexboxHelper {
// Check the size constraint after the first measurement for the child
// To prevent the child's width/height violate the size constraints imposed by the
// {@link LayoutParams#mMinWidth}, {@link LayoutParams#mMinHeight},
// {@link LayoutParams#mMaxWidth} and {@link LayoutParams#mMaxHeight} attributes.
// {@link FlexItem#getMinWidth()}, {@link FlexItem#getMinHeight()},
// {@link FlexItem#getMaxWidth()} and {@link FlexItem#getMaxHeight()} attributes.
// E.g. When the child's layout_height is wrap_content the measured height may be
// less than the min height after the first measurement.
checkSizeConstraints(child, i);
......@@ -545,12 +548,13 @@ class FlexboxHelper {
flexItem,
i, indexInFlexLine)) {
if (flexLine.getItemCountNotGone() > 0) {
addFlexLine(flexLines, flexLine);
addFlexLine(flexLines, flexLine, i > 0 ? i - 1 : 0);
}
flexLine = new FlexLine();
flexLine.mItemCount = 1;
flexLine.mMainSize = paddingTop + paddingBottom;
flexLine.mFirstIndex = i;
largestWidthInColumn = child.getMeasuredWidth() + flexItem.getMarginLeft()
+ flexItem.getMarginRight();
indexInFlexLine = 0;
......@@ -573,7 +577,7 @@ class FlexboxHelper {
mFlexContainer.onNewFlexItemAdded(i, indexInFlexLine, flexLine);
if (isLastFlexItem(i, childCount, flexLine)) {
addFlexLine(flexLines, flexLine);
addFlexLine(flexLines, flexLine, i);
sumCrossSize += flexLine.mCrossSize;
}
......@@ -629,10 +633,10 @@ class FlexboxHelper {
return childIndex == childCount - 1 && flexLine.getItemCountNotGone() != 0;
}
private List<FlexLine> addFlexLine(List<FlexLine> flexLines, FlexLine flexLine) {
private void addFlexLine(List<FlexLine> flexLines, FlexLine flexLine, int viewIndex) {
mFlexContainer.onNewFlexLineAdded(flexLine);
flexLine.mLastIndex = viewIndex;
flexLines.add(flexLine);
return flexLines;
}
/**
......@@ -730,10 +734,10 @@ class FlexboxHelper {
for (FlexLine flexLine : mFlexContainer.getFlexLinesInternal()) {
if (flexLine.mMainSize < mainSize) {
fromIndex = expandFlexItems(widthMeasureSpec, heightMeasureSpec, flexLine,
mainSize, paddingAlongMainAxis, fromIndex);
mainSize, paddingAlongMainAxis, fromIndex, false);
} else {
fromIndex = shrinkFlexItems(widthMeasureSpec, heightMeasureSpec, flexLine,
mainSize, paddingAlongMainAxis, fromIndex);
mainSize, paddingAlongMainAxis, fromIndex, false);
}
}
}
......@@ -761,13 +765,14 @@ class FlexboxHelper {
* needs to
* be an absolute index in the flex container (FlexboxLayout),
* not the relative index in the flex line.
* @param calledRecursively true if this method is called recursively, false otherwise
* @return the next index, the next flex line's first flex item starts from the returned index
* @see FlexContainer#getFlexDirection()
* @see FlexContainer#setFlexDirection(int)
* @see FlexItem#getFlexGrow()
*/
private int expandFlexItems(int widthMeasureSpec, int heightMeasureSpec, FlexLine flexLine,
int maxMainSize, int paddingAlongMainAxis, int fromIndex) {
int maxMainSize, int paddingAlongMainAxis, int fromIndex, boolean calledRecursively) {
int childIndex = fromIndex;
if (flexLine.mTotalFlexGrow <= 0 || maxMainSize < flexLine.mMainSize) {
childIndex += flexLine.mItemCount;
......@@ -787,7 +792,9 @@ class FlexboxHelper {
// 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;
if (!calledRecursively) {
flexLine.mCrossSize = Integer.MIN_VALUE;
}
float accumulatedRoundError = 0;
for (int i = 0; i < flexLine.mItemCount; i++) {
View child = mFlexContainer.getReorderedFlexItemAt(childIndex);
......@@ -918,7 +925,7 @@ class FlexboxHelper {
// Re-invoke the method with the same fromIndex to distribute the positive free space
// that wasn't fully distributed (because of maximum length constraint)
expandFlexItems(widthMeasureSpec, heightMeasureSpec, flexLine, maxMainSize,
paddingAlongMainAxis, fromIndex);
paddingAlongMainAxis, fromIndex, true);
}
return childIndex;
}
......@@ -935,13 +942,14 @@ class FlexboxHelper {
* needs to
* be an absolute index in the flex container (FlexboxLayout),
* not the relative index in the flex line.
* @param calledRecursively true if this method is called recursively, false otherwise
* @return the next index, the next flex line's first flex item starts from the returned index
* @see FlexContainer#getFlexDirection()
* @see FlexContainer#setFlexDirection(int)
* @see FlexItem#getFlexShrink()
*/
private int shrinkFlexItems(int widthMeasureSpec, int heightMeasureSpec, FlexLine flexLine,
int maxMainSize, int paddingAlongMainAxis, int fromIndex) {
int maxMainSize, int paddingAlongMainAxis, int fromIndex, boolean calledRecursively) {
int childIndex = fromIndex;
int sizeBeforeShrink = flexLine.mMainSize;
if (flexLine.mTotalFlexShrink <= 0 || maxMainSize > flexLine.mMainSize) {
......@@ -962,7 +970,9 @@ class FlexboxHelper {
// 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;
if (!calledRecursively) {
flexLine.mCrossSize = Integer.MIN_VALUE;
}
for (int i = 0; i < flexLine.mItemCount; i++) {
View child = mFlexContainer.getReorderedFlexItemAt(childIndex);
if (child == null) {
......@@ -1086,7 +1096,7 @@ class FlexboxHelper {
// Re-invoke the method with the same fromIndex to distribute the negative free space
// that wasn't fully distributed (because some views length were not enough)
shrinkFlexItems(widthMeasureSpec, heightMeasureSpec, flexLine,
maxMainSize, paddingAlongMainAxis, fromIndex);
maxMainSize, paddingAlongMainAxis, fromIndex, true);
}
return childIndex;
}
......@@ -1351,6 +1361,7 @@ class FlexboxHelper {
* @param crossSize the cross size
*/
private void stretchViewVertically(View view, int crossSize) {
// TODO: For FlexboxLayoutManager, retrieve the measured width from the cache
FlexboxLayout.LayoutParams lp = (FlexboxLayout.LayoutParams) view.getLayoutParams();
int newHeight = crossSize - lp.topMargin - lp.bottomMargin;
newHeight = Math.max(newHeight, 0);
......@@ -1366,6 +1377,7 @@ class FlexboxHelper {
* @param crossSize the cross size
*/
private void stretchViewHorizontally(View view, int crossSize) {
// TODO: For FlexboxLayoutManager, retrieve the measured height from the cache
FlexboxLayout.LayoutParams lp = (FlexboxLayout.LayoutParams) view.getLayoutParams();
int newWidth = crossSize - lp.leftMargin - lp.rightMargin;
newWidth = Math.max(newWidth, 0);
......
......@@ -217,7 +217,7 @@ public class FlexboxLayoutManager extends RecyclerView.LayoutManager implements
if (mFlexDirection != flexDirection) {
mFlexDirection = flexDirection;
mOrientationHelper = null;
mFlexLines.clear();
clearFlexLines();
requestLayout();
}
}
......@@ -233,7 +233,7 @@ public class FlexboxLayoutManager extends RecyclerView.LayoutManager implements
if (mFlexWrap != flexWrap) {
mFlexWrap = flexWrap;
mOrientationHelper = null;
mFlexLines.clear();
clearFlexLines();
requestLayout();
}
}
......@@ -262,7 +262,7 @@ public class FlexboxLayoutManager extends RecyclerView.LayoutManager implements
public void setAlignItems(@AlignItems int alignItems) {
if (mAlignItems != alignItems) {
mAlignItems = alignItems;
mFlexLines.clear();
clearFlexLines();
requestLayout();
}
}
......@@ -277,7 +277,7 @@ public class FlexboxLayoutManager extends RecyclerView.LayoutManager implements
public void setAlignContent(@AlignContent int alignContent) {
if (mAlignContent != alignContent) {
mAlignContent = alignContent;
mFlexLines.clear();
clearFlexLines();
requestLayout();
}
}
......@@ -684,15 +684,10 @@ public class FlexboxLayoutManager extends RecyclerView.LayoutManager implements
*/
private int fill(RecyclerView.Recycler recycler, RecyclerView.State state,
LayoutState layoutState) {
if (DEBUG) {
Log.d(TAG, String.format("fill started. childCount: %d, %s, %s", getChildCount(),
mLayoutState, mAnchorInfo));
}
if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) {
if (layoutState.mAvailable < 0) {
layoutState.mScrollingOffset += layoutState.mAvailable;
}
// TODO: Crash might happen if scrolled too fast. Investigate the cause
recycleByLayoutState(recycler, layoutState);
}
int start = layoutState.mAvailable;
......@@ -700,6 +695,7 @@ public class FlexboxLayoutManager extends RecyclerView.LayoutManager implements
int consumed = 0;
while (remainingSpace > 0 && layoutState.hasMore(state, mFlexLines)) {
FlexLine flexLine = mFlexLines.get(layoutState.mFlexLinePosition);
layoutState.mPosition = flexLine.mFirstIndex;
consumed += layoutFlexLine(recycler, state, flexLine, layoutState);
layoutState.mOffset += flexLine.getCrossSize() * layoutState.mLayoutDirection;
remainingSpace -= flexLine.getCrossSize();
......@@ -712,10 +708,6 @@ public class FlexboxLayoutManager extends RecyclerView.LayoutManager implements
}
recycleByLayoutState(recycler, layoutState);
}
if (DEBUG) {
Log.d(TAG, String.format("fill ended. childCount: %d, %s, %s", getChildCount(),
mLayoutState, mAnchorInfo));
}
return start - layoutState.mAvailable;
}
......@@ -1061,7 +1053,7 @@ public class FlexboxLayoutManager extends RecyclerView.LayoutManager implements
mLayoutState.mScrollingOffset = LayoutState.SCROLLING_OFFSET_NaN;
mLayoutState.mFlexLinePosition = anchorInfo.mFlexLinePosition;
if (anchorInfo.mFlexLinePosition > 0) {
if (anchorInfo.mFlexLinePosition > 0 && mFlexLines.size() > anchorInfo.mFlexLinePosition) {
FlexLine currentLine = mFlexLines.get(anchorInfo.mFlexLinePosition);
mLayoutState.mFlexLinePosition--;
mLayoutState.mPosition -= currentLine.getItemCount();
......@@ -1182,7 +1174,7 @@ public class FlexboxLayoutManager extends RecyclerView.LayoutManager implements
- mOrientationHelper.getEndAfterPadding();
// If the RecyclerView tries to scroll beyond the already calculated
// flex container, need to calculate until the amount that needs to be filled
// flex container, need to calculate beyond the amount that needs to be filled
if ((mLayoutState.mFlexLinePosition == NO_POSITION
|| mLayoutState.mFlexLinePosition > mFlexLines.size() - 1) &&
mLayoutState.mPosition <= getFlexItemCount()) {
......@@ -1193,9 +1185,15 @@ public class FlexboxLayoutManager extends RecyclerView.LayoutManager implements
.makeMeasureSpec(getHeight(), getHeightMode());
int needsToFill = absDelta - mLayoutState.mScrollingOffset;
if (needsToFill > 0) {
mFlexboxHelper.calculateHorizontalFlexLines(
widthMeasureSpec, heightMeasureSpec, needsToFill,
mLayoutState.mPosition, mFlexLines);
if (isMainAxisDirectionHorizontal()) {
mFlexboxHelper.calculateHorizontalFlexLines(
widthMeasureSpec, heightMeasureSpec, needsToFill,
mLayoutState.mPosition, mFlexLines);
} else {
mFlexboxHelper.calculateVerticalFlexLines(
widthMeasureSpec, heightMeasureSpec, needsToFill,
mLayoutState.mPosition, mFlexLines);
}
}
}
} else {
......@@ -1210,7 +1208,7 @@ public class FlexboxLayoutManager extends RecyclerView.LayoutManager implements
// The position of the next item toward start should be on the next flex line,
// shifting the position by the number of the items in the current line.
mLayoutState.mPosition = position - currentLine.getItemCount();
mLayoutState.mFlexLinePosition = flexLinePosition;
mLayoutState.mFlexLinePosition = flexLinePosition > 0 ? flexLinePosition - 1 : 0;
mLayoutState.mOffset = mOrientationHelper.getDecoratedStart(firstVisible);
mLayoutState.mScrollingOffset = -mOrientationHelper.getDecoratedStart(firstVisible)
......@@ -1253,6 +1251,11 @@ public class FlexboxLayoutManager extends RecyclerView.LayoutManager implements
return false;
}
private void clearFlexLines() {
mFlexLines.clear();
mAnchorInfo.reset();
}
/**
* LayoutParams used by the {@link FlexboxLayoutManager}, which stores per-child information
* required for the Flexbox.
......@@ -1587,34 +1590,16 @@ public class FlexboxLayoutManager extends RecyclerView.LayoutManager implements
} else {
mCoordinate = mOrientationHelper.getDecoratedStart(view);
}
int position = getPosition(view);
// It's likely that the view is the first item in a flex line, but if not get the
// index of the first item in the same line because the calculation of the flex lines
// expects that it starts from the first item in a flex line
mPosition = getFirstItemIndexInLine(position);
mPosition = getPosition(view);
assert mFlexboxHelper.mIndexToFlexLine != null;
int flexLinePosition = mFlexboxHelper.mIndexToFlexLine[mPosition];
mFlexLinePosition = flexLinePosition != NO_POSITION ? flexLinePosition : 0;
}
/**
* @param index the index in which the flex is determined
* @return the index of the flex item that is the first item in the same line
*/
private int getFirstItemIndexInLine(int index) {
assert mFlexboxHelper.mIndexToFlexLine != null;
int flexLinePosition = mFlexboxHelper.mIndexToFlexLine[index];
if (index == 0 || mFlexboxHelper.mIndexToFlexLine[index - 1] != flexLinePosition) {
return index;
}
for (int i = index - 1; i > 0; i--) {
int first = mFlexboxHelper.mIndexToFlexLine[i];
int second = mFlexboxHelper.mIndexToFlexLine[i - 1];
if (first != second) {
return first;
}
// It's likely that the view is the first item in a flex line, but if not get the
// index of the first item in the same line because the calculation of the flex lines
// expects that it starts from the first item in a flex line
if (mFlexLines.size() > mFlexLinePosition) {
mPosition = mFlexLines.get(mFlexLinePosition).mFirstIndex;
}
return 0;
}
@Override
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册