Search code examples
android-coordinatorlayoutandroid-motionlayout

MotionLayout as parent of CoordinatorLayout broke scrolling


When CoordinatorLayout is used inside of MotionLayout and while screen scrolling some view requests layout (via requestLayout() function), the rest of the scrolling is broken.

Positioning broken

The layout structure is:

<MotionLayout>
    <View/> <!-- Animated with motion scene -->
    <CoordinatorLayout>
        <AppBarLayout>
            <ImageView/> <!-- Requests layout -->
        </AppBarLayout>
        <RecyclerView/>
    </CoordinatorLayout>
<MotionLayout>

The MotionScene:

<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>

The animation progress is calculated like this:

appBarLayout.addOnOffsetChangedListener(OnOffsetChangedListener { _, verticalOffset ->
    motionLayout.progress = -verticalOffset / appBarLayout.totalScrollRange.toFloat()
})

The key points are next:

  1. AppBarLayout (as part of Coordinator layout) notifies (thru listener) about scrolling position changes.
  2. MotionLayout changes its progress according to the scrolling position mentioned above.
  3. While scrolling some view should call requestLayout() - in my example it happens on ImageView click After that, any following scrolling will be broken and the layout looks invalid.

To identify the root cause I've tried to remove the line with MotionLayout.setProgress() with the rest of the code remains unchanged. And after that scrolling has worked as expected. After this I decided to put back MotionLayout.setProgress() and this time I've removed View.requestLayout(). And this time scrolling works as expected too. But when I'm trying to put back both MotionLayout.setProgress() and View.requestLayout() the scrolling broke.

Also I've submitted a bug to the Google issue tracker: https://issuetracker.google.com/u/1/issues/178976381


Solution

  • By default MotionLayout ignores requestLayout during transitions. This is done for performance. To enable in your tag add

    layoutDuringTransition="honorRequest"