Motion Layout为不同视图设置独立moveWhenScrollAtTop属性实现滚动联动问题
MotionLayout 双区域滚动效果实现方案
参考布局效果:
你当前代码不生效的核心原因是 MotionLayout 默认同一时间仅会激活一个Transition,你定义的两个独立Transition不会自动按顺序串联触发,无法实现先收起Header、再操作Filters的联动效果。
修改思路
- 合并为单Transition,通过关键帧拆分两个组件的动画触发区间:
- 0% ~ 50% 进度段:仅执行Header的收起/展开动画,Filters保持原位
- 50% ~ 100% 进度段:Header保持收起状态,执行Filters的收起/展开动画
- 统一配置OnSwipe属性,同时适配顶部滚动判断和方向逻辑
- 增加RecyclerView滚动监听,满足Filters和滑动方向绑定的需求
调整后的MotionScene代码
<?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" xmlns:motion="http://schemas.android.com/apk/res-auto"> <Transition android:id="@+id/main_transition" app:constraintSetStart="@+id/state_idle" app:constraintSetEnd="@+id/state_all_hidden"> <OnSwipe motion:dragDirection="dragUp" motion:moveWhenScrollAtTop="true" motion:touchAnchorId="@id/mainRv" motion:touchAnchorSide="top" /> <!-- 关键帧配置:拆分两个组件的动画区间 --> <KeyFrameSet> <!-- Header 仅在0-50%进度完成收起 --> <KeyPosition motion:keyPositionType="parentRelative" motion:percentY="0" motion:framePosition="50" motion:target="@id/headerRv" /> <KeyAttribute android:alpha="0" motion:framePosition="50" motion:target="@id/headerRv" /> <!-- Filters 在0-50%进度保持原位,50%-100%完成收起 --> <KeyPosition motion:keyPositionType="parentRelative" motion:percentY="0" motion:framePosition="50" motion:target="@id/filtersRv" /> <KeyAttribute android:alpha="1" motion:framePosition="50" motion:target="@id/filtersRv" /> </KeyFrameSet> </Transition> <ConstraintSet android:id="@+id/state_idle"> <Constraint android:id="@+id/headerRv" android:layout_width="0dp" android:layout_height="wrap_content" android:alpha="1" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <Constraint android:id="@+id/filtersRv" android:layout_width="0dp" android:layout_height="wrap_content" android:paddingHorizontal="12dp" android:paddingVertical="16dp" android:alpha="1" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/headerRv" /> <ConstraintOverride android:id="@id/mainRv" motion:visibilityMode="ignore" /> </ConstraintSet> <ConstraintSet android:id="@+id/state_all_hidden"> <Constraint android:id="@+id/headerRv" android:layout_width="0dp" android:layout_height="wrap_content" android:alpha="0" app:layout_constraintBottom_toTopOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" /> <Constraint android:id="@+id/filtersRv" android:layout_width="0dp" android:layout_height="wrap_content" android:paddingHorizontal="12dp" android:paddingVertical="16dp" android:alpha="0" app:layout_constraintBottom_toTopOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" /> <ConstraintOverride android:id="@id/mainRv" motion:visibilityMode="ignore" /> </ConstraintSet> </MotionScene>
补充业务逻辑适配
在页面代码中添加RecyclerView滚动监听,实现「顶部显示Header、上滑显示Filters、下滑隐藏Filters」的需求:
var isScrollingUp = false mainRv.addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { super.onScrolled(recyclerView, dx, dy) isScrollingUp = dy > 0 // 列表滚动到顶部时自动显示Header if (!mainRv.canScrollVertically(-1)) { motionLayout.setProgress(0f) return } // 上滑时最大进度限制为0.5:仅隐藏Header,保持Filters显示 // 下滑时允许进度到1.0:Filters同步隐藏 val maxProgress = if (isScrollingUp) 0.5f else 1.0f if (motionLayout.progress > maxProgress) { motionLayout.setProgress(maxProgress) } } })
内容的提问来源于stack exchange,提问作者Totoro no Totoro
相关产品推荐
相关产品推荐

