Skip to content

Commit d5d604d

Browse files
imhappidsn5ft
authored andcommittedOct 2, 2023
[Carousel] Disallowing center aligned hero strategy with only 2 items since it does not make any sense. With only 2 items there can only be a start state and end state with the hero strategy.
Resolves #3589 PiperOrigin-RevId: 568965460
1 parent 8cb444b commit d5d604d

File tree

6 files changed

+137
-3
lines changed

6 files changed

+137
-3
lines changed
 

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

+3
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,7 @@ interface Carousel {
3232

3333
/** Gets the alignment of the carousel. */
3434
@Alignment int getCarouselAlignment();
35+
36+
/** Gets the number of items in the carousel. */
37+
int getItemCount();
3538
}

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

+25
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@ public class CarouselLayoutManager extends LayoutManager
132132
/** Aligns large items to the center of the carousel. */
133133
public static final int ALIGNMENT_CENTER = 1;
134134

135+
private int lastItemCount;
136+
135137
/**
136138
* An estimation of the current focused position, determined by which item center is closest to
137139
* the first focal keyline. This is used when restoring item position after the carousel keylines
@@ -1419,6 +1421,29 @@ public void setOrientation(@RecyclerView.Orientation int orientation) {
14191421
}
14201422
}
14211423

1424+
@Override
1425+
public void onItemsAdded(@NonNull RecyclerView recyclerView, int positionStart, int itemCount) {
1426+
super.onItemsAdded(recyclerView, positionStart, itemCount);
1427+
updateItemCount();
1428+
}
1429+
1430+
@Override
1431+
public void onItemsRemoved(@NonNull RecyclerView recyclerView, int positionStart, int itemCount) {
1432+
super.onItemsRemoved(recyclerView, positionStart, itemCount);
1433+
updateItemCount();
1434+
}
1435+
1436+
private void updateItemCount() {
1437+
int newItemCount = getItemCount();
1438+
if (newItemCount == this.lastItemCount || keylineStateList == null) {
1439+
return;
1440+
}
1441+
if (carouselStrategy.shouldRefreshKeylineState(this, this.lastItemCount)) {
1442+
refreshKeylineState();
1443+
}
1444+
this.lastItemCount = newItemCount;
1445+
}
1446+
14221447
/**
14231448
* Enables features to help debug keylines and other internal layout manager logic.
14241449
*

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

+12
Original file line numberDiff line numberDiff line change
@@ -127,4 +127,16 @@ static int[] doubleCounts(int[] count) {
127127
boolean isContained() {
128128
return true;
129129
}
130+
131+
/**
132+
* Whether or not the strategy keylines should be refreshed based on the old item count and the
133+
* carousel's current parameters.
134+
*
135+
* @return true if the keylines should be refreshed.
136+
*/
137+
boolean shouldRefreshKeylineState(Carousel carousel, int oldItemCount) {
138+
// TODO: b/301332183 - Update existing strategies with logic on when to refresh keyline
139+
// state based on item count.
140+
return false;
141+
}
130142
}

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

+13-2
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ public class HeroCarouselStrategy extends CarouselStrategy {
4949

5050
private static final int[] SMALL_COUNTS = new int[] {1};
5151
private static final int[] MEDIUM_COUNTS = new int[] {0, 1};
52+
private static final int MIN_ITEMS_FOR_CENTER_ALIGNMENT = 2;
5253

5354
@Override
5455
@NonNull
@@ -97,7 +98,8 @@ KeylineState onFirstChildMeasuredWithMargins(@NonNull Carousel carousel, @NonNul
9798
largeCounts[i] = largeCountMin + i;
9899
}
99100
boolean isCenterAligned =
100-
carousel.getCarouselAlignment() == CarouselLayoutManager.ALIGNMENT_CENTER;
101+
carousel.getCarouselAlignment() == CarouselLayoutManager.ALIGNMENT_CENTER
102+
&& carousel.getItemCount() > MIN_ITEMS_FOR_CENTER_ALIGNMENT;
101103
Arrangement arrangement =
102104
Arrangement.findLowestCostArrangement(
103105
availableSpace,
@@ -118,7 +120,16 @@ KeylineState onFirstChildMeasuredWithMargins(@NonNull Carousel carousel, @NonNul
118120
childMargins,
119121
availableSpace,
120122
arrangement,
121-
carousel.getCarouselAlignment());
123+
isCenterAligned
124+
? CarouselLayoutManager.ALIGNMENT_CENTER
125+
: CarouselLayoutManager.ALIGNMENT_START);
122126
}
127+
128+
@Override
129+
boolean shouldRefreshKeylineState(@NonNull Carousel carousel, int oldItemCount) {
130+
return carousel.getCarouselAlignment() == CarouselLayoutManager.ALIGNMENT_CENTER
131+
&& (oldItemCount == MIN_ITEMS_FOR_CENTER_ALIGNMENT
132+
|| carousel.getItemCount() == MIN_ITEMS_FOR_CENTER_ALIGNMENT);
133+
}
123134
}
124135

‎lib/javatests/com/google/android/material/carousel/CarouselHelper.java

+51
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
/** A helper class to facilitate Carousel tests */
4040
class CarouselHelper {
4141

42+
private static final int DEFAULT_ITEM_COUNT = 10;
43+
4244
private CarouselHelper() {}
4345

4446

@@ -193,6 +195,11 @@ public boolean isHorizontal() {
193195
public int getCarouselAlignment() {
194196
return CarouselLayoutManager.ALIGNMENT_START;
195197
}
198+
199+
@Override
200+
public int getItemCount() {
201+
return DEFAULT_ITEM_COUNT;
202+
}
196203
};
197204
}
198205

@@ -217,6 +224,11 @@ public boolean isHorizontal() {
217224
public int getCarouselAlignment() {
218225
return CarouselLayoutManager.ALIGNMENT_CENTER;
219226
}
227+
228+
@Override
229+
public int getItemCount() {
230+
return DEFAULT_ITEM_COUNT;
231+
}
220232
};
221233
}
222234

@@ -246,6 +258,45 @@ public boolean isHorizontal() {
246258
public int getCarouselAlignment() {
247259
return alignment;
248260
}
261+
262+
@Override
263+
public int getItemCount() {
264+
return DEFAULT_ITEM_COUNT;
265+
}
266+
};
267+
}
268+
269+
/**
270+
* Creates a {@link Carousel} with a specified {@code size} for both width and height and the
271+
* specified item count and alignment.
272+
*/
273+
static Carousel createCarouselWithItemCount(int size, int alignment, int itemCount) {
274+
return new Carousel() {
275+
276+
@Override
277+
public int getContainerWidth() {
278+
return size;
279+
}
280+
281+
@Override
282+
public int getContainerHeight() {
283+
return size;
284+
}
285+
286+
@Override
287+
public boolean isHorizontal() {
288+
return true;
289+
}
290+
291+
@Override
292+
public int getCarouselAlignment() {
293+
return alignment;
294+
}
295+
296+
@Override
297+
public int getItemCount() {
298+
return itemCount;
299+
}
249300
};
250301
}
251302

‎lib/javatests/com/google/android/material/carousel/HeroCarouselStrategyTest.java

+33-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import com.google.android.material.test.R;
1919

2020
import static com.google.android.material.carousel.CarouselHelper.createCarousel;
21+
import static com.google.android.material.carousel.CarouselHelper.createCarouselWithItemCount;
2122
import static com.google.android.material.carousel.CarouselHelper.createCarouselWithWidth;
2223
import static com.google.android.material.carousel.CarouselHelper.createViewWithSize;
2324
import static com.google.common.truth.Truth.assertThat;
@@ -166,7 +167,7 @@ public void testKnownCenterAlignmentArrangement_correctlyCalculatesKeylineLocati
166167
ApplicationProvider.getApplicationContext(), (int) largeSize, (int) largeSize);
167168
int carouselSize = (int) (largeSize + smallSize * 2);
168169

169-
MultiBrowseCarouselStrategy strategy = new MultiBrowseCarouselStrategy();
170+
HeroCarouselStrategy strategy = new HeroCarouselStrategy();
170171
List<Keyline> keylines =
171172
strategy.onFirstChildMeasuredWithMargins(
172173
createCarousel(
@@ -180,4 +181,35 @@ public void testKnownCenterAlignmentArrangement_correctlyCalculatesKeylineLocati
180181
assertThat(keylines.get(i).locOffset).isEqualTo(locOffsets[i]);
181182
}
182183
}
184+
185+
@Test
186+
public void testCenterAlignment_isLeftAlignedWithMinItems() {
187+
float largeSize = 40F * 3; // 120F
188+
float smallSize = 40F;
189+
190+
View view =
191+
createViewWithSize(
192+
ApplicationProvider.getApplicationContext(), (int) largeSize, (int) largeSize);
193+
int carouselSize = (int) (largeSize + smallSize * 2);
194+
195+
HeroCarouselStrategy strategy = new HeroCarouselStrategy();
196+
List<Keyline> keylines =
197+
strategy
198+
.onFirstChildMeasuredWithMargins(
199+
createCarouselWithItemCount(
200+
carouselSize, CarouselLayoutManager.ALIGNMENT_CENTER, 2),
201+
view)
202+
.getKeylines();
203+
204+
float minSmallItemSize =
205+
view.getResources().getDimension(R.dimen.m3_carousel_small_item_size_min);
206+
207+
// keylines when there are only 2 items is {xsmall, large, small, xsmall}
208+
float[] locOffsets =
209+
new float[] {-.5F, (200 - minSmallItemSize) / 2F, 200 - minSmallItemSize / 2F, 200.5F};
210+
211+
for (int i = 0; i < keylines.size(); i++) {
212+
assertThat(keylines.get(i).locOffset).isEqualTo(locOffsets[i]);
213+
}
214+
}
183215
}

0 commit comments

Comments
 (0)
Please sign in to comment.