@@ -315,7 +315,29 @@ private static float[] getStateStepInterpolationPoints(
315315 private static boolean isFirstFocalItemAtLeftOfContainer (KeylineState state ) {
316316 float firstFocalItemLeft =
317317 state .getFirstFocalKeyline ().locOffset - (state .getFirstFocalKeyline ().maskedItemSize / 2F );
318- return firstFocalItemLeft <= 0F || state .getFirstFocalKeyline () == state .getFirstKeyline ();
318+ return firstFocalItemLeft >= 0F
319+ && state .getFirstFocalKeyline () == state .getFirstNonAnchorKeyline ();
320+ }
321+
322+ /**
323+ * Determines whether or not the first focal item for the given {@code state} is at the right of
324+ * the carousel container and fully visible.
325+ *
326+ * @param carousel the {@link Carousel} associated with this {@link KeylineStateList}.
327+ * @param state the state to check for right item position
328+ * @return true if the {@code state}'s first focal item has its right aligned with the right of
329+ * the {@code carousel} container and is fully visible.
330+ */
331+ private static boolean isLastFocalItemVisibleAtRightOfContainer (
332+ Carousel carousel , KeylineState state ) {
333+ int containerSize = carousel .getContainerHeight ();
334+ if (carousel .isHorizontal ()) {
335+ containerSize = carousel .getContainerWidth ();
336+ }
337+ float lastFocalItemRight =
338+ state .getLastFocalKeyline ().locOffset + (state .getLastFocalKeyline ().maskedItemSize / 2F );
339+ return lastFocalItemRight <= containerSize
340+ && state .getLastFocalKeyline () == state .getLastNonAnchorKeyline ();
319341 }
320342
321343 /**
@@ -339,6 +361,7 @@ private static List<KeylineState> getStateStepsStart(
339361 List <KeylineState > steps = new ArrayList <>();
340362 steps .add (defaultState );
341363 int firstNonAnchorKeylineIndex = findFirstNonAnchorKeylineIndex (defaultState );
364+
342365 // If the first focal item is already at the left of the container or there are no in bounds
343366 // keylines, return a list of steps that only includes the default state (there is nowhere to
344367 // shift).
@@ -348,15 +371,26 @@ private static List<KeylineState> getStateStepsStart(
348371 }
349372
350373 int start = firstNonAnchorKeylineIndex ;
351- int end = defaultState .getFirstFocalKeylineIndex () - 1 ;
374+ int end = defaultState .getFirstFocalKeylineIndex ();
352375 int numberOfSteps = end - start ;
353- float cutoffs = 0 ;
354-
376+ float carouselSize =
377+ carousel . isHorizontal () ? carousel . getContainerWidth () : carousel . getContainerHeight ();
355378 float originalStart =
356379 defaultState .getFirstKeyline ().locOffset
357380 - (defaultState .getFirstKeyline ().maskedItemSize / 2F );
358381
359- for (int i = 0 ; i <= numberOfSteps ; i ++) {
382+ if (numberOfSteps <= 0 && defaultState .getFirstFocalKeyline ().cutoff > 0 ) {
383+ // If there are no steps, there still might be a cutoff focal item that we should shift into
384+ // view. Add a step that shifts all the keylines over to bring the first focal item into full
385+ // view.
386+ float cutoffs = defaultState .getFirstFocalKeyline ().cutoff ;
387+ steps .add (
388+ shiftKeylinesAndCreateKeylineState (defaultState , originalStart + cutoffs , carouselSize ));
389+ return steps ;
390+ }
391+
392+ float cutoffs = 0 ;
393+ for (int i = 0 ; i < numberOfSteps ; i ++) {
360394 KeylineState prevStepState = steps .get (steps .size () - 1 );
361395 int itemOrigIndex = start + i ;
362396 // If this is the first item from the original state, place it at the end of the dest state.
@@ -382,35 +416,12 @@ private static List<KeylineState> getStateStepsStart(
382416 originalStart + cutoffs ,
383417 newFirstFocalIndex ,
384418 newLastFocalIndex ,
385- carousel .isHorizontal ()
386- ? carousel .getContainerWidth ()
387- : carousel .getContainerHeight ());
419+ carouselSize );
388420 steps .add (shifted );
389421 }
390422 return steps ;
391423 }
392424
393- /**
394- * Determines whether or not the first focal item for the given {@code state} is at the right of
395- * the carousel container and fully visible.
396- *
397- * @param carousel the {@link Carousel} associated with this {@link KeylineStateList}.
398- * @param state the state to check for right item position
399- * @return true if the {@code state}'s first focal item has its right aligned with the right of
400- * the {@code carousel} container and is fully visible.
401- */
402- private static boolean isLastFocalItemVisibleAtRightOfContainer (
403- Carousel carousel , KeylineState state ) {
404- int containerSize = carousel .getContainerHeight ();
405- if (carousel .isHorizontal ()) {
406- containerSize = carousel .getContainerWidth ();
407- }
408- float lastFocalItemRight =
409- state .getLastFocalKeyline ().locOffset + (state .getLastFocalKeyline ().maskedItemSize / 2F );
410- return lastFocalItemRight <= containerSize
411- && state .getLastFocalKeyline () == state .getLastNonAnchorKeyline ();
412- }
413-
414425 /**
415426 * Generates discreet steps which move the focal range from it's original position until it
416427 * reaches the right of the carousel container.
@@ -441,35 +452,26 @@ private static List<KeylineState> getStateStepsEnd(
441452 return steps ;
442453 }
443454
455+ int start = defaultState .getLastFocalKeylineIndex ();
456+ int end = lastNonAnchorKeylineIndex ;
457+ int numberOfSteps = end - start ;
444458 float carouselSize =
445459 carousel .isHorizontal () ? carousel .getContainerWidth () : carousel .getContainerHeight ();
446-
447460 float originalStart =
448461 defaultState .getFirstKeyline ().locOffset
449462 - (defaultState .getFirstKeyline ().maskedItemSize / 2F );
450463
451- // If we are here, it means that the last keyline is focal, but is cut off
452- // since it is not visible. If this is the case, we want to add one step where
453- // the keylines are shifted so that the last keyline is visible.
454- if (defaultState .getLastFocalKeyline () == defaultState .getLastNonAnchorKeyline ()) {
455- KeylineState shifted =
456- moveKeylineAndCreateKeylineState (
457- defaultState ,
458- /* keylineSrcIndex= */ defaultState .getLastFocalKeylineIndex (),
459- /* keylineDstIndex= */ defaultState .getFirstFocalKeylineIndex (),
460- originalStart - defaultState .getLastFocalKeyline ().cutoff ,
461- defaultState .getFirstFocalKeylineIndex (),
462- defaultState .getLastFocalKeylineIndex (),
463- carouselSize );
464- steps .add (shifted );
464+ if (numberOfSteps <= 0 && defaultState .getLastFocalKeyline ().cutoff > 0 ) {
465+ // If there are no steps, there still might be a cutoff focal item that we should shift into
466+ // view. Add a step that shifts all the keylines over to bring the last focal item into full
467+ // view.
468+ float cutoffs = defaultState .getLastFocalKeyline ().cutoff ;
469+ steps .add (
470+ shiftKeylinesAndCreateKeylineState (defaultState , originalStart - cutoffs , carouselSize ));
465471 return steps ;
466472 }
467473
468- int start = defaultState .getLastFocalKeylineIndex ();
469- int end = lastNonAnchorKeylineIndex ;
470- int numberOfSteps = end - start ;
471474 float cutoffs = 0 ;
472-
473475 for (int i = 0 ; i < numberOfSteps ; i ++) {
474476 KeylineState prevStepState = steps .get (steps .size () - 1 );
475477 int itemOrigIndex = end - i ;
@@ -503,6 +505,28 @@ private static List<KeylineState> getStateStepsEnd(
503505 return steps ;
504506 }
505507
508+ /**
509+ * Creates a new, valid KeylineState that has the same order as {@code state} but with all
510+ * keylines shifted along the scrolling axis.
511+ *
512+ * @param state the state to shift
513+ * @param startOffset the point along the scrolling axis where keylines should start being added
514+ * from
515+ * @param carouselSize the size of the carousel container
516+ * @return a new {@link KeylineState} with the shifted keylines
517+ */
518+ private static KeylineState shiftKeylinesAndCreateKeylineState (
519+ KeylineState state , float startOffset , float carouselSize ) {
520+ return moveKeylineAndCreateKeylineState (
521+ state ,
522+ 0 ,
523+ 0 ,
524+ startOffset ,
525+ state .getFirstFocalKeylineIndex (),
526+ state .getLastFocalKeylineIndex (),
527+ carouselSize );
528+ }
529+
506530 /**
507531 * Creates a new, valid KeylineState from a list of keylines that have been re-arranged.
508532 *
0 commit comments