返回

巧用MotionLayout,实现高德地图like的可拉伸BottomSheet

Android

MotionLayout简介

MotionLayout是Android中一个强大的布局组件,它允许开发者在布局文件中定义动画和过渡效果。MotionLayout通过使用一组约束条件来定义布局元素的位置和大小,并通过一组过渡来定义元素如何从一个状态过渡到另一个状态。

实现可拉伸BottomSheet

要实现可拉伸BottomSheet效果,我们需要使用MotionLayout来定义布局和动画。首先,我们需要创建一个MotionLayout布局文件,并在其中定义BottomSheet的布局元素。然后,我们需要定义一组约束条件来定义BottomSheet的位置和大小,以及一组过渡来定义BottomSheet如何从收缩状态过渡到展开状态。

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.motion.widget.MotionLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/motionLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/bottomSheet"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent">

        <!-- BottomSheet的内容 -->

    </androidx.constraintlayout.widget.ConstraintLayout>

    <Transition
        android:id="@+id/transition"
        app:constraintSetEnd="@+id/end"
        app:constraintSetStart="@+id/start"
        app:duration="1000">

        <OnSwipe
            android:id="@+id/onSwipe"
            app:dragDirection="dragUp"
            app:touchAnchorId="@+id/bottomSheet" />

        <KeyFrameSet>
            <KeyAttribute
                android:id="@+id/keyFrame1"
                app:framePosition="0"
                app:motionTarget="@+id/bottomSheet"
                app:percentHeight="0.5" />

            <KeyAttribute
                android:id="@+id/keyFrame2"
                app:framePosition="1"
                app:motionTarget="@+id/bottomSheet"
                app:percentHeight="1" />
        </KeyFrameSet>

        <KeyCycle
            android:id="@+id/keyCycle1"
            app:framePosition="0"
            app:motionTarget="@+id/bottomSheet"
            app:percentBackgroundColor="0"
            app:percentX="0.5"
            app:percentY="0.5" />

        <KeyCycle
            android:id="@+id/keyCycle2"
            app:framePosition="1"
            app:motionTarget="@+id/bottomSheet"
            app:percentBackgroundColor="1"
            app:percentX="0.5"
            app:percentY="0.5" />

    </Transition>

    <ConstraintSet android:id="@+id/start">
        <Constraint
            android:id="@+id/constraint1"
            android:layout_height="0dp"
            android:layout_width="0dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    </ConstraintSet>

    <ConstraintSet android:id="@+id/end">
        <Constraint
            android:id="@+id/constraint2"
            android:layout_height="0dp"
            android:layout_width="0dp"
            app:layout_constraintBottom_toTopOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    </ConstraintSet>

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

在以上代码中,我们首先定义了MotionLayout布局文件,并在其中包含了一个ConstraintLayout布局,用于放置BottomSheet的内容。然后,我们定义了一组约束条件和过渡来定义BottomSheet如何从收缩状态过渡到展开状态。

在约束条件中,我们首先定义了一个名为“start”的约束条件,该约束条件将BottomSheet固定在父布局的底部,并使其高度为0dp。然后,我们定义了一个名为“end”的约束条件,该约束条件将BottomSheet固定在父布局的顶部,并使其高度等于父布局的高度。

在过渡中,我们首先定义了一个名为“onSwipe”的OnSwipe事件,该事件将在BottomSheet被向上滑动时触发。然后,我们定义了一个名为“transition”的过渡,该过渡将从“start”约束条件过渡到“end”约束条件,并在此过程中执行动画。

在动画中,我们首先定义了一个名为“keyFrame1”的关键帧,该关键帧将BottomSheet的高度设置为父布局高度的50%。然后,我们定义了一个名为“keyFrame2”的关键帧,该关键帧将BottomSheet的高度设置为父布局的高度。

最后,我们定义了两个名为“keyCycle1”和“keyCycle2”的关键循环,这两个关键循环将BottomSheet的背景色从透明过渡到纯白。

使用MotionLayout实现可拉伸BottomSheet

要使用MotionLayout实现可拉伸BottomSheet,我们需要在布局文件中引用MotionLayout布局文件,并将其设置为根布局。然后,我们需要在代码中监听BottomSheet的滑动事件,并根据滑动事件来控制动画的播放。

public class MainActivity extends AppCompatActivity {

    private MotionLayout motionLayout;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        motionLayout = findViewById(R.id.motionLayout);

        // 监听BottomSheet的滑动事件
        motionLayout.addTransitionListener(new MotionLayout.TransitionListener() {
            @Override
            public void onTransitionStarted(MotionLayout motionLayout, int startId, int endId) {

            }

            @Override
            public void onTransitionChange(MotionLayout motionLayout, int startId, int endId, float progress) {
                // 根据滑动进度控制动画的播放
                if (progress > 0.5) {
                    motionLayout.setTransition(R.id.transition2);
                    motionLayout.transitionToEnd();
                } else {
                    motionLayout.setTransition(R.id.transition1);
                    motionLayout.transitionToStart();
                }
            }

            @Override
            public void onTransitionCompleted(MotionLayout motionLayout, int currentId) {

            }

            @Override
            public void onTransitionTrigger(MotionLayout motionLayout, int triggerId, boolean positive, float progress) {

            }
        });
    }
}

在以上代码中,我们首先获取了MotionLayout实例。然后,我们监听了BottomSheet的滑动事件,并根据滑动事件来控制动画的播放。

当BottomSheet被向上滑动时,我们将动画的当前状态设置为“transition2”,并调用transitionToEnd()方法来播放动画。当BottomSheet被向下滑动时,我们将动画的当前状态设置为“transition1”,并调用transitionToStart()方法来播放动画。

结语

通过使用MotionLayout,我们可以轻松实现高德地图like的可拉伸BottomSheet效果。这种效果可以使BottomSheet更具动感和交互性,从而提升用户体验。