作为 CoordinatorLayout 父级的 MotionLayout 中断了滚动
MotionLayout as parent of CoordinatorLayout broke scrolling
当在 MotionLayout 内部使用 CoordinatorLayout 并且在屏幕滚动时一些视图请求布局(通过 requestLayout()
函数),其余的滚动被破坏。
布局结构为:
<MotionLayout>
<View/> <!-- Animated with motion scene -->
<CoordinatorLayout>
<AppBarLayout>
<ImageView/> <!-- Requests layout -->
</AppBarLayout>
<RecyclerView/>
</CoordinatorLayout>
<MotionLayout>
运动场景:
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<Transition
app:constraintSetEnd="@id/end"
app:constraintSetStart="@id/start" />
<ConstraintSet android:id="@+id/start">
<Constraint android:id="@id/background">
<PropertySet android:alpha="0" />
</Constraint>
</ConstraintSet>
<ConstraintSet android:id="@+id/end">
<Constraint android:id="@id/background">
<PropertySet android:alpha="1" />
</Constraint>
</ConstraintSet>
</MotionScene>
动画进度是这样计算的:
appBarLayout.addOnOffsetChangedListener(OnOffsetChangedListener { _, verticalOffset ->
motionLayout.progress = -verticalOffset / appBarLayout.totalScrollRange.toFloat()
})
接下来是重点:
- AppBarLayout(作为协调器布局的一部分)通知(通过侦听器)有关滚动位置的变化。
- MotionLayout 根据上面提到的滚动位置改变它的进度。
- 虽然滚动一些视图应该调用
requestLayout()
- 在我的例子中它发生在 ImageView 点击
之后,任何后续滚动都将被破坏,布局看起来无效。
为了确定根本原因,我尝试删除带有 MotionLayout.setProgress()
的行,其余代码保持不变。然后滚动按预期工作。在此之后,我决定放回 MotionLayout.setProgress()
,这次我删除了 View.requestLayout()
。这次滚动也按预期工作。但是,当我试图同时放回 MotionLayout.setProgress()
和 View.requestLayout()
时,滚动中断了。
我还向 Google 问题跟踪器提交了一个错误:https://issuetracker.google.com/u/1/issues/178976381
默认情况下,MotionLayout 在转换期间忽略 requestLayout。这样做是为了性能。要在您的标签中启用添加
layoutDuringTransition="honorRequest"
当在 MotionLayout 内部使用 CoordinatorLayout 并且在屏幕滚动时一些视图请求布局(通过 requestLayout()
函数),其余的滚动被破坏。
布局结构为:
<MotionLayout>
<View/> <!-- Animated with motion scene -->
<CoordinatorLayout>
<AppBarLayout>
<ImageView/> <!-- Requests layout -->
</AppBarLayout>
<RecyclerView/>
</CoordinatorLayout>
<MotionLayout>
运动场景:
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<Transition
app:constraintSetEnd="@id/end"
app:constraintSetStart="@id/start" />
<ConstraintSet android:id="@+id/start">
<Constraint android:id="@id/background">
<PropertySet android:alpha="0" />
</Constraint>
</ConstraintSet>
<ConstraintSet android:id="@+id/end">
<Constraint android:id="@id/background">
<PropertySet android:alpha="1" />
</Constraint>
</ConstraintSet>
</MotionScene>
动画进度是这样计算的:
appBarLayout.addOnOffsetChangedListener(OnOffsetChangedListener { _, verticalOffset ->
motionLayout.progress = -verticalOffset / appBarLayout.totalScrollRange.toFloat()
})
接下来是重点:
- AppBarLayout(作为协调器布局的一部分)通知(通过侦听器)有关滚动位置的变化。
- MotionLayout 根据上面提到的滚动位置改变它的进度。
- 虽然滚动一些视图应该调用
requestLayout()
- 在我的例子中它发生在 ImageView 点击 之后,任何后续滚动都将被破坏,布局看起来无效。
为了确定根本原因,我尝试删除带有 MotionLayout.setProgress()
的行,其余代码保持不变。然后滚动按预期工作。在此之后,我决定放回 MotionLayout.setProgress()
,这次我删除了 View.requestLayout()
。这次滚动也按预期工作。但是,当我试图同时放回 MotionLayout.setProgress()
和 View.requestLayout()
时,滚动中断了。
我还向 Google 问题跟踪器提交了一个错误:https://issuetracker.google.com/u/1/issues/178976381
默认情况下,MotionLayout 在转换期间忽略 requestLayout。这样做是为了性能。要在您的标签中启用添加
layoutDuringTransition="honorRequest"