底部 sheet + Android MotionLayout 实现

Bottom sheet + Android MotionLayout implementation

我正在尝试使用 MotionLayout 实现底部 sheet。它适用于一个微不足道的情况 - 当 bottom sheet 应该只在屏幕的一半可见时(例如)。但是我无法在底部 sheet 扩展并填满整个屏幕的情况下工作。

所以这里可以有 3 个状态:

  1. 底部sheet隐藏
  2. 底部sheet 占半屏
  3. 底部sheet 填满整个屏幕

布局如下:

<androidx.constraintlayout.motion.widget.MotionLayout
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       app:layoutDescription="@xml/scene_description">

         <FrameLayout
            android:id="@+id/fragmentPlaceholder"
            android:layout_width="match_parent"
            android:layout_height="570dp"
            android:elevation="8dp"> some content here </FrameLayout>


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

这里570dp等于半屏(例如)

以及scene_description.xml的内容:

<?xml version="1.0" encoding="utf-8"?>
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <Transition
        android:id="@+id/transition1"
        app:constraintSetStart="@id/bottomSheetHidden"
        app:constraintSetEnd="@id/bottomSheetHalfOpen"
        app:duration="300">

        <OnSwipe
            app:dragDirection="dragDown"
            app:onTouchUp="autoCompleteToStart"
            app:touchAnchorId="@+id/fragmentPlaceholder"
            app:touchAnchorSide="bottom"/>

    </Transition>


    <Transition
        android:id="@+id/transition2"
        app:constraintSetStart="@id/bottomSheetHalfOpen"
        app:constraintSetEnd="@id/bottomSheetOpenFullScreen"
        app:duration="300">

    </Transition>


    <Transition
        android:id="@+id/transition3"
        app:constraintSetStart="@id/bottomSheetHidden"
        app:constraintSetEnd="@id/bottomSheetOpenFullScreen"
        app:duration="300">

        <OnSwipe
            app:dragDirection="dragDown"
            app:touchAnchorId="@+id/fragmentPlaceholder"
            app:touchAnchorSide="bottom" />

    </Transition>


    <ConstraintSet android:id="@+id/bottomSheetHidden">

        <Constraint android:id="@id/fragmentPlaceholder">
            <Layout
                android:layout_width="match_parent"
                android:layout_height="570dp"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintTop_toBottomOf="parent"/> <!-- below screen, not visible-->
        </Constraint>

    </ConstraintSet>


    <ConstraintSet android:id="@+id/bottomSheetHalfOpen"
        app:deriveConstraintsFrom="@id/bottomSheetHidden">

        <Constraint android:id="@id/fragmentPlaceholder">
            <Layout
                android:layout_width="match_parent"
                android:layout_height="570dp"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintBottom_toBottomOf="parent" />
        </Constraint>
    </ConstraintSet>


    <ConstraintSet android:id="@+id/bottomSheetOpenFullScreen"
        app:deriveConstraintsFrom="@id/bottomSheetHidden">

        <Constraint android:id="@id/fragmentPlaceholder">
            <Layout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_marginTop="30dp"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintBottom_toBottomOf="parent" />
        </Constraint>

    </ConstraintSet>

</MotionScene>


问题是 OnSwipe 在底部 sheet 处于全屏状态时不起作用,但在底部 sheet 处于半屏状态时起作用。 我想要使用滑动移动隐藏底部 sheet 的机会。

这个问题怎么解决的?是否应该添加或修改过渡?

您的转换只需要稍微调整一下,约束很好:

  • 您不需要第三次转换。如果您在同一个方向的不同状态之间滑动 - 或者实际上即使您改变方向,MotionLayout 会负责干净地移动通过多个 onSwipe 转换。
  • 您的第二个和第三个 ConstraintSets 实际上并没有改变您正在移动的片段的底部边缘的位置,但是您指定场景应该根据相对于滑动手势的边缘来计算进度。我建议仔细阅读 MotionLayout codelab and specifically Lesson 9,它很好地说明了这个概念。因此,我将 touchAnchorSide 更改为 top
  • durationOnSwipe 转换没有任何意义,可以删除它
  • 无论您使用 dragUp 还是 dragDown,它都以相同的基于垂直手势的动画结束。我认为 dragUp 在这种情况下更容易理解。

修改后的转换:

    <Transition
        android:id="@+id/transition1"
        app:constraintSetStart="@id/bottomSheetHidden"
        app:constraintSetEnd="@id/bottomSheetHalfOpen">

        <OnSwipe
            app:dragDirection="dragUp"
            app:onTouchUp="autoCompleteToStart"
            app:touchAnchorId="@+id/fragmentPlaceholder"
            app:touchAnchorSide="top"/>

    </Transition>

    <Transition
        android:id="@+id/transition2"
        app:constraintSetStart="@id/bottomSheetHalfOpen"
        app:constraintSetEnd="@id/bottomSheetOpenFullScreen">

        <OnSwipe
            app:dragDirection="dragUp"
            app:touchAnchorId="@+id/fragmentPlaceholder"
            app:touchAnchorSide="top" />

    </Transition>