androidx / constraintlayout

ConstraintLayout is an Android layout component which allows you to position and size widgets in a flexible way
Apache License 2.0
1.08k stars 174 forks source link

MotionLayout animation issue when recyclerview scrolling #441

Open cloner93 opened 3 years ago

cloner93 commented 3 years ago

We have been working on an android project, and we faced with a problem in animation into the motionLayout when recyclerView is scrolling (you can see in video).

after scroll in anywhere on screen animation is fine but after scroll recyclerview appBar animation is run but so weird.

I added picture and video from UI architect to avoid confusion.

enter image description here

ProfileFragment.kt

    private fun coordinateMotion() {
        val appBarLayout: AppBarLayout? = binding.appbarLayout
        val motionLayout: MotionLayout = binding.profileHeaderInfo as MotionLayout

        val p = DecimalFormat("0.0");

        val listener = AppBarLayout.OnOffsetChangedListener { unused, verticalOffset ->
            val seekPosition = -verticalOffset / appBarLayout?.totalScrollRange!!.toFloat()
            motionLayout.progress = seekPosition
            Log.d(TAG, "coordinateMotion: $seekPosition")
        }

        appBarLayout?.addOnOffsetChangedListener(listener)
    }

ProfileFragment:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <androidx.coordinatorlayout.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <androidx.core.widget.NestedScrollView
            android:layout_width="match_parent"
            android:fillViewport="true"
            android:layout_height="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior">

            <include
                android:id="@+id/profile_include"
                layout="@layout/profile_layout_content" />
        </androidx.core.widget.NestedScrollView>

        <com.google.android.material.appbar.AppBarLayout
            android:id="@+id/appbar_layout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <androidx.constraintlayout.motion.widget.MotionLayout
                android:id="@+id/profile_header_info"
                android:layout_width="match_parent"
                app:motionDebug="SHOW_PATH"
                android:layout_height="wrap_content"
                android:minHeight="80dp"
                android:background="@color/primary"
                app:layout_scrollFlags="scroll|enterAlways|snap|exitUntilCollapsed"
                app:layoutDescription="@xml/fragment_profile_xml_profile_header_info_scene"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent">

                .....

            </androidx.constraintlayout.motion.widget.MotionLayout>
        </com.google.android.material.appbar.AppBarLayout>
    </androidx.coordinatorlayout.widget.CoordinatorLayout>

app ui architect

fragment_profile_xml_profile_header_info_scene.xml

<?xml version="1.0" encoding="utf-8"?>
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:motion="http://schemas.android.com/apk/res-auto">

    <ConstraintSet android:id="@+id/start">
        <Constraint
            android:id="@+id/profile_user_name"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginStart="16dp"
            android:layout_marginEnd="24dp"
            motion:layout_constraintEnd_toEndOf="parent"
            motion:layout_constraintStart_toEndOf="@+id/profile_image"
            motion:layout_constraintTop_toTopOf="@+id/profile_image">
        </Constraint>
        <Constraint
            android:id="@+id/profile_user_login"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            motion:layout_constraintBottom_toTopOf="@+id/profile_user_desc"
            motion:layout_constraintEnd_toEndOf="@+id/profile_user_name"
            motion:layout_constraintStart_toStartOf="@+id/profile_user_name"
            motion:layout_constraintTop_toBottomOf="@+id/profile_user_name">
        </Constraint>
        <Constraint
            android:id="@+id/profile_user_desc"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            motion:layout_constraintBottom_toBottomOf="@+id/profile_image"
            motion:layout_constraintEnd_toEndOf="@+id/profile_user_login"
            motion:layout_constraintStart_toStartOf="@+id/profile_user_login"
            motion:layout_constraintTop_toBottomOf="@+id/profile_user_login" />
        <Constraint
            android:id="@+id/profile_image"
            android:layout_width="75dp"
            android:layout_height="75dp"
            android:layout_marginStart="24dp"
            android:layout_marginTop="24dp"
            android:layout_marginEnd="16dp"
            android:elevation="10dp"
            motion:layout_constraintEnd_toEndOf="parent"
            motion:layout_constraintHorizontal_bias="0.0"
            motion:layout_constraintStart_toStartOf="parent"
            motion:layout_constraintTop_toTopOf="parent" />

    </ConstraintSet>

    <ConstraintSet android:id="@+id/end">
        <Constraint
            android:id="@+id/profile_user_name"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginStart="16dp"
            motion:layout_constraintStart_toEndOf="@+id/profile_image"
            motion:layout_constraintTop_toTopOf="@+id/profile_image">
            <CustomAttribute
                motion:attributeName="textSize"
                motion:customDimension="10sp" />
        </Constraint>
        <Constraint
            android:id="@+id/profile_user_login"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            motion:layout_constraintBottom_toBottomOf="@id/profile_user_name"
            motion:layout_constraintStart_toEndOf="@+id/profile_user_name"
            motion:layout_constraintTop_toTopOf="@id/profile_user_name"
            motion:layout_constraintEnd_toEndOf="parent">

            <CustomAttribute
                motion:attributeName="textSize"
                motion:customDimension="8sp" />
        </Constraint>
        <Constraint
            android:id="@+id/profile_user_desc"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            motion:layout_constraintBottom_toBottomOf="@+id/profile_image"
            motion:layout_constraintEnd_toEndOf="@+id/profile_user_login"
            motion:layout_constraintStart_toStartOf="@+id/profile_user_name"
            motion:layout_constraintTop_toBottomOf="@+id/profile_user_name" />
        <Constraint
            android:id="@+id/profile_image"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:layout_marginStart="24dp"
            android:layout_marginEnd="16dp"
            android:layout_marginBottom="16dp"
            android:elevation="10dp"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintEnd_toEndOf="parent"
            motion:layout_constraintHorizontal_bias="0.0"
            motion:layout_constraintStart_toStartOf="parent"
            motion:layout_constraintTop_toTopOf="parent" />
        <Constraint
            android:id="@+id/profile_header_divider"
            android:layout_width="0dp"
            android:layout_height="1dp"
            android:layout_marginTop="0dp"
            motion:layout_constraintEnd_toEndOf="@+id/profile_user_desc"
            motion:layout_constraintStart_toStartOf="@+id/profile_image"
            motion:layout_constraintTop_toBottomOf="@+id/profile_user_desc" />
        <Constraint
            android:id="@+id/profile_header_company_icon"
            android:layout_width="20dp"
            android:layout_height="20dp"
            motion:layout_constraintStart_toStartOf="@+id/profile_header_divider"
            motion:layout_constraintTop_toTopOf="parent" />
    </ConstraintSet>

    <Transition
        motion:constraintSetEnd="@+id/end"
        motion:constraintSetStart="@id/start"
        motion:duration="1000">
        <KeyFrameSet>
            <KeyAttribute
                android:alpha="0"
                motion:framePosition="100"
                motion:motionTarget="@+id/profile_header_divider" />
        </KeyFrameSet>
        <KeyFrameSet>
<!--            <KeyAttribute-->
<!--                android:alpha="0"-->
<!--                motion:framePosition="100"-->
<!--                motion:motionTarget="@+id/profile_user_desc" />-->
            <KeyAttribute
                android:alpha="0"
                motion:framePosition="100"
                motion:motionTarget="@+id/profile_header_company" />
            <KeyAttribute
                android:alpha="0"
                motion:framePosition="100"
                motion:motionTarget="@+id/profile_header_location" />
            <KeyAttribute
                android:alpha="0"
                motion:framePosition="100"
                motion:motionTarget="@+id/profile_header_create_at" />
            <KeyAttribute
                android:alpha="0"
                motion:framePosition="100"
                motion:motionTarget="@+id/profile_header_company_icon" />
            <KeyAttribute
                android:alpha="0.0"
                motion:framePosition="100"
                motion:motionTarget="@+id/profile_header_location_icon" />
            <KeyAttribute
                android:alpha="0"
                motion:framePosition="100"
                motion:motionTarget="@+id/profile_header_create_at_icon" />
        </KeyFrameSet>

        <OnSwipe />

    </Transition>
</MotionScene>
jafu888 commented 3 years ago

A few issues KeyFrames are for modifying the behavior between states. You seem to be trying to change the end constraintSet by using KeyFrames. That will lead to bugs. Generally motion:framePosition="100" is a bug except in complex situations. It is creating a difference between the final state (The ConstraintSet) and the transitions final state the (KeyAttributes)

add "android:alpha="0" to the end constraintsets's constraints

Also

You probably do not want the <OnSwipe> CoordnatorLayout is driving the animation. See MotionLayout examples

cloner93 commented 3 years ago

hi @jafu888 thanks for your help.

as you said, I deleted all KeyFrameSets and set android:alpha="0" in constraints. also i delete <OnSwip> , coordnatorLayout is driving the animation. But the problem remains.

ezgif-2-eacca465651b

jafu888 commented 3 years ago

Your code looks almost exactly like the example. The only thing I see odd is you set android:layout_height="wrap_content"

Not match parent
android:layout_height="match_parent"

Try that?