From a0cc0a8c163c0b8868995fb93bea438596a605b6 Mon Sep 17 00:00:00 2001 From: Takeshi Hagikura Date: Tue, 17 Mar 2020 19:47:10 +0900 Subject: [PATCH 1/2] Fix the issue item decorations are not included for judging if wrapping is required. Now RecyclerView#getItemDecorationCount and RecyclerView#getItemDecorationAt are exposed, retrieving the decoration length for each view is possible. This PR takes the length of the decorations into account when judging if a line wrapping is required. --- .../flexbox/test/FlexboxLayoutManagerTest.kt | 62 +++++++++++++++++++ .../android/flexbox/FlexboxLayoutManager.java | 24 +++++-- 2 files changed, 82 insertions(+), 4 deletions(-) diff --git a/flexbox/src/androidTest/java/com/google/android/flexbox/test/FlexboxLayoutManagerTest.kt b/flexbox/src/androidTest/java/com/google/android/flexbox/test/FlexboxLayoutManagerTest.kt index 26e6da6a..cd0cf6bf 100644 --- a/flexbox/src/androidTest/java/com/google/android/flexbox/test/FlexboxLayoutManagerTest.kt +++ b/flexbox/src/androidTest/java/com/google/android/flexbox/test/FlexboxLayoutManagerTest.kt @@ -2494,6 +2494,68 @@ class FlexboxLayoutManagerTest { assertThat(view7!!.left, isEqualAllowingError(activity.dpToPixel(200))) } + @Test + @FlakyTest + @Throws(Throwable::class) + fun testDecoration_length_is_included_in_wrapCondition_direction_row() { + val activity = activityRule.activity + val layoutManager = FlexboxLayoutManager(activity) + val adapter = TestAdapter() + val drawable = ResourcesCompat.getDrawable(activity.resources, R.drawable.divider_thick, null) + val itemDecoration = FlexboxItemDecoration(activity) + itemDecoration.setDrawable(drawable) + + activityRule.runOnUiThread { + activity.setContentView(R.layout.recyclerview) + val recyclerView = activity.findViewById(R.id.recyclerview) + layoutManager.flexDirection = FlexDirection.ROW + recyclerView.layoutManager = layoutManager + recyclerView.addItemDecoration(itemDecoration) + recyclerView.adapter = adapter + + adapter.addItem(createLayoutParams(activity, 90, 110)) + adapter.addItem(createLayoutParams(activity, 90, 110)) + adapter.addItem(createLayoutParams(activity, 90, 110)) + // RecyclerView width: 320, height: 240. + } + InstrumentationRegistry.getInstrumentation().waitForIdleSync() + + // Including the length of decorations, the size should be 2 + assertThat(layoutManager.flexDirection, `is`(FlexDirection.ROW)) + assertThat(layoutManager.flexLines.size, `is`(2)) + } + + @Test + @FlakyTest + @Throws(Throwable::class) + fun testDecoration_length_is_included_in_wrapCondition_direction_column() { + val activity = activityRule.activity + val layoutManager = FlexboxLayoutManager(activity) + val adapter = TestAdapter() + val drawable = ResourcesCompat.getDrawable(activity.resources, R.drawable.divider_thick, null) + val itemDecoration = FlexboxItemDecoration(activity) + itemDecoration.setDrawable(drawable) + + activityRule.runOnUiThread { + activity.setContentView(R.layout.recyclerview) + val recyclerView = activity.findViewById(R.id.recyclerview) + layoutManager.flexDirection = FlexDirection.COLUMN + recyclerView.layoutManager = layoutManager + recyclerView.addItemDecoration(itemDecoration) + recyclerView.adapter = adapter + + adapter.addItem(createLayoutParams(activity, 100, 68)) + adapter.addItem(createLayoutParams(activity, 100, 68)) + adapter.addItem(createLayoutParams(activity, 100, 68)) + // RecyclerView width: 320, height: 240. + } + InstrumentationRegistry.getInstrumentation().waitForIdleSync() + + // Including the length of decorations, the size should be 2 + assertThat(layoutManager.flexDirection, `is`(FlexDirection.COLUMN)) + assertThat(layoutManager.flexLines.size, `is`(2)) + } + @Test @FlakyTest @Throws(Throwable::class) diff --git a/flexbox/src/main/java/com/google/android/flexbox/FlexboxLayoutManager.java b/flexbox/src/main/java/com/google/android/flexbox/FlexboxLayoutManager.java index 21ea7338..79bedf8e 100644 --- a/flexbox/src/main/java/com/google/android/flexbox/FlexboxLayoutManager.java +++ b/flexbox/src/main/java/com/google/android/flexbox/FlexboxLayoutManager.java @@ -51,8 +51,8 @@ public class FlexboxLayoutManager extends RecyclerView.LayoutManager implements private static final String TAG = "FlexboxLayoutManager"; /** - * Temporary Rect instance to be passed to - * {@link RecyclerView.LayoutManager#calculateItemDecorationsForChild} + * Temporary Rect instance to be passed to a method that needs a Rect instance for receiving + * output (e.g.{@link RecyclerView.LayoutManager#calculateItemDecorationsForChild}) * to avoid creating a Rect instance every time. */ private static final Rect TEMP_RECT = new Rect(); @@ -114,6 +114,8 @@ public class FlexboxLayoutManager extends RecyclerView.LayoutManager implements */ private RecyclerView.Recycler mRecycler; + private RecyclerView mRecyclerView; + /** * A snapshot of the {@link RecyclerView.State} instance at a given moment. * It's not guaranteed that this instance has a reference to the latest State. @@ -389,11 +391,23 @@ public List getFlexLines() { @Override public int getDecorationLengthMainAxis(View view, int index, int indexInFlexLine) { + int result = 0; if (isMainAxisDirectionHorizontal()) { - return getLeftDecorationWidth(view) + getRightDecorationWidth(view); + result = result + getLeftDecorationWidth(view) + getRightDecorationWidth(view); + for (int i = 0; i < mRecyclerView.getItemDecorationCount(); i++) { + mRecyclerView.getItemDecorationAt(i).getItemOffsets( + TEMP_RECT, view, mRecyclerView, mState); + result = result + TEMP_RECT.right + TEMP_RECT.left; + } } else { - return getTopDecorationHeight(view) + getBottomDecorationHeight(view); + result = result + getTopDecorationHeight(view) + getBottomDecorationHeight(view); + for (int i = 0; i < mRecyclerView.getItemDecorationCount(); i++) { + mRecyclerView.getItemDecorationAt(i).getItemOffsets( + TEMP_RECT, view, mRecyclerView, mState); + result = result + TEMP_RECT.bottom - TEMP_RECT.top; + } } + return result; } @Override @@ -1902,11 +1916,13 @@ public void setRecycleChildrenOnDetach(boolean recycleChildrenOnDetach) { public void onAttachedToWindow(RecyclerView recyclerView) { super.onAttachedToWindow(recyclerView); mParent = (View) recyclerView.getParent(); + mRecyclerView = recyclerView; } @Override public void onDetachedFromWindow(RecyclerView view, RecyclerView.Recycler recycler) { super.onDetachedFromWindow(view, recycler); + mRecyclerView = null; if (mRecycleChildrenOnDetach) { if (DEBUG) { Log.d(TAG, "onDetachedFromWindow. Recycling children in the recycler"); From 8fb313b29f83049dfe39ae991985606b195b5c01 Mon Sep 17 00:00:00 2001 From: Takeshi Hagikura Date: Wed, 18 Mar 2020 14:37:20 +0900 Subject: [PATCH 2/2] Update the decoration length in the getDecorationLengthCrossAxis method --- .../android/flexbox/FlexboxLayoutManager.java | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/flexbox/src/main/java/com/google/android/flexbox/FlexboxLayoutManager.java b/flexbox/src/main/java/com/google/android/flexbox/FlexboxLayoutManager.java index 79bedf8e..08aed6a5 100644 --- a/flexbox/src/main/java/com/google/android/flexbox/FlexboxLayoutManager.java +++ b/flexbox/src/main/java/com/google/android/flexbox/FlexboxLayoutManager.java @@ -412,11 +412,23 @@ public int getDecorationLengthMainAxis(View view, int index, int indexInFlexLine @Override public int getDecorationLengthCrossAxis(View view) { + int result = 0; if (isMainAxisDirectionHorizontal()) { - return getTopDecorationHeight(view) + getBottomDecorationHeight(view); + result = result + getTopDecorationHeight(view) + getBottomDecorationHeight(view); + for (int i = 0; i < mRecyclerView.getItemDecorationCount(); i++) { + mRecyclerView.getItemDecorationAt(i).getItemOffsets( + TEMP_RECT, view, mRecyclerView, mState); + result = result + TEMP_RECT.bottom - TEMP_RECT.top; + } } else { - return getLeftDecorationWidth(view) + getRightDecorationWidth(view); + result = result + getLeftDecorationWidth(view) + getRightDecorationWidth(view); + for (int i = 0; i < mRecyclerView.getItemDecorationCount(); i++) { + mRecyclerView.getItemDecorationAt(i).getItemOffsets( + TEMP_RECT, view, mRecyclerView, mState); + result = result + TEMP_RECT.right + TEMP_RECT.left; + } } + return result; } @Override @@ -2373,8 +2385,7 @@ private int computeScrollRange(RecyclerView.State state) { } /** - * Copied from {@link RecyclerView.LayoutManager#shouldMeasureChild - * (View, + * Copied from RecyclerView.LayoutManager#shouldMeasureChild(View, * int, int, RecyclerView.LayoutParams)}} */ private boolean shouldMeasureChild(View child, int widthSpec, int heightSpec, @@ -2387,8 +2398,7 @@ private boolean shouldMeasureChild(View child, int widthSpec, int heightSpec, /** * Copied from - * {@link RecyclerView.LayoutManager#isMeasurementUpToDate(int, int, - * int)} + * RecyclerView.LayoutManager#isMeasurementUpToDate(int, int, int)} */ private static boolean isMeasurementUpToDate(int childSize, int spec, int dimension) { final int specMode = View.MeasureSpec.getMode(spec); @@ -2493,7 +2503,6 @@ private boolean isViewVisible(View view, boolean completelyVisible) { * @see #findFirstCompletelyVisibleItemPosition() * @see #findLastVisibleItemPosition() */ - @SuppressWarnings("WeakerAccess") public int findFirstVisibleItemPosition() { final View child = findOneVisibleChild(0, getChildCount(), false); return child == null ? NO_POSITION : getPosition(child); @@ -2508,7 +2517,6 @@ public int findFirstVisibleItemPosition() { * @see #findFirstVisibleItemPosition() * @see #findLastCompletelyVisibleItemPosition() */ - @SuppressWarnings("WeakerAccess") public int findFirstCompletelyVisibleItemPosition() { final View child = findOneVisibleChild(0, getChildCount(), true); return child == null ? NO_POSITION : getPosition(child); @@ -2527,7 +2535,6 @@ public int findFirstCompletelyVisibleItemPosition() { * @see #findLastCompletelyVisibleItemPosition() * @see #findFirstVisibleItemPosition() */ - @SuppressWarnings("WeakerAccess") public int findLastVisibleItemPosition() { final View child = findOneVisibleChild(getChildCount() - 1, -1, false); return child == null ? NO_POSITION : getPosition(child); @@ -2542,7 +2549,6 @@ public int findLastVisibleItemPosition() { * @see #findLastVisibleItemPosition() * @see #findFirstCompletelyVisibleItemPosition() */ - @SuppressWarnings("WeakerAccess") public int findLastCompletelyVisibleItemPosition() { final View child = findOneVisibleChild(getChildCount() - 1, -1, true); return child == null ? NO_POSITION : getPosition(child);