Skip to content

Commit 8c541e6

Browse files
committed
[Carousel] Prevent scrolling if there's less items than focal keylines
PiperOrigin-RevId: 625466841
1 parent 9393b97 commit 8c541e6

File tree

3 files changed

+34
-2
lines changed

3 files changed

+34
-2
lines changed

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

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -784,6 +784,10 @@ private static KeylineRange getSurroundingKeylineRange(
784784
keylines.get(startMinDistanceIndex), keylines.get(endMinDistanceIndex));
785785
}
786786

787+
private KeylineState getKeylineStartingState(KeylineStateList keylineStateList) {
788+
return isLayoutRtl() ? keylineStateList.getEndState() : keylineStateList.getStartState();
789+
}
790+
787791
/**
788792
* Update the current keyline state by shifting it in response to any change in scroll offset.
789793
*
@@ -794,8 +798,7 @@ private void updateCurrentKeylineStateForScrollOffset(
794798
if (maxScroll <= minScroll) {
795799
// We don't have enough items in the list to scroll and we should use the keyline state
796800
// that aligns items to the start of the container.
797-
this.currentKeylineState =
798-
isLayoutRtl() ? keylineStateList.getEndState() : keylineStateList.getStartState();
801+
this.currentKeylineState = getKeylineStartingState(keylineStateList);
799802
} else {
800803
this.currentKeylineState =
801804
keylineStateList.getShiftedState(scrollOffset, minScroll, maxScroll);
@@ -1454,6 +1457,12 @@ private int scrollBy(int distance, Recycler recycler, State state) {
14541457
recalculateKeylineStateList(recycler);
14551458
}
14561459

1460+
// If the number of items is equal or less than the number of focal items, we should not be able
1461+
// to scroll.
1462+
if (getItemCount() <= getKeylineStartingState(keylineStateList).getTotalVisibleFocalItems()) {
1463+
return 0;
1464+
}
1465+
14571466
// Calculate how much the carousel should scroll and update the scroll offset.
14581467
int scrolledBy = calculateShouldScrollBy(distance, scrollOffset, minScroll, maxScroll);
14591468
scrollOffset += scrolledBy;

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
final class KeylineState {
5353

5454
private final float itemSize;
55+
private int totalVisibleFocalItems;
5556
private final List<Keyline> keylines;
5657
private final int firstFocalKeylineIndex;
5758
private final int lastFocalKeylineIndex;
@@ -65,6 +66,11 @@ private KeylineState(
6566
this.keylines = Collections.unmodifiableList(keylines);
6667
this.firstFocalKeylineIndex = firstFocalKeylineIndex;
6768
this.lastFocalKeylineIndex = lastFocalKeylineIndex;
69+
for (int i = firstFocalKeylineIndex; i <= lastFocalKeylineIndex; i++) {
70+
if (keylines.get(i).cutoff == 0) {
71+
this.totalVisibleFocalItems += 1;
72+
}
73+
}
6874
}
6975

7076
/**
@@ -81,6 +87,11 @@ List<Keyline> getKeylines() {
8187
return keylines;
8288
}
8389

90+
/** Returns the number of focal items in the keyline state. */
91+
int getTotalVisibleFocalItems() {
92+
return totalVisibleFocalItems;
93+
}
94+
8495
/** Returns the first focal keyline in the list. */
8596
Keyline getFirstFocalKeyline() {
8697
return keylines.get(firstFocalKeylineIndex);

lib/javatests/com/google/android/material/carousel/CarouselLayoutManagerTest.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,18 @@ public void testRequestChildRectangleOnScreen_doesntScrollIfChildIsFocal() throw
614614
assertThat(layoutManager.scrollOffset).isEqualTo(0);
615615
}
616616

617+
@Test
618+
public void testSingleItem_shouldNotScrollWithPadding() throws Throwable {
619+
recyclerView.setPadding(50, 0, 50, 0);
620+
recyclerView.setClipToPadding(false);
621+
setAdapterItems(recyclerView, layoutManager, adapter, createDataSetWithSize(1));
622+
int originalLeft = recyclerView.getChildAt(0).getLeft();
623+
624+
scrollHorizontallyBy(recyclerView, layoutManager, 100);
625+
626+
assertThat(recyclerView.getChildAt(0).getLeft()).isEqualTo(originalLeft);
627+
}
628+
617629
/**
618630
* Assigns explicit sizes to fixtures being used to construct the testing environment.
619631
*

0 commit comments

Comments
 (0)