Skip to content

Commit

Permalink
[Carousel] Add vertical scrolling capability
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 542943240
  • Loading branch information
imhappi authored and raajkumars committed Jun 27, 2023
1 parent 2362f4b commit 6b48d3b
Show file tree
Hide file tree
Showing 11 changed files with 884 additions and 313 deletions.
7 changes: 7 additions & 0 deletions lib/java/com/google/android/material/carousel/Carousel.java
Expand Up @@ -18,6 +18,13 @@

/** An interface that defines a widget that can be configured as a Carousel. */
interface Carousel {

/** Gets the width of the carousel container. */
int getContainerWidth();

/** Gets the height of the carousel container. */
int getContainerHeight();

/** Whether or not the orientation is horizontal. */
boolean isHorizontal();
}
468 changes: 328 additions & 140 deletions lib/java/com/google/android/material/carousel/CarouselLayoutManager.java

Large diffs are not rendered by default.

Expand Up @@ -66,13 +66,16 @@ private int[] calculateDistanceToSnap(
return new int[] {0, 0};
}

int offset = 0;
int offset =
distanceToFirstFocalKeyline(view, (CarouselLayoutManager) layoutManager, partialSnap);
if (layoutManager.canScrollHorizontally()) {
offset =
distanceToFirstFocalKeyline(view, (CarouselLayoutManager) layoutManager, partialSnap);
return new int[] {offset, 0};
}
// TODO(b/279088745): Implement snap helper for vertical scrolling.
return new int[] {offset, 0};

if (layoutManager.canScrollVertically()) {
return new int[] {0, offset};
}
return new int[] {0, 0};
}

private int distanceToFirstFocalKeyline(
Expand All @@ -84,11 +87,7 @@ private int distanceToFirstFocalKeyline(
@Nullable
@Override
public View findSnapView(LayoutManager layoutManager) {
// TODO(b/279088745): Implement snap helper for vertical scrolling.
if (layoutManager.canScrollHorizontally()) {
return findViewNearestFirstKeyline(layoutManager);
}
return null;
return findViewNearestFirstKeyline(layoutManager);
}

/**
Expand Down
Expand Up @@ -34,8 +34,8 @@
* A {@link CarouselStrategy} that knows how to size and fit one large item and one small item into
* a container to create a layout to browse one 'hero' item at a time with a preview item.
*
* <p>Note that this strategy resizes Carousel items to take up the full width of the Carousel, save
* room for the small item.
* <p>Note that this strategy resizes Carousel items to take up the full width or height of the
* Carousel, save room for the small item.
*
* <p>This class will automatically be reversed by {@link CarouselLayoutManager} if being laid out
* right-to-left and does not need to make any account for layout direction itself.
Expand All @@ -48,52 +48,56 @@ public class HeroCarouselStrategy extends CarouselStrategy {
@Override
@NonNull
KeylineState onFirstChildMeasuredWithMargins(@NonNull Carousel carousel, @NonNull View child) {
float availableSpace = carousel.getContainerWidth();
int availableSpace = carousel.getContainerHeight();
if (carousel.isHorizontal()) {
availableSpace = carousel.getContainerWidth();
}

LayoutParams childLayoutParams = (LayoutParams) child.getLayoutParams();
float childHorizontalMargins = childLayoutParams.leftMargin + childLayoutParams.rightMargin;
float childMargins = childLayoutParams.topMargin + childLayoutParams.bottomMargin;

float measuredChildSize = child.getMeasuredWidth() * 2;

float smallChildWidthMin = getSmallSizeMin(child.getContext()) + childHorizontalMargins;
float smallChildWidthMax = getSmallSizeMax(child.getContext()) + childHorizontalMargins;
if (carousel.isHorizontal()) {
childMargins = childLayoutParams.leftMargin + childLayoutParams.rightMargin;
measuredChildSize = child.getMeasuredHeight() * 2;
}

float measuredChildHeight = child.getMeasuredHeight();
float measuredChildWidth = measuredChildHeight * 2;
float smallChildSizeMin = getSmallSizeMin(child.getContext()) + childMargins;
float smallChildSizeMax = getSmallSizeMax(child.getContext()) + childMargins;

float targetLargeChildWidth = min(measuredChildWidth + childHorizontalMargins, availableSpace);
float targetLargeChildSize = min(measuredChildSize + childMargins, availableSpace);
// Ideally we would like to create a balanced arrangement where a small item is 1/3 the size of
// the large item. Clamp the small target size within our min-max range and as close to 1/3 of
// the target large item size as possible.
float targetSmallChildWidth =
float targetSmallChildSize =
MathUtils.clamp(
measuredChildWidth / 3F + childHorizontalMargins,
getSmallSizeMin(child.getContext()) + childHorizontalMargins,
getSmallSizeMax(child.getContext()) + childHorizontalMargins);
float targetMediumChildWidth = (targetLargeChildWidth + targetSmallChildWidth) / 2F;
measuredChildSize / 3F + childMargins,
getSmallSizeMin(child.getContext()) + childMargins,
getSmallSizeMax(child.getContext()) + childMargins);
float targetMediumChildSize = (targetLargeChildSize + targetSmallChildSize) / 2F;

// Find the minimum space left for large items after filling the carousel with the most
// permissible small items to determine a plausible minimum large count.
float minAvailableLargeSpace =
availableSpace
- (smallChildWidthMax * maxValue(SMALL_COUNTS));
int largeCountMin = (int) max(1, floor(minAvailableLargeSpace / targetLargeChildWidth));
int largeCountMax = (int) ceil(availableSpace / targetLargeChildWidth);
float minAvailableLargeSpace = availableSpace - (smallChildSizeMax * maxValue(SMALL_COUNTS));
int largeCountMin = (int) max(1, floor(minAvailableLargeSpace / targetLargeChildSize));
int largeCountMax = (int) ceil(availableSpace / targetLargeChildSize);
int[] largeCounts = new int[largeCountMax - largeCountMin + 1];
for (int i = 0; i < largeCounts.length; i++) {
largeCounts[i] = largeCountMin + i;
}

Arrangement arrangement = Arrangement.findLowestCostArrangement(
availableSpace,
targetSmallChildWidth,
smallChildWidthMin,
smallChildWidthMax,
SMALL_COUNTS,
targetMediumChildWidth,
MEDIUM_COUNTS,
targetLargeChildWidth,
largeCounts);
availableSpace,
targetSmallChildSize,
smallChildSizeMin,
smallChildSizeMax,
SMALL_COUNTS,
targetMediumChildSize,
MEDIUM_COUNTS,
targetLargeChildSize,
largeCounts);
return createLeftAlignedKeylineState(
child.getContext(), childHorizontalMargins, availableSpace, arrangement);
child.getContext(), childMargins, availableSpace, arrangement);
}
}

0 comments on commit 6b48d3b

Please sign in to comment.