Skip to content

Commit 6b48d3b

Browse files
imhappiraajkumars
authored andcommittedJun 27, 2023
[Carousel] Add vertical scrolling capability
PiperOrigin-RevId: 542943240
1 parent 2362f4b commit 6b48d3b

11 files changed

+884
-313
lines changed
 

‎lib/java/com/google/android/material/carousel/Carousel.java

+7
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@
1818

1919
/** An interface that defines a widget that can be configured as a Carousel. */
2020
interface Carousel {
21+
2122
/** Gets the width of the carousel container. */
2223
int getContainerWidth();
24+
25+
/** Gets the height of the carousel container. */
26+
int getContainerHeight();
27+
28+
/** Whether or not the orientation is horizontal. */
29+
boolean isHorizontal();
2330
}

‎lib/java/com/google/android/material/carousel/CarouselLayoutManager.java

+328-140
Large diffs are not rendered by default.

‎lib/java/com/google/android/material/carousel/CarouselSnapHelper.java

+9-10
Original file line numberDiff line numberDiff line change
@@ -66,13 +66,16 @@ private int[] calculateDistanceToSnap(
6666
return new int[] {0, 0};
6767
}
6868

69-
int offset = 0;
69+
int offset =
70+
distanceToFirstFocalKeyline(view, (CarouselLayoutManager) layoutManager, partialSnap);
7071
if (layoutManager.canScrollHorizontally()) {
71-
offset =
72-
distanceToFirstFocalKeyline(view, (CarouselLayoutManager) layoutManager, partialSnap);
72+
return new int[] {offset, 0};
7373
}
74-
// TODO(b/279088745): Implement snap helper for vertical scrolling.
75-
return new int[] {offset, 0};
74+
75+
if (layoutManager.canScrollVertically()) {
76+
return new int[] {0, offset};
77+
}
78+
return new int[] {0, 0};
7679
}
7780

7881
private int distanceToFirstFocalKeyline(
@@ -84,11 +87,7 @@ private int distanceToFirstFocalKeyline(
8487
@Nullable
8588
@Override
8689
public View findSnapView(LayoutManager layoutManager) {
87-
// TODO(b/279088745): Implement snap helper for vertical scrolling.
88-
if (layoutManager.canScrollHorizontally()) {
89-
return findViewNearestFirstKeyline(layoutManager);
90-
}
91-
return null;
90+
return findViewNearestFirstKeyline(layoutManager);
9291
}
9392

9493
/**

‎lib/java/com/google/android/material/carousel/HeroCarouselStrategy.java

+34-30
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@
3434
* A {@link CarouselStrategy} that knows how to size and fit one large item and one small item into
3535
* a container to create a layout to browse one 'hero' item at a time with a preview item.
3636
*
37-
* <p>Note that this strategy resizes Carousel items to take up the full width of the Carousel, save
38-
* room for the small item.
37+
* <p>Note that this strategy resizes Carousel items to take up the full width or height of the
38+
* Carousel, save room for the small item.
3939
*
4040
* <p>This class will automatically be reversed by {@link CarouselLayoutManager} if being laid out
4141
* right-to-left and does not need to make any account for layout direction itself.
@@ -48,52 +48,56 @@ public class HeroCarouselStrategy extends CarouselStrategy {
4848
@Override
4949
@NonNull
5050
KeylineState onFirstChildMeasuredWithMargins(@NonNull Carousel carousel, @NonNull View child) {
51-
float availableSpace = carousel.getContainerWidth();
51+
int availableSpace = carousel.getContainerHeight();
52+
if (carousel.isHorizontal()) {
53+
availableSpace = carousel.getContainerWidth();
54+
}
5255

5356
LayoutParams childLayoutParams = (LayoutParams) child.getLayoutParams();
54-
float childHorizontalMargins = childLayoutParams.leftMargin + childLayoutParams.rightMargin;
57+
float childMargins = childLayoutParams.topMargin + childLayoutParams.bottomMargin;
58+
59+
float measuredChildSize = child.getMeasuredWidth() * 2;
5560

56-
float smallChildWidthMin = getSmallSizeMin(child.getContext()) + childHorizontalMargins;
57-
float smallChildWidthMax = getSmallSizeMax(child.getContext()) + childHorizontalMargins;
61+
if (carousel.isHorizontal()) {
62+
childMargins = childLayoutParams.leftMargin + childLayoutParams.rightMargin;
63+
measuredChildSize = child.getMeasuredHeight() * 2;
64+
}
5865

59-
float measuredChildHeight = child.getMeasuredHeight();
60-
float measuredChildWidth = measuredChildHeight * 2;
66+
float smallChildSizeMin = getSmallSizeMin(child.getContext()) + childMargins;
67+
float smallChildSizeMax = getSmallSizeMax(child.getContext()) + childMargins;
6168

62-
float targetLargeChildWidth = min(measuredChildWidth + childHorizontalMargins, availableSpace);
69+
float targetLargeChildSize = min(measuredChildSize + childMargins, availableSpace);
6370
// Ideally we would like to create a balanced arrangement where a small item is 1/3 the size of
6471
// the large item. Clamp the small target size within our min-max range and as close to 1/3 of
6572
// the target large item size as possible.
66-
float targetSmallChildWidth =
73+
float targetSmallChildSize =
6774
MathUtils.clamp(
68-
measuredChildWidth / 3F + childHorizontalMargins,
69-
getSmallSizeMin(child.getContext()) + childHorizontalMargins,
70-
getSmallSizeMax(child.getContext()) + childHorizontalMargins);
71-
float targetMediumChildWidth = (targetLargeChildWidth + targetSmallChildWidth) / 2F;
75+
measuredChildSize / 3F + childMargins,
76+
getSmallSizeMin(child.getContext()) + childMargins,
77+
getSmallSizeMax(child.getContext()) + childMargins);
78+
float targetMediumChildSize = (targetLargeChildSize + targetSmallChildSize) / 2F;
7279

7380
// Find the minimum space left for large items after filling the carousel with the most
7481
// permissible small items to determine a plausible minimum large count.
75-
float minAvailableLargeSpace =
76-
availableSpace
77-
- (smallChildWidthMax * maxValue(SMALL_COUNTS));
78-
int largeCountMin = (int) max(1, floor(minAvailableLargeSpace / targetLargeChildWidth));
79-
int largeCountMax = (int) ceil(availableSpace / targetLargeChildWidth);
82+
float minAvailableLargeSpace = availableSpace - (smallChildSizeMax * maxValue(SMALL_COUNTS));
83+
int largeCountMin = (int) max(1, floor(minAvailableLargeSpace / targetLargeChildSize));
84+
int largeCountMax = (int) ceil(availableSpace / targetLargeChildSize);
8085
int[] largeCounts = new int[largeCountMax - largeCountMin + 1];
8186
for (int i = 0; i < largeCounts.length; i++) {
8287
largeCounts[i] = largeCountMin + i;
8388
}
84-
8589
Arrangement arrangement = Arrangement.findLowestCostArrangement(
86-
availableSpace,
87-
targetSmallChildWidth,
88-
smallChildWidthMin,
89-
smallChildWidthMax,
90-
SMALL_COUNTS,
91-
targetMediumChildWidth,
92-
MEDIUM_COUNTS,
93-
targetLargeChildWidth,
94-
largeCounts);
90+
availableSpace,
91+
targetSmallChildSize,
92+
smallChildSizeMin,
93+
smallChildSizeMax,
94+
SMALL_COUNTS,
95+
targetMediumChildSize,
96+
MEDIUM_COUNTS,
97+
targetLargeChildSize,
98+
largeCounts);
9599
return createLeftAlignedKeylineState(
96-
child.getContext(), childHorizontalMargins, availableSpace, arrangement);
100+
child.getContext(), childMargins, availableSpace, arrangement);
97101
}
98102
}
99103

0 commit comments

Comments
 (0)