Карусель с MotionLayout

Попробуйте Compose
Jetpack Compose – это рекомендуемый набор инструментов для создания пользовательского интерфейса в Android. Узнайте, как добавить карусель в Compose.

Carousel – это вспомогательный объект движения для создания пользовательских представлений карусели, которые показывают список элементов, которые пользователь может просматривать. По сравнению с другими способами реализации таких представлений, этот помощник позволяет быстро создавать сложные изменения движения и размеров для вашего Carousel, используя преимущества MotionLayout.

Виджет Carousel поддерживает списки с началом и концом, а также циклические списки.

Как работает карусель с MotionLayout

Предположим, вы хотите создать горизонтальный вид Carousel с увеличенным центральным элементом:

Этот базовый макет содержит несколько представлений, представляющих элементы Carousel:

Создайте компонент MotionLayout со следующими тремя состояниями и присвойте им идентификаторы:

  • предыдущий
  • start
  • next

Если состояние start соответствует базовому макету, то в состояниях previous и next элементы Carousel сдвигаются на один влево и вправо соответственно.

Например, возьмем пять представлений на рисунке 3 и предположим, что в начальном состоянии представления B, C и D видны, а A и E находятся за пределами экрана. Настройте предыдущее состояние так, чтобы позиции A, B, C и D находились там, где были B, C, D и E, а представления перемещались слева направо. В состоянии next должно произойти обратное: B, C, D и E перемещаются туда, где были A, B, C и D, а представления перемещаются справа налево. Это показано на рисунке 4.

Важно, чтобы новые представления заканчивались точно там, где начинаются исходные. Carousel создает иллюзию бесконечной коллекции элементов, перемещая фактические представления обратно туда, где они были, но повторно инициализируя их с новым соответствующим контентом. Этот механизм показан на схеме ниже. Обратите внимание на значения item #.

Переходы

Определив эти три набора ограничений в файле сцены движения, создайте два перехода – вперед и назад – между состояниями start и next, а также между состояниями start и previous. Добавьте обработчик OnSwipe, чтобы запускать переходы в ответ на жест, как показано в следующем примере:

    <Transition
        motion:constraintSetStart="@id/start"
        motion:constraintSetEnd="@+id/next"
        motion:duration="1000"
        android:id="@+id/forward">
        <OnSwipe
            motion:dragDirection="dragLeft"
            motion:touchAnchorSide="left" />
    </Transition>

    <Transition
        motion:constraintSetStart="@+id/start"
        motion:constraintSetEnd="@+id/previous"
        android:id="@+id/backward">
        <OnSwipe
            motion:dragDirection="dragRight"
            motion:touchAnchorSide="right" />
    </Transition>

После создания этой базовой сцены движения добавьте в макет помощник Carousel и укажите представления в том же порядке, в котором вы реализуете предыдущую и следующую анимацию.

Задайте следующие атрибуты для помощника Carousel:

  • app:carousel_firstView – представление, которое соответствует первому элементу Carousel (в этом примере – C).
  • app:carousel_previousState – идентификатор ConstraintSet предыдущего состояния.
  • app:carousel_nextState – идентификатор ConstraintSet следующего состояния.
  • app:carousel_backwardTransition – идентификатор Transition, примененный между состояниями start и previous.
  • app:carousel_forwardTransition – идентификатор Transition, применяемый между состояниями start и next.

Например, в XML-файле макета есть следующий код:

    <androidx.constraintlayout.motion.widget.MotionLayout ... >

        <ImageView  android:id="@+id/imageView0" .. />
        <ImageView  android:id="@+id/imageView1" .. />
        <ImageView  android:id="@+id/imageView2" .. />
        <ImageView  android:id="@+id/imageView3" .. />
        <ImageView  android:id="@+id/imageView4" .. />

        <androidx.constraintlayout.helper.widget.Carousel
            android:id="@+id/carousel"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:carousel_forwardTransition="@+id/forward"
            app:carousel_backwardTransition="@+id/backward"
            app:carousel_previousState="@+id/previous"
            app:carousel_nextState="@+id/next"
            app:carousel_infinite="true"
            app:carousel_firstView="@+id/imageView2"
            app:constraint_referenced_ids="imageView0,imageView1,imageView2,imageView3,imageView4" />

    </androidx.constraintlayout.motion.widget.MotionLayout>

Настройте адаптер Carousel в коде:

Kotlin

carousel.setAdapter(object : Carousel.Adapter {
            override fun count(): Int {
              // Return the number of items in the Carousel.
            }

            override fun populate(view: View, index: Int) {
                // Implement this to populate the view at the given index.
            }

            override fun onNewItem(index: Int) {
                // Called when an item is set.
            }
        })

Java

carousel.setAdapter(new Carousel.Adapter() {
            @Override
            public int count() {
                // Return the number of items in the Carousel.
            }

            @Override
            public void populate(View view, int index) {
                // Populate the view at the given index.
            }

            @Override
            public void onNewItem(int index) {
                 // Called when an item is set.
            }
        });

Дополнительные сведения

В зависимости от того, какой элемент сейчас выбран в Carousel, представления, соответствующие элементам до или после него, может потребоваться скрыть, чтобы правильно учесть начало и конец Carousel. Вспомогательный класс Carousel обрабатывает это автоматически. По умолчанию в таких ситуациях представления помечаются как View.INVISIBLE, поэтому общий макет не меняется.

В альтернативном режиме помощник Carousel помечает эти представления как View.GONE. Для этого используется следующее свойство:

app:carousel_emptyViewsBehavior="gone"

Примеры

Дополнительные примеры использования помощника по каруселям можно найти в примерах проектов на GitHub.