Search code examples
androidandroid-constraintlayoutandroid-motionlayout

How to create multiple transitions on a single view


Is it possible to implement draggable panel! using motion layout?

I have tried to play around the google sample of youtube like transition by using multiple Transitions with OnSwipe on a single view but not able to succeed. Can anyone please guide me to implement how to apply the two transitions using OnSwipe on the same view?

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

    <Transition
        app:constraintSetEnd="@id/expanded"
        app:constraintSetStart="@id/collapsed"
        app:duration="100"
        app:interpolator="easeInOut">

        <OnSwipe
            app:dragDirection="dragUp"
            app:maxAcceleration="200"
            app:touchAnchorId="@+id/videoViewContainer"
            app:touchAnchorSide="top" />


        <KeyFrameSet>
            <!--<KeyAttribute-->
                <!--app:framePosition="0"-->
                <!--app:target="@id/videoView">-->
                <!--<CustomAttribute-->
                    <!--app:attributeName="EndPadding"-->
                    <!--app:customDimension="@dimen/video_view_right_padding" />-->
            <!--</KeyAttribute>-->

            <!--<KeyAttribute-->
                <!--app:framePosition="20"-->
                <!--app:target="@id/videoView">-->
                <!--<CustomAttribute-->
                    <!--app:attributeName="EndPadding"-->
                    <!--app:customDimension="0dp" />-->
            <!--</KeyAttribute>-->

            <KeyAttribute
                app:framePosition="0"
                app:target="@id/videoMotionLayout">
                <CustomAttribute
                    app:attributeName="height"
                    app:customDimension="90dp" />
            </KeyAttribute>

            <KeyAttribute
                app:framePosition="20"
                app:target="@id/videoMotionLayout">
                <CustomAttribute
                    app:attributeName="height"
                    app:customDimension="250dp" />
            </KeyAttribute>

            <KeyAttribute
                app:framePosition="0"
                app:target="@id/videoMotionLayout">
                <CustomAttribute
                    app:attributeName="width"
                    app:customDimension="160dp" />
            </KeyAttribute>

            <KeyAttribute
                app:framePosition="20"
                app:target="@id/videoMotionLayout">
                <CustomAttribute
                    app:attributeName="width"
                    app:customDimension="0dp" />
            </KeyAttribute>

            <KeyAttribute
                android:alpha="0"
                app:framePosition="0"
                app:target="@id/videoRecyclerView" />

            <KeyAttribute
                android:alpha="1"
                app:framePosition="100"
                app:target="@id/videoRecyclerView" />

            <KeyAttribute
                app:framePosition="0"
                app:target="@id/videoViewContainer">
                <CustomAttribute
                    app:attributeName="cardElevation"
                    app:customDimension="5dp" />
            </KeyAttribute>

            <KeyAttribute
                app:framePosition="3"
                app:target="@id/videoViewContainer">
                <CustomAttribute
                    app:attributeName="cardElevation"
                    app:customDimension="0dp" />
            </KeyAttribute>

        </KeyFrameSet>
    </Transition>


    <Transition
        app:constraintSetEnd="@id/close"
        app:constraintSetStart="@id/close"
        app:duration="100"
        app:interpolator="easeInOut">

        <OnSwipe
            app:dragDirection="dragLeft"
            app:maxAcceleration="200"
            app:touchAnchorId="@+id/videoViewContainer"
            app:touchAnchorSide="top" />


        <KeyFrameSet>
            <KeyAttribute
                app:framePosition="0"
                app:target="@id/videoMotionLayout">
            </KeyAttribute>
        </KeyFrameSet>
    </Transition>

    <Transition
        app:constraintSetEnd="@id/close"
        app:constraintSetStart="@id/close"
        app:duration="100"
        app:interpolator="easeInOut">

        <OnSwipe
            app:dragDirection="dragRight"
            app:maxAcceleration="200"
            app:touchAnchorId="@+id/videoViewContainer"
            app:touchAnchorSide="top" />


        <KeyFrameSet>

        </KeyFrameSet>
    </Transition>

    <ConstraintSet android:id="@+id/close">

        <Constraint
            android:id="@id/videoViewContainer"
            android:layout_height="250dp"
            android:layout_marginStart="0dp"
            android:layout_marginEnd="0dp"
            android:layout_marginBottom="0dp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <Constraint
            android:id="@id/content"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_marginStart="0dp"
            android:layout_marginEnd="0dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/videoViewContainer" />

    </ConstraintSet>

    <ConstraintSet android:id="@+id/collapsed">

        <Constraint
            android:id="@id/videoViewContainer"
            android:layout_height="113dp"
            android:layout_width="200dp"
            android:layout_marginStart="10dp"
            android:layout_marginEnd="10dp"
            android:layout_marginBottom="65dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"/>

        <Constraint
            android:id="@id/content"
            android:layout_width="match_parent"
            android:layout_height="-1dp"
            android:layout_marginStart="10dp"
            android:layout_marginEnd="10dp"
            android:layout_marginBottom="66dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/videoViewContainer" />

    </ConstraintSet>

</MotionScene>


It would be helpful If I can move the mini-player scene to the left and right directions as well. Thank you.


Solution

  • How to create multiple transitions on a single view

    It is possible to add multiple transitions to the same View. For example we can define animations with motion:dragDirection="dragDown" and motion:dragDirection="dragUp" on the same touchAnchorId without any problem. The possible configurations are given below,

      motion:dragDirection="dragUp"
      motion:dragDirection="dragDown"
      motion:dragDirection="dragLeft"
      motion:dragDirection="dragRight"
    

    What to do

    So, here I show you how to implement all these together on the same View. Before writing any code, please see the below gif to better understand what we are trying to create.

    gif image showing multi transition example

    How to do

    Add the ConstraintLayout dependency:

    dependencies {
    implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta2'
     }
    

    Create a MotionLayout file:

    activity_main.xml

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.motion.widget.MotionLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:motion="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/motionLayout"
        motion:layoutDescription="@xml/motionscene1"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <View
            android:id="@+id/image"
            android:layout_width="match_parent"
            android:layout_height="240dp"
            android:background="#453672"
            motion:layout_constraintStart_toStartOf="parent"
            motion:layout_constraintTop_toTopOf="parent"/>
    
    </androidx.constraintlayout.motion.widget.MotionLayout>
    

    Create a MotionScene: A MotionScene is an XML resource file that contains all of the motion descriptions for the corresponding layout. Put the file under res/xml/ folder

    motionscene1.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">
    
        <!-- top to bottom right-->
        <Transition
            motion:constraintSetEnd="@id/step2"
            motion:constraintSetStart="@id/step1"
    
            motion:duration="500">
            <OnSwipe
                motion:dragDirection="dragDown"
                motion:touchAnchorId="@id/image"
                motion:touchAnchorSide="top" />
        </Transition>
        <!-- bottom right to bottom left-->
        <Transition
            motion:constraintSetEnd="@id/step3"
            motion:constraintSetStart="@id/step2"
    
            motion:duration="500">
            <OnSwipe
                motion:dragDirection="dragLeft"
                motion:touchAnchorId="@id/image"
                motion:touchAnchorSide="left" />
        </Transition>
    
        <!-- bottom left to top-->
        <Transition
            motion:constraintSetEnd="@id/step1"
            motion:constraintSetStart="@id/step3"
    
            motion:duration="500">
            <OnSwipe
                motion:dragDirection="dragUp"
                motion:touchAnchorId="@id/image"
                motion:touchAnchorSide="top" />
        </Transition>
        <!-- bottom right to top-->
        <Transition
            motion:constraintSetEnd="@id/step1"
            motion:constraintSetStart="@id/step2"
    
            motion:duration="500">
            <OnSwipe
                motion:dragDirection="dragUp"
                motion:touchAnchorId="@id/image"
                motion:touchAnchorSide="top" />
        </Transition>
        <!-- place the view on top-->
        <ConstraintSet android:id="@+id/step1">
            <Constraint
                android:id="@id/image"
                android:layout_width="match_parent"
                android:layout_height="240dp"
                android:layout_marginStart="0dp"
                android:layout_marginLeft="0dp"
                android:layout_marginTop="0dp"
                android:layout_marginEnd="0dp"
                android:layout_marginRight="0dp"
                android:layout_marginBottom="0dp"
                motion:layout_constraintStart_toStartOf="parent"
                motion:layout_constraintTop_toTopOf="parent">
                <PropertySet android:alpha="1" />
            </Constraint>
        </ConstraintSet>
        <!-- place the view on bottom right-->
        <ConstraintSet android:id="@+id/step2">
            <Constraint
                android:id="@id/image"
                android:layout_width="150dp"
                android:layout_height="100dp"
                android:layout_marginStart="16dp"
                android:layout_marginLeft="16dp"
                android:layout_marginTop="16dp"
                android:layout_marginEnd="16dp"
                android:layout_marginRight="16dp"
                android:layout_marginBottom="16dp"
    
                motion:layout_constraintBottom_toBottomOf="parent"
                motion:layout_constraintEnd_toEndOf="parent">
                <PropertySet android:alpha="1" />
            </Constraint>
        </ConstraintSet>
        <!-- place the view on bottom left-->
        <ConstraintSet android:id="@+id/step3">
            <Constraint
                android:id="@id/image"
                android:layout_width="150dp"
                android:layout_height="100dp"
                android:layout_marginStart="16dp"
                android:layout_marginLeft="16dp"
                android:layout_marginTop="16dp"
                android:layout_marginEnd="16dp"
                android:layout_marginRight="16dp"
                android:layout_marginBottom="16dp"
    
                motion:layout_constraintBottom_toBottomOf="parent"
                motion:layout_constraintStart_toStartOf="parent">
                <PropertySet android:alpha="1" />
            </Constraint>
        </ConstraintSet>
    
    
    </MotionScene>
    

    That's all. You created an amazing custom animation with out writing any java/kotlin code. MotionLayout is fully declarative, meaning that you can describe any transitions in XML, no matter how complex.