Skip to content

Commit 1ef42e2

Browse files
imhappipekingme
authored andcommitted
[Carousel] Shift keylines in contained strategies when there exists padding, and clipToPadding=false
PiperOrigin-RevId: 595162372
1 parent 701d5c2 commit 1ef42e2

File tree

7 files changed

+345
-44
lines changed

7 files changed

+345
-44
lines changed

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

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,32 @@ public int getCarouselAlignment() {
224224
return carouselAlignment;
225225
}
226226

227+
private int getLeftOrTopPaddingForKeylineShift() {
228+
// TODO(b/316969331): Fix keyline shifting by decreasing carousel size when carousel is clipped
229+
// to padding.
230+
// TODO(b/316968490): Fix keyline shifting by adjusting cutoffs if strategy is not contained.
231+
if (getClipToPadding() || !carouselStrategy.isContained()) {
232+
return 0;
233+
}
234+
if (getOrientation() == VERTICAL) {
235+
return getPaddingTop();
236+
}
237+
return getPaddingLeft();
238+
}
239+
240+
private int getRightOrBottomPaddingForKeylineShift() {
241+
// TODO(b/316969331): Fix keyline shifting by decreasing carousel size when carousel is clipped
242+
// to padding.
243+
// TODO(b/316968490): Fix keyline shifting by adjusting cutoffs if strategy is not contained.
244+
if (getClipToPadding() || !carouselStrategy.isContained()) {
245+
return 0;
246+
}
247+
if (getOrientation() == VERTICAL) {
248+
return getPaddingBottom();
249+
}
250+
return getPaddingRight();
251+
}
252+
227253
@Override
228254
public LayoutParams generateDefaultLayoutParams() {
229255
return new LayoutParams(
@@ -319,7 +345,21 @@ private void recalculateKeylineStateList(Recycler recycler) {
319345
keylineStateList =
320346
KeylineStateList.from(
321347
this,
322-
isLayoutRtl() ? KeylineState.reverse(keylineState, getContainerSize()) : keylineState);
348+
isLayoutRtl() ? KeylineState.reverse(keylineState, getContainerSize()) : keylineState,
349+
getItemMargins(),
350+
getLeftOrTopPaddingForKeylineShift(),
351+
getRightOrBottomPaddingForKeylineShift());
352+
}
353+
354+
private int getItemMargins() {
355+
if (getChildCount() > 0) {
356+
LayoutParams lp = (LayoutParams) getChildAt(0).getLayoutParams();
357+
if (orientationHelper.orientation == HORIZONTAL) {
358+
return lp.leftMargin + lp.rightMargin;
359+
}
360+
return lp.topMargin + lp.bottomMargin;
361+
}
362+
return 0;
323363
}
324364

325365
/**
@@ -795,9 +835,9 @@ private int calculateStartScroll(@NonNull KeylineStateList stateList) {
795835
KeylineState startState = isRtl ? stateList.getEndState() : stateList.getStartState();
796836
Keyline startFocalKeyline =
797837
isRtl ? startState.getLastFocalKeyline() : startState.getFirstFocalKeyline();
798-
float firstItemDistanceFromStart = getPaddingStart() * (isRtl ? 1 : -1);
799838
float firstItemStart = addStart(startFocalKeyline.loc, startState.getItemSize() / 2F);
800-
return (int) (firstItemDistanceFromStart + getParentStart() - firstItemStart);
839+
// This value already includes any padding since startFocalKeyline.loc is already adjusted
840+
return (int) (getParentStart() - firstItemStart);
801841
}
802842

803843
/**
@@ -811,9 +851,10 @@ private int calculateEndScroll(State state, KeylineStateList stateList) {
811851
isRtl ? endState.getFirstFocalKeyline() : endState.getLastFocalKeyline();
812852
// Get the total distance from the first item to the last item in the end-to-end model
813853
float lastItemDistanceFromFirstItem =
814-
(((state.getItemCount() - 1) * endState.getItemSize()) + getPaddingEnd())
815-
* (isRtl ? -1F : 1F);
854+
((state.getItemCount() - 1) * endState.getItemSize()) * (isRtl ? -1F : 1F);
816855

856+
float endPadding =
857+
isRtl ? -endFocalKeyline.leftOrTopPaddingShift : endFocalKeyline.rightOrBottomPaddingShift;
817858
float endFocalLocDistanceFromStart = endFocalKeyline.loc - getParentStart();
818859
float endFocalLocDistanceFromEnd = getParentEnd() - endFocalKeyline.loc;
819860

@@ -824,7 +865,10 @@ private int calculateEndScroll(State state, KeylineStateList stateList) {
824865
(int)
825866
(lastItemDistanceFromFirstItem
826867
- endFocalLocDistanceFromStart
827-
+ endFocalLocDistanceFromEnd);
868+
+ endFocalLocDistanceFromEnd
869+
// If there is padding, adjust for the extra padding offset since offset is
870+
// implicitly added from both endFocalLocDistance calculations.
871+
+ endPadding);
828872

829873
return isRtl ? min(0, endScroll) : max(0, endScroll);
830874
}

lib/java/com/google/android/material/carousel/KeylineState.java

Lines changed: 70 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,17 @@ Keyline getLastNonAnchorKeyline() {
140140
return null;
141141
}
142142

143+
/** Returns how many non-anchor keylines. */
144+
int getNumberOfNonAnchorKeylines() {
145+
int anchorKeylines = 0;
146+
for (Keyline keyline : keylines) {
147+
if (keyline.isAnchor) {
148+
anchorKeylines += 1;
149+
}
150+
}
151+
return keylines.size() - anchorKeylines;
152+
}
153+
143154
/**
144155
* Linearly interpolate between two {@link KeylineState}s.
145156
*
@@ -337,7 +348,9 @@ Builder addKeyline(
337348
float maskedItemSize,
338349
boolean isFocal,
339350
boolean isAnchor,
340-
float cutoff) {
351+
float cutoff,
352+
float leftOrTopPaddingShift,
353+
float rightOrBottomPaddingShift) {
341354
if (maskedItemSize <= 0F) {
342355
return this;
343356
}
@@ -353,7 +366,8 @@ Builder addKeyline(
353366
}
354367

355368
Keyline tmpKeyline =
356-
new Keyline(UNKNOWN_LOC, offsetLoc, mask, maskedItemSize, isAnchor, cutoff);
369+
new Keyline(UNKNOWN_LOC, offsetLoc, mask, maskedItemSize, isAnchor, cutoff,
370+
leftOrTopPaddingShift, rightOrBottomPaddingShift);
357371
if (isFocal) {
358372
if (tmpFirstFocalKeyline == null) {
359373
tmpFirstFocalKeyline = tmpKeyline;
@@ -388,6 +402,44 @@ Builder addKeyline(
388402
return this;
389403
}
390404

405+
/**
406+
* Adds a keyline along the scrolling axis where an object should be masked by the given {@code
407+
* mask} and positioned at {@code offsetLoc}.
408+
*
409+
* <p>Note that calls to {@link #addKeyline(float, float, float, boolean, boolean)} and {@link
410+
* #addKeylineRange(float, float, float, int)} are added in order. Typically, this means
411+
* keylines should be added in order of ascending {@code offsetLoc}. The first and last keylines
412+
* added are 'anchor' keylines that mark the start and ends of the keylines. These keylines do
413+
* not shift when scrolled.
414+
*
415+
* <p>Note also that {@code isFocal} and {@code isAnchor} cannot be true at the same time as
416+
* anchor keylines refer to keylines offscreen that dictate the ends of the keylines.
417+
*
418+
* @param offsetLoc The location of this keyline along the scrolling axis. An offsetLoc of 0
419+
* will be at the start of the scroll container.
420+
* @param mask The percentage of a child's full size that it should be masked by when its center
421+
* is at {@code offsetLoc}. 0 is fully unmasked and 1 is fully masked.
422+
* @param maskedItemSize The total size of this item when masked. This might differ from {@code
423+
* itemSize - (itemSize * mask)} depending on how margins are included in the {@code mask}.
424+
* @param isFocal Whether this keyline is considered part of the focal range. Typically, this is
425+
* when {@code mask} is equal to 0.
426+
* @param isAnchor Whether this keyline is an anchor keyline. Anchor keylines do not shift when
427+
* keylines are shifted.
428+
* @param cutoff How much the keyline item is out the bounds of the available space.
429+
*/
430+
@NonNull
431+
@CanIgnoreReturnValue
432+
Builder addKeyline(
433+
float offsetLoc,
434+
@FloatRange(from = 0.0F, to = 1.0F) float mask,
435+
float maskedItemSize,
436+
boolean isFocal,
437+
boolean isAnchor,
438+
float cutoff) {
439+
return addKeyline(offsetLoc, mask, maskedItemSize, isFocal, isAnchor, cutoff,
440+
0, 0);
441+
}
442+
391443
/**
392444
* Adds a keyline along the scrolling axis where an object should be masked by the given {@code
393445
* mask} and positioned at {@code offsetLoc}. This method also calculates the amount that a
@@ -537,7 +589,9 @@ KeylineState build() {
537589
tmpKeyline.mask,
538590
tmpKeyline.maskedItemSize,
539591
tmpKeyline.isAnchor,
540-
tmpKeyline.cutoff);
592+
tmpKeyline.cutoff,
593+
tmpKeyline.leftOrTopPaddingShift,
594+
tmpKeyline.rightOrBottomPaddingShift);
541595
keylines.add(keyline);
542596
}
543597

@@ -574,6 +628,8 @@ static final class Keyline {
574628
final float maskedItemSize;
575629
final boolean isAnchor;
576630
final float cutoff;
631+
final float leftOrTopPaddingShift;
632+
final float rightOrBottomPaddingShift;
577633

578634
/**
579635
* Creates a non-anchor keyline along a scroll axis.
@@ -587,7 +643,8 @@ static final class Keyline {
587643
* @param maskedItemSize The size of this item when masked.
588644
*/
589645
Keyline(float loc, float locOffset, float mask, float maskedItemSize) {
590-
this(loc, locOffset, mask, maskedItemSize, /* isAnchor= */ false, 0);
646+
this(loc, locOffset, mask, maskedItemSize, /* isAnchor= */ false, 0,
647+
0, 0);
591648
}
592649

593650
/**
@@ -603,20 +660,28 @@ static final class Keyline {
603660
* @param isAnchor Whether or not the keyline is an anchor keyline (keylines at the end that do
604661
* not shift).
605662
* @param cutoff The amount by which the keyline item is cut off by the bounds of the carousel.
663+
* @param leftOrTopPaddingShift The amount by which this keyline was shifted to account for left
664+
* or top padding
665+
* @param rightOrBottomPaddingShift The amount by which this keyline was shifted to account for
666+
* right or bottom padding
606667
*/
607668
Keyline(
608669
float loc,
609670
float locOffset,
610671
float mask,
611672
float maskedItemSize,
612673
boolean isAnchor,
613-
float cutoff) {
674+
float cutoff,
675+
float leftOrTopPaddingShift,
676+
float rightOrBottomPaddingShift) {
614677
this.loc = loc;
615678
this.locOffset = locOffset;
616679
this.mask = mask;
617680
this.maskedItemSize = maskedItemSize;
618681
this.isAnchor = isAnchor;
619682
this.cutoff = cutoff;
683+
this.leftOrTopPaddingShift = leftOrTopPaddingShift;
684+
this.rightOrBottomPaddingShift = rightOrBottomPaddingShift;
620685
}
621686

622687
/** Linearly interpolates between two keylines and returns the interpolated object. */

0 commit comments

Comments
 (0)