使用 MotionLayout 的轮播界面

尝试使用 Compose 方式
Jetpack Compose 是推荐用于 Android 的界面工具包。了解如何在 Compose 中添加轮播界面。

Carousel 是一个动作帮助程序对象,用于构建自定义轮播视图,该视图会显示用户可以快速浏览的元素列表。与其他实现此类视图的方式相比,此辅助函数可让您利用 MotionLayout 快速为 Carousel 创建复杂的运动和尺寸变化。

Carousel widget 支持具有开头和结尾的列表,以及循环环绕列表。

使用 MotionLayout 的轮播界面的运作方式

假设您要构建一个水平 Carousel 视图,并放大中心项:

此基本布局包含多个表示 Carousel 项的视图:

创建一个具有以下三种状态的 MotionLayout,并为其指定 ID:

  • 上一个
  • start
  • 下一步

如果 start 状态对应于基本布局,那么在 previous 状态和 next 状态下,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 通过将实际视图移回原来的位置,但使用新的匹配内容重新初始化它们,从而营造出无限元素集合的错觉。下图展示了此机制。请注意“商品编号”值):

转场效果

在运动场景文件中定义这三个约束集后,在 startnext 状态之间以及 startprevious 状态之间创建两个过渡效果(向前和向后)。添加 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 ID。
  • app:carousel_nextState下一个状态的 ConstraintSet ID。
  • app:carousel_backwardTransition:在开始状态和上一个状态之间应用的 Transition ID。
  • app:carousel_forwardTransition:在开始状态和下一个状态之间应用的 Transition ID。

例如,您的布局 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"

示例

如需查看更多使用 Carousel 辅助函数的示例,请参阅 GitHub 上的示例项目