Карусель с MotionLayout

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

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

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

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

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

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

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

  • предыдущий
  • начинать
  • следующий

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

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

Крайне важно, чтобы элементы в итоге оказались точно там, где начинались исходные элементы. Carousel создает иллюзию бесконечной коллекции элементов, перемещая исходные элементы обратно на прежнее место, но инициализируя их новым соответствующим содержимым. Следующая диаграмма демонстрирует этот механизм. Обратите внимание на значения "номер элемента":

Переходы

Определив эти три набора ограничений в файле сцены движения, создайте два перехода — вперед и назад — между начальным и следующим состояниями, а также между начальным и предыдущим состояниями. Добавьте обработчик 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 , применяемого между начальным и предыдущим состояниями.
  • app:carousel_forwardTransition : идентификатор Transition , применяемого между начальным и следующим состояниями.

Например, в вашем 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 в коде:

Котлин

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"

Примеры

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