首页 > 解决方案 > MotionLayout 作为 CoordinatorLayout 的父级打破了滚动

问题描述

当在 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()
})

接下来是重点:

  1. AppBarLayout(作为协调器布局的一部分)通知(通过侦听器)滚动位置更改。
  2. MotionLayout 根据上面提到的滚动位置改变它的进度。
  3. 在滚动某些视图时应该调用requestLayout()- 在我的示例中,它发生在 ImageView 单击之后,任何后续滚动都将被破坏并且布局看起来无效。

为了确定根本原因,我尝试删除MotionLayout.setProgress()与其余代码保持不变的行。之后滚动按预期工作。在此之后,我决定放回去MotionLayout.setProgress(),这次我删除了View.requestLayout(). 这次滚动也按预期工作。但是当我试图把两者都放回去时MotionLayout.setProgress()View.requestLayout()滚动就坏了。

我还向 Google 问题跟踪器提交了一个错误:https ://issuetracker.google.com/u/1/issues/178976381

标签: android-coordinatorlayoutandroid-motionlayout

解决方案


默认情况下,MotionLayout 在转换期间会忽略 requestLayout。这样做是为了性能。要在您的标签中启用,请添加

layoutDuringTransition="honorRequest"


推荐阅读