You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android中使用MotionLayout实现指定动画遇到问题求助

MotionLayout实现底部上滑联动动画方案

目标动画说明

底部内容区域向上滑动时,顶部导航栏从紧凑状态展开为高栏,同时显示展开式标题,底部内容同步上移至导航栏下方,整体过渡平滑连贯。

核心实现思路

通过MotionLayout的ConstraintSet定义初始(收缩)和目标(展开)两个关键帧状态,配合MotionScene配置过渡动画规则,绑定滚动事件触发动画,实现视图间的联动效果。

具体代码实现

1. 主布局文件(activity_main.xml)

<?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"
    android:id="@+id/motion_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layoutDescription="@xml/motion_scene">

    <!-- 顶部导航栏 -->
    <androidx.appcompat.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="@color/white"
        app:title="收缩状态">

        <TextView
            android:id="@+id/expanded_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="展开状态标题"
            android:textSize="24sp"
            android:visibility="gone"/>
    </androidx.appcompat.widget.Toolbar>

    <!-- 底部可滚动内容区域 -->
    <androidx.core.widget.NestedScrollView
        android:id="@+id/content_scroll"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintBottom_toBottomOf="parent">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:padding="16dp">

            <!-- 填充足够多的内容使视图可滚动 -->
            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="内容项 1"
                android:textSize="18sp"
                android:paddingVertical="12dp"/>
            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="内容项 2"
                android:textSize="18sp"
                android:paddingVertical="12dp"/>
            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="内容项 3"
                android:textSize="18sp"
                android:paddingVertical="12dp"/>
            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="内容项 4"
                android:textSize="18sp"
                android:paddingVertical="12dp"/>
            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="内容项 5"
                android:textSize="18sp"
                android:paddingVertical="12dp"/>
            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="内容项 6"
                android:textSize="18sp"
                android:paddingVertical="12dp"/>
            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="内容项 7"
                android:textSize="18sp"
                android:paddingVertical="12dp"/>
            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="内容项 8"
                android:textSize="18sp"
                android:paddingVertical="12dp"/>
        </LinearLayout>
    </androidx.core.widget.NestedScrollView>

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

2. MotionScene配置文件(res/xml/motion_scene.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">

    <!-- 初始收缩状态 -->
    <ConstraintSet android:id="@+id/start">
        <Constraint
            android:id="@id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            app:layout_constraintTop_toTopOf="parent"/>

        <Constraint
            android:id="@id/expanded_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintMarginStart="16dp">
            <PropertySet
                android:alpha="0"
                android:visibility="invisible"/>
        </Constraint>

        <Constraint
            android:id="@id/content_scroll"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_constraintBottom_toBottomOf="parent">
            <CustomAttribute
                app:attributeName="translationY"
                app:customFloatValue="200dp"/> <!-- 初始偏移,模拟底部悬浮状态 -->
        </Constraint>
    </ConstraintSet>

    <!-- 目标展开状态 -->
    <ConstraintSet android:id="@+id/end">
        <Constraint
            android:id="@id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="120dp"
            app:layout_constraintTop_toTopOf="parent"/>

        <Constraint
            android:id="@id/expanded_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintMarginStart="16dp"
            app:layout_constraintMarginTop="40dp">
            <PropertySet
                android:alpha="1"
                android:visibility="visible"/>
        </Constraint>

        <Constraint
            android:id="@id/content_scroll"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_constraintTop_toBottomOf="@id/toolbar"
            app:layout_constraintBottom_toBottomOf="parent">
            <CustomAttribute
                app:attributeName="translationY"
                app:customFloatValue="0dp"/>
        </Constraint>
    </ConstraintSet>

    <!-- 过渡动画规则 -->
    <Transition
        app:constraintSetStart="@id/start"
        app:constraintSetEnd="@id/end"
        app:duration="500"
        app:motionInterpolator="linear">

        <!-- 绑定滚动触发动画 -->
        <OnSwipe
            app:touchAnchorId="@id/content_scroll"
            app:touchAnchorSide="top"
            app:dragDirection="dragUp"/>

        <!-- 标题显隐的渐变动画 -->
        <KeyFrameSet>
            <KeyAttribute
                android:alpha="0"
                app:motionTarget="@id/expanded_title"
                app:framePosition="0"/>
            <KeyAttribute
                android:alpha="1"
                app:motionTarget="@id/expanded_title"
                app:framePosition="100"/>
        </KeyFrameSet>
    </Transition>
</MotionScene>

常见问题排查

  • 动画卡顿:确保NestedScrollView内容不要过于复杂,或在对应Activity的Manifest配置中添加android:hardwareAccelerated="true"开启硬件加速。
  • 滚动联动失效:检查MotionScene中OnSwipe的touchAnchorId是否指向可滚动视图,dragDirection是否匹配滑动方向,同时确认MotionLayout的layoutDescription路径正确。
  • 视图显隐异常:在PropertySet中同时配置alpha和visibility属性,避免单一属性导致的视觉闪烁。

内容的提问来源于stack exchange,提问作者Abhay

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 18:15:42