Handle padding in wrap content
This CL fixes a bug in GridLayoutManager and StaggeredGridLM where
they were ignoring padding in non-scroll direction when calculating
the content wrapping. This would eventually cause clipped children.
I've also fixed some code-style errors.
Change-Id: I7dca4a30007596ae52bf367e5f7af9a10e387954
Bug: 26769843
diff --git a/v7/recyclerview/src/android/support/v7/widget/GridLayoutManager.java b/v7/recyclerview/src/android/support/v7/widget/GridLayoutManager.java
index c1c0d6d..7f541fe 100644
--- a/v7/recyclerview/src/android/support/v7/widget/GridLayoutManager.java
+++ b/v7/recyclerview/src/android/support/v7/widget/GridLayoutManager.java
@@ -279,15 +279,17 @@
super.setMeasuredDimension(childrenBounds, wSpec, hSpec);
}
final int width, height;
+ final int horizontalPadding = getPaddingLeft() + getPaddingRight();
+ final int verticalPadding = getPaddingTop() + getPaddingBottom();
if (mOrientation == VERTICAL) {
- int usedHeight = childrenBounds.height() + getPaddingTop() + getPaddingBottom();
+ final int usedHeight = childrenBounds.height() + verticalPadding;
height = chooseSize(hSpec, usedHeight, getMinimumHeight());
- width = chooseSize(wSpec, mCachedBorders[mCachedBorders.length - 1],
+ width = chooseSize(wSpec, mCachedBorders[mCachedBorders.length - 1] + horizontalPadding,
getMinimumWidth());
} else {
- int usedWidth = childrenBounds.width() + getPaddingLeft() + getPaddingRight();
+ final int usedWidth = childrenBounds.width() + horizontalPadding;
width = chooseSize(wSpec, usedWidth, getMinimumWidth());
- height = chooseSize(hSpec, mCachedBorders[mCachedBorders.length - 1],
+ height = chooseSize(hSpec, mCachedBorders[mCachedBorders.length - 1] + verticalPadding,
getMinimumHeight());
}
setMeasuredDimension(width, height);
diff --git a/v7/recyclerview/src/android/support/v7/widget/LinearLayoutManager.java b/v7/recyclerview/src/android/support/v7/widget/LinearLayoutManager.java
index fc8c523..34e1450 100644
--- a/v7/recyclerview/src/android/support/v7/widget/LinearLayoutManager.java
+++ b/v7/recyclerview/src/android/support/v7/widget/LinearLayoutManager.java
@@ -16,6 +16,8 @@
package android.support.v7.widget;
+import static android.support.v7.widget.RecyclerView.NO_POSITION;
+
import android.content.Context;
import android.graphics.PointF;
import android.os.Parcel;
@@ -33,8 +35,6 @@
import java.util.List;
-import static android.support.v7.widget.RecyclerView.NO_POSITION;
-
/**
* A {@link android.support.v7.widget.RecyclerView.LayoutManager} implementation which provides
* similar functionality to {@link android.widget.ListView}.
diff --git a/v7/recyclerview/src/android/support/v7/widget/StaggeredGridLayoutManager.java b/v7/recyclerview/src/android/support/v7/widget/StaggeredGridLayoutManager.java
index a121ec9..7b92de5 100644
--- a/v7/recyclerview/src/android/support/v7/widget/StaggeredGridLayoutManager.java
+++ b/v7/recyclerview/src/android/support/v7/widget/StaggeredGridLayoutManager.java
@@ -16,6 +16,12 @@
package android.support.v7.widget;
+import static android.support.v7.widget.LayoutState.ITEM_DIRECTION_HEAD;
+import static android.support.v7.widget.LayoutState.ITEM_DIRECTION_TAIL;
+import static android.support.v7.widget.LayoutState.LAYOUT_END;
+import static android.support.v7.widget.LayoutState.LAYOUT_START;
+import static android.support.v7.widget.RecyclerView.NO_POSITION;
+
import android.content.Context;
import android.graphics.PointF;
import android.graphics.Rect;
@@ -37,12 +43,6 @@
import java.util.BitSet;
import java.util.List;
-import static android.support.v7.widget.LayoutState.ITEM_DIRECTION_HEAD;
-import static android.support.v7.widget.LayoutState.ITEM_DIRECTION_TAIL;
-import static android.support.v7.widget.LayoutState.LAYOUT_END;
-import static android.support.v7.widget.LayoutState.LAYOUT_START;
-import static android.support.v7.widget.RecyclerView.NO_POSITION;
-
/**
* A LayoutManager that lays out children in a staggered grid formation.
* It supports horizontal & vertical layout as well as an ability to layout children in reverse.
@@ -569,14 +569,18 @@
public void setMeasuredDimension(Rect childrenBounds, int wSpec, int hSpec) {
// we don't like it to wrap content in our non-scroll direction.
final int width, height;
+ final int horizontalPadding = getPaddingLeft() + getPaddingRight();
+ final int verticalPadding = getPaddingTop() + getPaddingBottom();
if (mOrientation == VERTICAL) {
- int usedHeight = childrenBounds.height() + getPaddingTop() + getPaddingBottom();
+ final int usedHeight = childrenBounds.height() + verticalPadding;
height = chooseSize(hSpec, usedHeight, getMinimumHeight());
- width = chooseSize(wSpec, mSizePerSpan * mSpanCount, getMinimumWidth());
+ width = chooseSize(wSpec, mSizePerSpan * mSpanCount + horizontalPadding,
+ getMinimumWidth());
} else {
- int usedWidth = childrenBounds.width() + getPaddingLeft() + getPaddingRight();
+ final int usedWidth = childrenBounds.width() + horizontalPadding;
width = chooseSize(wSpec, usedWidth, getMinimumWidth());
- height = chooseSize(hSpec, mSizePerSpan * mSpanCount, getMinimumHeight());
+ height = chooseSize(hSpec, mSizePerSpan * mSpanCount + verticalPadding,
+ getMinimumHeight());
}
setMeasuredDimension(width, height);
}
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/BaseWrapContentTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/BaseWrapContentTest.java
index 23aa5d5..c085244 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/BaseWrapContentTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/BaseWrapContentTest.java
@@ -15,6 +15,12 @@
*/
package android.support.v7.widget;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
import android.content.Context;
import android.graphics.Color;
import android.graphics.Rect;
@@ -31,12 +37,6 @@
import java.util.Collections;
import java.util.List;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
/**
* Class to test any generic wrap content behavior.
* It does so by running the same view scenario twice. Once with match parent setup to record all
@@ -146,21 +146,27 @@
recyclerView.setLayoutManager(createLayoutManager());
recyclerView.setAdapter(adapter);
recyclerView.setLayoutParams(lp);
+ Rect padding = mWrapContentConfig.padding;
+ recyclerView.setPadding(padding.left, padding.top,
+ padding.right, padding.bottom);
setRecyclerView(recyclerView);
recyclerView.waitUntilLayout();
Snapshot snapshot = takeSnapshot();
int index = 0;
+ Rect tmp = new Rect();
for (BaseWrapContentWithAspectRatioTest.MeasureBehavior behavior : adapter.behaviors) {
+ tmp.set(expected[index]);
+ tmp.offset(padding.left, padding.top);
assertThat("behavior " + index, snapshot.mChildCoordinates.get(behavior.getId()),
- is(expected[index]));
+ is(tmp));
index ++;
}
Rect boundingBox = new Rect(0, 0, 0, 0);
for (Rect rect : expected) {
boundingBox.union(rect);
}
- assertThat(recyclerView.getWidth(), is(width));
- assertThat(recyclerView.getHeight(), is(height));
+ assertThat(recyclerView.getWidth(), is(width + padding.left + padding.right));
+ assertThat(recyclerView.getHeight(), is(height + padding.top + padding.bottom));
}
@@ -469,14 +475,21 @@
static class WrapContentConfig {
- private boolean unlimitedWidth;
- private boolean unlimitedHeight;
+ public boolean unlimitedWidth;
+ public boolean unlimitedHeight;
+ public Rect padding = new Rect(0, 0, 0, 0);
public WrapContentConfig(boolean unlimitedWidth, boolean unlimitedHeight) {
this.unlimitedWidth = unlimitedWidth;
this.unlimitedHeight = unlimitedHeight;
}
+ public WrapContentConfig(boolean unlimitedWidth, boolean unlimitedHeight, Rect padding) {
+ this.unlimitedWidth = unlimitedWidth;
+ this.unlimitedHeight = unlimitedHeight;
+ this.padding.set(padding);
+ }
+
public boolean isUnlimitedWidth() {
return unlimitedWidth;
}
@@ -497,10 +510,11 @@
@Override
public String toString() {
- return "WrapContentConfig{" +
- "unlimitedWidth=" + unlimitedWidth +
- ", unlimitedHeight=" + unlimitedHeight +
- '}';
+ return "WrapContentConfig{"
+ + "unlimitedWidth=" + unlimitedWidth
+ + ", unlimitedHeight=" + unlimitedHeight
+ + ", padding=" + padding
+ + '}';
}
public TestedFrameLayout.FullControlLayoutParams toLayoutParams(int wDim, int hDim) {
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerWrapContentTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerWrapContentTest.java
index c8fdbeb..fd409c6 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerWrapContentTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerWrapContentTest.java
@@ -15,31 +15,70 @@
*/
package android.support.v7.widget;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
+import static android.support.v7.widget.BaseWrapContentWithAspectRatioTest.AspectRatioMeasureBehavior;
+import static android.support.v7.widget.BaseWrapContentWithAspectRatioTest.MeasureBehavior;
+import static android.support.v7.widget.BaseWrapContentWithAspectRatioTest.WrapContentAdapter;
+import static android.support.v7.widget.LinearLayoutManager.HORIZONTAL;
+import static android.support.v7.widget.LinearLayoutManager.VERTICAL;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-import android.graphics.Color;
import android.graphics.Rect;
import android.view.Gravity;
-import static android.support.v7.widget.LinearLayoutManager.HORIZONTAL;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-import static android.support.v7.widget.BaseWrapContentWithAspectRatioTest.*;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
-@RunWith(JUnit4.class)
+import java.util.Arrays;
+import java.util.List;
+
+@RunWith(Parameterized.class)
public class GridLayoutManagerWrapContentTest extends BaseWrapContentTest {
+ private boolean mHorizontal = false;
+ private int mSpanCount = 3;
+ public GridLayoutManagerWrapContentTest(Rect padding) {
+ super(new WrapContentConfig(false, false, padding));
+ }
- public GridLayoutManagerWrapContentTest() {
- super(new WrapContentConfig(false, false));
+ @Parameterized.Parameters(name = "paddingRect={0}")
+ public static List<Rect> params() {
+ return Arrays.asList(
+ new Rect(0, 0, 0, 0),
+ new Rect(5, 0, 0, 0),
+ new Rect(0, 3, 0, 0),
+ new Rect(0, 0, 2, 0),
+ new Rect(0, 0, 0, 7),
+ new Rect(3, 5, 7, 11)
+ );
}
@Override
RecyclerView.LayoutManager createLayoutManager() {
- return new GridLayoutManager(getActivity(), 3);
+ GridLayoutManager lm = new GridLayoutManager(getActivity(), mSpanCount);
+ lm.setOrientation(mHorizontal ? HORIZONTAL : VERTICAL);
+ return lm;
+ }
+
+ @Test
+ public void testHorizontal() throws Throwable {
+ mHorizontal = true;
+ mSpanCount = 2;
+ TestedFrameLayout.FullControlLayoutParams lp =
+ mWrapContentConfig.toLayoutParams(WRAP_CONTENT, WRAP_CONTENT);
+ WrapContentAdapter adapter = new WrapContentAdapter(
+ new MeasureBehavior(10, 10, WRAP_CONTENT, WRAP_CONTENT),
+ new MeasureBehavior(10, 10, WRAP_CONTENT, WRAP_CONTENT),
+ new MeasureBehavior(10, 10, WRAP_CONTENT, WRAP_CONTENT),
+ new MeasureBehavior(20, 10, WRAP_CONTENT, WRAP_CONTENT)
+ );
+ Rect[] expected = new Rect[] {
+ new Rect(0, 0, 10, 10),
+ new Rect(0, 10, 10, 20),
+ new Rect(10, 0, 30, 10),
+ new Rect(10, 10, 30, 20)
+ };
+ layoutAndCheck(lp, adapter, expected, 30, 20);
}
@Test
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerWrapContentTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerWrapContentTest.java
index 52c27bb..2a411b3 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerWrapContentTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerWrapContentTest.java
@@ -16,20 +16,22 @@
package android.support.v7.widget;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
+import static android.support.v7.widget.BaseLinearLayoutManagerTest.Config;
+import static android.support.v7.widget.LinearLayoutManager.HORIZONTAL;
+import static android.support.v7.widget.LinearLayoutManager.VERTICAL;
+import android.graphics.Rect;
import android.support.v4.view.ViewCompat;
import android.test.suitebuilder.annotation.MediumTest;
import android.view.Gravity;
-import java.util.ArrayList;
-import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
-import static android.support.v7.widget.BaseLinearLayoutManagerTest.Config;
-import static android.support.v7.widget.LinearLayoutManager.HORIZONTAL;
-import static android.support.v7.widget.LinearLayoutManager.VERTICAL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
@RunWith(Parameterized.class)
@MediumTest
@@ -100,22 +102,32 @@
@Parameterized.Parameters(name = "{0} {1}")
public static Iterable<Object[]> data() {
List<Object[]> params = new ArrayList<>();
- for (int orientation : new int[]{VERTICAL, HORIZONTAL}) {
- for (boolean reverseLayout : new boolean[]{false, true}) {
- for (boolean stackFromBottom : new boolean[]{false, true}) {
- params.add(
- new Object[]{
- new Config(orientation, reverseLayout, stackFromBottom),
- new WrapContentConfig(false, false)
- }
- );
- params.add(
- new Object[]{
- new Config(orientation, reverseLayout, stackFromBottom),
- new WrapContentConfig(HORIZONTAL == orientation,
- VERTICAL == orientation)
- }
- );
+ List<Rect> paddings = Arrays.asList(
+ new Rect(0, 0, 0, 0),
+ new Rect(5, 0, 0, 0),
+ new Rect(0, 6, 0, 0),
+ new Rect(0, 0, 7, 0),
+ new Rect(0, 0, 0, 8),
+ new Rect(3, 5, 7, 11)
+ );
+ for (Rect padding : paddings) {
+ for (int orientation : new int[]{VERTICAL, HORIZONTAL}) {
+ for (boolean reverseLayout : new boolean[]{false, true}) {
+ for (boolean stackFromBottom : new boolean[]{false, true}) {
+ params.add(
+ new Object[]{
+ new Config(orientation, reverseLayout, stackFromBottom),
+ new WrapContentConfig(false, false, new Rect(padding))
+ }
+ );
+ params.add(
+ new Object[]{
+ new Config(orientation, reverseLayout, stackFromBottom),
+ new WrapContentConfig(HORIZONTAL == orientation,
+ VERTICAL == orientation, new Rect(padding))
+ }
+ );
+ }
}
}
}
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerWrapContentTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerWrapContentTest.java
index 5b04302..6b44e4f 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerWrapContentTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerWrapContentTest.java
@@ -15,24 +15,41 @@
*/
package android.support.v7.widget;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
+
+import static android.support.v7.widget.BaseWrapContentWithAspectRatioTest.MeasureBehavior;
+import static android.support.v7.widget.BaseWrapContentWithAspectRatioTest.WrapContentAdapter;
+import static android.support.v7.widget.StaggeredGridLayoutManager.HORIZONTAL;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import android.graphics.Rect;
import android.view.Gravity;
import android.view.View;
-import static android.support.v7.widget.StaggeredGridLayoutManager.VERTICAL;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-import static android.support.v7.widget.BaseWrapContentWithAspectRatioTest.*;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
-@RunWith(JUnit4.class)
+import java.util.Arrays;
+import java.util.List;
+
+@RunWith(Parameterized.class)
public class StaggeredGridLayoutManagerWrapContentTest extends BaseWrapContentTest {
+ int mOrientation = StaggeredGridLayoutManager.VERTICAL;
+ public StaggeredGridLayoutManagerWrapContentTest(Rect padding) {
+ super(new WrapContentConfig(false, false, padding));
+ }
- public StaggeredGridLayoutManagerWrapContentTest() {
- super(new WrapContentConfig(false, false));
+ @Parameterized.Parameters(name = "paddingRect={0}")
+ public static List<Rect> params() {
+ return Arrays.asList(
+ new Rect(0, 0, 0, 0),
+ new Rect(5, 0, 0, 0),
+ new Rect(0, 3, 0, 0),
+ new Rect(0, 0, 2, 0),
+ new Rect(0, 0, 0, 7),
+ new Rect(3, 5, 7, 11)
+ );
}
@Test
@@ -55,6 +72,26 @@
}
@Test
+ public void testSimpleHorizontal() throws Throwable {
+ mOrientation = HORIZONTAL;
+ TestedFrameLayout.FullControlLayoutParams lp =
+ mWrapContentConfig.toLayoutParams(WRAP_CONTENT, WRAP_CONTENT);
+ WrapContentAdapter adapter = new WrapContentAdapter(
+ new MeasureBehavior(10, 10, WRAP_CONTENT, WRAP_CONTENT),
+ new MeasureBehavior(15, 10, WRAP_CONTENT, WRAP_CONTENT),
+ new MeasureBehavior(20, 10, WRAP_CONTENT, WRAP_CONTENT),
+ new MeasureBehavior(10, 20, WRAP_CONTENT, WRAP_CONTENT)
+ );
+ Rect[] expected = new Rect[] {
+ new Rect(0, 0, 10, 10),
+ new Rect(0, 20, 15, 30),
+ new Rect(0, 40, 20, 50),
+ new Rect(10, 0, 20, 20)
+ };
+ layoutAndCheck(lp, adapter, expected, 20, 60);
+ }
+
+ @Test
public void testUnspecifiedWidth() throws Throwable {
TestedFrameLayout.FullControlLayoutParams lp =
mWrapContentConfig.toLayoutParams(WRAP_CONTENT, WRAP_CONTENT);
@@ -96,7 +133,7 @@
@Override
RecyclerView.LayoutManager createLayoutManager() {
- return new StaggeredGridLayoutManager(3, VERTICAL);
+ return new StaggeredGridLayoutManager(3, mOrientation);
}
@Override