巧用MotionLayout,实现高德地图like的可拉伸BottomSheet
2024-01-01 07:05:16
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更具动感和交互性,从而提升用户体验。