Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Production MotionLayout

Production MotionLayout

I'm going be showing how to solve practical animation problems using MotionLayout. We'll start with an introduction to ConstraintSets, Transitions, and KeyFrames before exploring various experiments (swiping items in a list, creating a tile game, progress animations, RecyclerView placeholders). Lastly I want to share what we've learned from using these components in production.

Jason Pearson

January 15, 2020
Tweet

More Decks by Jason Pearson

Other Decks in Programming

Transcript

  1. src/ ├── java/ └── res/ ├── drawable ├── layout │

    └── ├── mipmap └── values Creating our first MotionScene activity_main.xml @kaeawc
  2. Creating our first MotionScene src/ ├── java/ └── res/ ├──

    drawable ├── layout │ └── ├── mipmap ├── values └── xml activity_main.xml @kaeawc
  3. src/ ├── java/ └── res/ ├── drawable ├── layout │

    └── ├── mipmap ├── values └── xml └── motion_scene.xml activity_main.xml Creating our first MotionScene xml motion_scene @kaeawc
  4. @xml/motion_scene <?xml version="1.0" encoding="utf-8"?> <MotionScene xmlns:android="…" xmlns:app="…" > </MotionScene> @layout/activity_main

    <?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.motion.widget.MotionLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" app:layoutDescription="…" /> Creating our first MotionScene @layout/activity_main
  5. @xml/motion_scene <?xml version="1.0" encoding="utf-8"?> <MotionScene xmlns:android="…" xmlns:app="…" > </MotionScene> @layout/activity_main

    <?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.motion.widget.MotionLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" app:layoutDescription="…" /> Creating our first MotionScene @layout/activity_main
  6. @xml/motion_scene <?xml version="1.0" encoding="utf-8"?> <MotionScene xmlns:android="…" xmlns:app="…" > </MotionScene> @layout/activity_main

    <?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.motion.widget.MotionLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" app:layoutDescription="@xml/motion_scene" /> Creating our first MotionScene @layout/activity_main
  7. @xml/motion_scene <?xml version="1.0" encoding="utf-8"?> <MotionScene xmlns:android="…" xmlns:app="…" > <ConstraintSet android:id="@+id/start">

    </ConstraintSet> </MotionScene> Adding first ConstraintSet Omitting schemas from now on @kaeawc
  8. @xml/motion_scene <MotionScene> <ConstraintSet android:id="@+id/start"> <Constraint android:id="@+id/square" android:layout_width="100dp" android:layout_height="100dp" app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent"

    app:layout_constraintEnd_toEndOf="parent" /> </ConstraintSet> </MotionScene> Adding first ConstraintSet @xml/motion_scene motion_scene.xml ConstraintSet start @kaeawc
  9. <MotionScene> <ConstraintSet android:id="@+id/start"> <Constraint android:id="@+id/square" android:layout_width="100dp" android:layout_height="100dp" app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"

    /> </ConstraintSet> <ConstraintSet android:id="@+id/end"> </ConstraintSet> </MotionScene> Adding second ConstraintSet @xml/motion_scene motion_scene.xml ConstraintSet start ConstraintSet end @kaeawc
  10. <MotionScene> <ConstraintSet android:id="@+id/start"> <Constraint android:id="@+id/square" android:layout_width="100dp" android:layout_height="100dp" app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"

    /> </ConstraintSet> <ConstraintSet android:id="@+id/end"> <Constraint android:id="@+id/square" android:layout_width="100dp" android:layout_height="100dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" /> </ConstraintSet> </MotionScene> Adding second ConstraintSet @xml/motion_scene motion_scene.xml ConstraintSet start ConstraintSet end @kaeawc
  11. <MotionScene> <ConstraintSet android:id="@+id/start"> <Constraint android:id="@+id/square" android:layout_width="100dp" android:layout_height="100dp" app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"

    /> </ConstraintSet> <ConstraintSet android:id="@+id/end"> <Constraint android:id="@+id/square" android:layout_width="100dp" android:layout_height="100dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" /> </ConstraintSet> </MotionScene> Adding second ConstraintSet @xml/motion_scene motion_scene.xml ConstraintSet start ConstraintSet end @kaeawc
  12. @xml/motion_scene <MotionScene> <Transition /> <ConstraintSet android:id="@+id/start"> <Constraint android:id="@+id/square" android:layout_width="100dp" android:layout_height="100dp"

    app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" /> </ConstraintSet> <ConstraintSet android:id="@+id/end"> <Constraint android:id="@+id/square" android:layout_width="100dp" android:layout_height="100dp" app:layout_constraintBottom_toBottomOf="parent" Adding first Transition @xml/motion_scene motion_scene.xml ConstraintSet start ConstraintSet end Transition @kaeawc
  13. @xml/motion_scene <MotionScene> <Transition > </Transition> <ConstraintSet android:id="@+id/start"> <Constraint android:id="@+id/square" android:layout_width="100dp"

    android:layout_height="100dp" app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" /> </ConstraintSet> <ConstraintSet android:id="@+id/end"> <Constraint android:id="@+id/square" android:layout_width="100dp" Adding first Transition @xml/motion_scene motion_scene.xml ConstraintSet start ConstraintSet end Transition @kaeawc
  14. @xml/motion_scene <MotionScene> <Transition app:constraintSetStart="@id/start" > </Transition> <ConstraintSet android:id="@+id/start"> <Constraint android:id="@+id/square"

    android:layout_width="100dp" android:layout_height="100dp" app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" /> </ConstraintSet> Adding first Transition @xml/motion_scene motion_scene.xml ConstraintSet start ConstraintSet end Transition start @kaeawc
  15. @xml/motion_scene <MotionScene> <Transition app:constraintSetStart="@id/start" app:constraintSetEnd="@id/end" > </Transition> <ConstraintSet android:id="@+id/start"> <Constraint

    android:id="@+id/square" android:layout_width="100dp" android:layout_height="100dp" app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" /> </ConstraintSet> Adding first Transition @xml/motion_scene motion_scene.xml ConstraintSet start ConstraintSet end Transition start end @kaeawc
  16. @xml/motion_scene <MotionScene> <Transition app:constraintSetStart="@id/start" app:constraintSetEnd="@id/end" > <OnClick /> </Transition> <ConstraintSet

    android:id="@+id/start"> <Constraint android:id="@+id/square" android:layout_width="100dp" android:layout_height="100dp" app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" Adding first Transition @xml/motion_scene motion_scene.xml ConstraintSet start ConstraintSet end Transition OnClick start end @kaeawc
  17. @xml/motion_scene <MotionScene> <Transition app:constraintSetStart="@id/start" app:constraintSetEnd="@id/end" > <OnClick app:targetId="@id/square" /> </Transition>

    <ConstraintSet android:id="@+id/start"> <Constraint android:id="@+id/square" android:layout_width="100dp" android:layout_height="100dp" app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent" Adding first Transition @xml/motion_scene motion_scene.xml ConstraintSet start ConstraintSet end Transition OnClick square start end @kaeawc
  18. @xml/motion_scene <MotionScene> <Transition app:constraintSetStart="@id/start" app:constraintSetEnd="@id/end" > <OnClick app:targetId="@id/square" app:clickAction="transitionToEnd" />

    </Transition> <ConstraintSet android:id="@+id/start"> <Constraint android:id="@+id/square" android:layout_width="100dp" android:layout_height="100dp" app:layout_constraintTop_toTopOf="parent" Adding first Transition @xml/motion_scene motion_scene.xml ConstraintSet start ConstraintSet end Transition OnClick square start end @kaeawc
  19. @xml/motion_scene <MotionScene> <Transition app:constraintSetStart="@id/start" app:constraintSetEnd="@id/end" > <OnClick app:targetId="@id/square" app:clickAction="transitionToEnd" />

    </Transition> <ConstraintSet android:id="@+id/start"> <Constraint android:id="@+id/square" android:layout_width="100dp" android:layout_height="100dp" app:layout_constraintTop_toTopOf="parent" motion_scene.xml Adding first Transition @xml/motion_scene ConstraintSet start ConstraintSet end Transition OnClick square start end @kaeawc
  20. @xml/motion_scene <MotionScene> <Transition app:constraintSetStart="@id/start" app:constraintSetEnd="@id/end" > <OnClick app:targetId="@id/square" app:clickAction="transitionToEnd" />

    </Transition> <ConstraintSet android:id="@+id/start" … /> <ConstraintSet android:id="@+id/end" … /> </MotionScene> motion_scene.xml @xml/motion_scene ConstraintSet start ConstraintSet end OnClick Transition OnClick square start end @kaeawc
  21. @xml/motion_scene <MotionScene> <Transition app:constraintSetStart="@id/start" app:constraintSetEnd="@id/end" > <OnClick app:targetId="@id/square" app:clickAction="toggle" />

    </Transition> <ConstraintSet android:id="@+id/start" … /> <ConstraintSet android:id="@+id/end" … /> </MotionScene> motion_scene.xml @xml/motion_scene ConstraintSet start ConstraintSet end OnClick Transition OnClick square start end @kaeawc
  22. motion_scene_with_click.xml motion_scene_with_swipe.xml OnSwipe OnClick ConstraintSet start ConstraintSet end Transition OnClick

    square start end Transition OnSwipe square start end start end ConstraintSet start ConstraintSet end @kaeawc
  23. @xml/motion_scene_with_swipe <MotionScene> <Transition app:constraintSetStart="@id/start" app:constraintSetEnd="@id/end" > <OnSwipe app:touchAnchorId="@id/square" app:touchAnchorSide="bottom" app:dragDirection="dragDown"

    /> </Transition> <ConstraintSet android:id="@+id/start" … /> <ConstraintSet android:id="@+id/end" … /> </MotionScene> @xml/motion_scene_with_click <MotionScene> <Transition app:constraintSetStart="@id/start" app:constraintSetEnd="@id/end" > <OnClick app:targetId="@id/square" app:clickAction="transitionToEnd" /> </Transition> <ConstraintSet android:id="@+id/start" … /> <ConstraintSet android:id="@+id/end" … /> </MotionScene> @xml/motion_scene_with_click @xml/motion_scene_with_swipe OnSwipe OnClick @kaeawc
  24. @xml/motion_scene_with_swipe <MotionScene> <Transition app:constraintSetStart="@id/start" app:constraintSetEnd="@id/end" > <OnSwipe app:touchAnchorId="@id/square" app:touchAnchorSide="bottom" app:dragDirection="dragDown"

    /> </Transition> <ConstraintSet android:id="@+id/start" … /> <ConstraintSet android:id="@+id/end" … /> </MotionScene> @xml/motion_scene_with_click <MotionScene> <Transition app:constraintSetStart="@id/start" app:constraintSetEnd="@id/end" > <OnClick app:targetId="@id/square" app:clickAction="transitionToEnd" /> </Transition> <ConstraintSet android:id="@+id/start" … /> <ConstraintSet android:id="@+id/end" … /> </MotionScene> OnSwipe OnClick @xml/motion_scene_with_click @xml/motion_scene_with_swipe @kaeawc
  25. Adding another state swiping_scene.xml ConstraintSet start ConstraintSet end ConstraintSet middle

    Transition swipe_a OnSwipe square start middle Transition swipe_b OnSwipe square middle end
  26. @xml/motion_scene <MotionScene> <Transition app:constraintSetStart="@id/start" app:constraintSetEnd="@id/end" app:motionInterpolator="easeInOut"a > <OnClick-…-/> </Transition> <ConstraintSet-android:id="@+id/start">

    <Constraint-android:id="@id/square"> <Layout-…-/> <Motion-app:transitionEasing="standard"> </Constraint> <Constraint-android:id="@id/otherSquare"> @xml/motion_scene Interpolation @kaeawc
  27. easeInOut easeIn easeOut linear standard bounce cubic @xml/motion_scene <MotionScene> <Transition

    app:constraintSetStart="@id/start" app:constraintSetEnd="@id/end" app:motionInterpolator=" "a > <OnClick-…-/> </Transition> <ConstraintSet-android:id="@+id/start"> <Constraint-android:id="@id/square"> <Layout-…-/> Interpolation @kaeawc
  28. <ConstraintSet android:id="@+id/start" > <Constraint android:id="@id/square" > <Layout android:layout_width="100dp" android:layout_height="100dp" app:layout_constraintTop_toTopOf="parent"

    app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" > </Constraint> </ConstraintSet> Sub Elements @kaeawc
  29. <ConstraintSet android:id="@+id/start" > <Constraint android:id="@id/square" > <Layout /> <PropertySet />

    <Transform /> <Motion /> <CustomAttribute /> </Constraint> </ConstraintSet> Sub Elements @kaeawc
  30. @xml/motion_scene <MotionScene> <Transition app:constraintSetStart="@id/start" app:constraintSetEnd="@id/end" app:motionInterpolator="standard" > <OnClick-…-/> </Transition> <ConstraintSet-android:id="@+id/start">

    <Constraint-android:id="@id/square"> <Layout-…-/> <Motion-app:transitionEasing="standard"> </Constraint> <Constraint-android:id="@id/otherSquare"> @xml/motion_scene Sub Elements :-Motion @kaeawc
  31. <Transition app:constraintSetStart="@id/start" app:constraintSetEnd="@id/end" app:motionInterpolator="standard" > <OnClick-…-/> </Transition> <ConstraintSet-android:id="@+id/start"> <Constraint-android:id="@id/square"> <Layout-…-/>

    <Motion-app:transitionEasing="standard"> </Constraint> <Constraint-android:id="@id/otherSquare"> <Layout-…-/> </Constraint> </ConstraintSet> <ConstraintSet android:id="@+id/end"-…-/> </MotionScene> Sub Elements :-Motion @xml/motion_scene @kaeawc
  32. swiping_scene.xml Sub Elements: Transform ConstraintSet start ConstraintSet end Transition toggle

    OnClick square start end Transform rotate 360 rotateX 360 @kaeawc
  33. swiping_scene.xml Sub Elements: Transform ConstraintSet start ConstraintSet end Transition toggle

    OnClick square start end Transform rotate 360 rotateX 360 @kaeawc
  34. Sub Elements: Transform swiping_scene.xml ConstraintSet start ConstraintSet end Transition toggle

    OnClick square start end Transform rotate 360 rotateX 360 @kaeawc
  35. Sub Elements: Transform swiping_scene.xml ConstraintSet start ConstraintSet end Transition toggle

    OnClick square start end KeyFrameSet Transform rotate 360 rotateX 360 @kaeawc
  36. Sub Elements: Transform swiping_scene.xml ConstraintSet start ConstraintSet end Transition toggle

    OnClick square start end KeyFrameSet KeyFrame 33 KeyFrame 66 translateX -64dp translateX 64dp Transform rotate 360 rotateX 360 @kaeawc
  37. KeyFrameSet Sub Elements: Transform swiping_scene.xml ConstraintSet start ConstraintSet end Transition

    toggle OnClick square start end KeyFrameSet KeyFrame 33 KeyFrame 66 translateX -64dp translateX 64dp Transform rotate 360 rotateX 360 @kaeawc
  38. KeyFrameSets KeyFrameSet 100 0 25 50 75 KeyFrames let you

    specify a change at a point in time during transition @kaeawc
  39. KeyFrameSets KeyFrameSet 100 0 25 50 75 KeyFrames let you

    specify a change at a point in time during transition @kaeawc
  40. KeyFrameSets KeyFrameSet 100 0 25 50 75 KeyFrames let you

    specify a change at a point in time during transition @kaeawc
  41. KeyFrameSets KeyFrameSet 100 0 25 50 75 KeyFrames let you

    specify a change at a point in time during transition @kaeawc
  42. KeyFrameSets KeyFrameSet 100 0 25 50 75 KeyFrames let you

    specify a change at a point in time during transition @kaeawc
  43. <Transition> <OnClick-/> <KeyFrameSet> <KeyAttribute app:motionTarget="@id/square" app:framePosition="33" android:translationX="-64dp" /> <KeyAttribute app:motionTarget="@id/square"

    app:framePosition="66" android:translationX="64dp" /> </KeyFrameSet> </Transition> KeyFrameSets: Key Attribute @kaeawc
  44. <Transition> <OnClick-/> <KeyFrameSet> <KeyAttribute app:motionTarget="@id/square" app:framePosition="33" android:translationX="-64dp" /> <KeyAttribute app:motionTarget="@id/square"

    app:framePosition="66" android:translationX="64dp" /> </KeyFrameSet> </Transition> KeyFrameSets: Key Attribute @kaeawc
  45. <Transition> <OnClick-/> <KeyFrameSet> <KeyAttribute app:motionTarget="@id/square" app:framePosition="33" android:translationX="-64dp" /> <KeyAttribute app:motionTarget="@id/square"

    app:framePosition="66" android:translationX="64dp" /> </KeyFrameSet> </Transition> KeyFrameSets: Key Attribute @kaeawc
  46. <Transition> <OnClick-/> <KeyFrameSet> <KeyAttribute app:motionTarget="@id/square" app:framePosition="33" android:translationX="-64dp" /> <KeyAttribute app:motionTarget="@id/square"

    app:framePosition="66" android:translationX="64dp" /> </KeyFrameSet> </Transition> KeyFrameSets: Key Attribute @kaeawc
  47. KeyFrameSets: Key Attribute <Transition> <OnClick-/> <KeyFrameSet> <KeyAttribute app:motionTarget="@id/square" app:framePosition="33" android:translationX="-64dp"

    /> <KeyAttribute app:motionTarget="@id/square" app:framePosition="66" android:translationX="64dp" /> </KeyFrameSet> </Transition> @kaeawc
  48. Swipe To Reveal Action in a List @layout/activity_swipe_items <androidx.recyclerview.widget.RecyclerView android:id="@+id/email_list"

    android:layout_width="match_parent" android:layout_height="match_parent" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" /> @kaeawc
  49. @layout/activity_swipe_items <androidx.recyclerview.widget.RecyclerView android:id="@+id/email_list" android:layout_width="match_parent" @layout/email_item <androidx.constraintlayout.motion.widget.MotionLayout app:layoutDescription="@xml/swipe_item_scene" > <View android:id="@+id/archive_button"

    …/> <View android:id="@+id/foreground" …/> <ImageView android:id="@+id/thumbnail" …/> <TextView android:id="@+id/name" …/> </androidx.constraintlayout.motion.widget.MotionLayout> @xml/swipe_item_scene Swipe To Reveal Action in a List @kaeawc
  50. <MotionScene> <Transition app:constraintSetStart="@id/start" app:constraintSetEnd="@id/end" > <OnSwipe app:touchAnchorId="@id/foreground" app:touchAnchorSide="right" app:dragDirection="dragLeft" app:onTouchUp="decelerate"

    /> </Transition> <ConstraintSet android:id="@+id/start"> <Constraint android:id="@+id/foreground"> <Transform android:translationY="0dp" /> @xml/swipe_item_scene Swipe To Reveal Action in a List @kaeawc
  51. <MotionScene> <Transition app:constraintSetStart="@id/start" app:constraintSetEnd="@id/end" > <OnSwipe app:touchAnchorId="@id/foreground" app:touchAnchorSide="right" app:dragDirection="dragLeft" app:onTouchUp="decelerate"

    /> </Transition> <ConstraintSet android:id="@+id/start"> <Constraint android:id="@+id/foreground"> <Transform android:translationY="0dp" /> @xml/swipe_item_scene Swipe To Reveal Action in a List @kaeawc
  52. <MotionScene> <Transition app:constraintSetStart="@id/start" app:constraintSetEnd="@id/end" > <OnSwipe app:touchAnchorId="@id/foreground" app:touchAnchorSide="right" app:dragDirection="dragLeft" app:onTouchUp="decelerate"

    /> </Transition> <ConstraintSet android:id="@+id/start"> <Constraint android:id="@+id/foreground"> <Transform android:translationY="0dp" /> @xml/swipe_item_scene Swipe To Reveal Action in a List @kaeawc
  53. There is no change in horizontal position of any side

    in the foreground view, therefore MotionLayout cannot determine how to interpolate between the two states Swipe To Reveal @kaeawc
  54. <OnSwipe app:dragDirection="dragLeft" /> Or do not specify a touchAnchor and

    then there is no need to create clever hidden views. Swipe To Reveal Action in a List @kaeawc
  55. Collapsing Toolbar @xml/collapse_on_scroll_scene <MotionScene> <Transition app:constraintSetStart="@id/start" app:constraintSetEnd="@id/end" > <OnSwipe app:targetId="@id/header_layout"

    app:touchAnchorSide="bottom" app:dragDirection="dragUp" /> </Transition> <ConstraintSet android:id="@+id/start" … /> <ConstraintSet android:id="@+id/end" … /> </MotionScene> @kaeawc
  56. <MotionScene> <Transition …/> <ConstraintSet android:id="@+id/start"> <Constraint android:id="@id/header_layout" android:layout_width="match_parent" android:layout_height="128dp" app:layout_constraintTop_toTopOf="parent"/>

    <Constraint android:id="@id/email_list" android:layout_width="match_parent" android:layout_height="0dp" app:layout_constraintTop_toBottomOf="@id/header_layout" app:layout_constraintBottom_toBottomOf="parent"/> </ConstraintSet> <ConstraintSet android:id="@+id/end" app:deriveConstraintsFrom="@id/start"> <Constraint android:id="@id/header_layout" Collapsing Toolbar @xml/collapse_on_scroll_scene @kaeawc
  57. android:layout_height="128dp" app:layout_constraintTop_toTopOf="parent"/> <Constraint android:id="@id/todo_list" android:layout_width="match_parent" android:layout_height="0dp" app:layout_constraintTop_toBottomOf="@id/header_layout" app:layout_constraintBottom_toBottomOf="parent"/> </ConstraintSet> <ConstraintSet

    android:id="@+id/end" app:deriveConstraintsFrom="@id/start"> <Constraint android:id="@id/header_layout" android:layout_width="match_parent" android:layout_height="80dp" app:layout_constraintTop_toTopOf="parent" /> </ConstraintSet> </MotionScene> Collapsing Toolbar @xml/collapse_on_scroll_scene @kaeawc
  58. android:layout_height="128dp" app:layout_constraintTop_toTopOf="parent"/> <Constraint android:id="@id/todo_list" android:layout_width="match_parent" android:layout_height="0dp" app:layout_constraintTop_toBottomOf="@id/header_layout" app:layout_constraintBottom_toBottomOf="parent"/> </ConstraintSet> <ConstraintSet

    android:id="@+id/end" app:deriveConstraintsFrom="@id/start"> <Constraint android:id="@id/header_layout" android:layout_width="match_parent" android:layout_height="80dp" app:layout_constraintTop_toTopOf="parent" /> </ConstraintSet> </MotionScene> Collapsing Toolbar - Derived Constraints @xml/collapse_on_scroll_scene @kaeawc
  59. <MotionScene> <Transition app:constraintSetStart="@id/start" app:constraintSetEnd="@id/end" /> <ConstraintSet android:id="@+id/start"> <Constraint android:id="@id/header_title"> </Constraint>

    </ConstraintSet> <ConstraintSet android:id="@+id/end"> <Constraint android:id="@id/header_title"> </Constraint> </ConstraintSet> </MotionScene> Nested MotionLayout Header - Scaling Text @xml/collapsing_header_scene @kaeawc
  60. <MotionScene> <Transition …/> <ConstraintSet android:id="@+id/start"> <Constraint android:id="@id/header_title"> </Constraint> </ConstraintSet> <ConstraintSet

    android:id="@+id/end"> <Constraint android:id="@id/header_title"> </Constraint> </ConstraintSet> </MotionScene> @xml/collapsing_header_scene Nested MotionLayout Header - Scaling Text @kaeawc
  61. Scaling Text <ConstraintSet android:id="@+id/start"> <Constraint android:id="@id/sample_text"> <CustomAttribute app:attributeName="textSize" app:customFloatValue="64" />

    </Constraint> </ConstraintSet> <ConstraintSet android:id="@+id/end"> <Constraint android:id="@id/sample_text"> <CustomAttribute app:attributeName="textSize" app:customFloatValue="32" /> </Constraint> </ConstraintSet> @kaeawc
  62. <ConstraintSet android:id="@+id/start" > <Constraint android:id="@id/sample_text" > <CustomAttribute app:attributeName="textSize" app:customFloatValue="64" />

    </Constraint> </ConstraintSet> <ConstraintSet android:id="@+id/end" > <Constraint android:id="@id/sample_text" > <CustomAttribute app:attributeName="textSize" app:customFloatValue="32" /> </Constraint> </ConstraintSet> Scaling Text @kaeawc
  63. <ConstraintSet android:id="@+id/start" > <Constraint android:id="@id/sample_t <Transform android:scaleX="1" android:scaleY="1" /> </Constraint>

    </ConstraintSet> <ConstraintSet android:id="@+id/end" > <Constraint android:id="@id/sample_t <Transform android:scaleX="0.5" android:scaleY="0.5" /> </Constraint> </ConstraintSet> <ConstraintSet android:id="@+id/start" > <Constraint android:id="@id/sample_text" > <CustomAttribute app:attributeName="textSize" app:customFloatValue="64" /> </Constraint> </ConstraintSet> <ConstraintSet android:id="@+id/end" > <Constraint android:id="@id/sample_text" > <CustomAttribute app:attributeName="textSize" app:customFloatValue="32" /> </Constraint> </ConstraintSet> Scaling Text @kaeawc
  64. <MotionScene> <Transition …/> <ConstraintSet android:id="@+id/start"> <Constraint android:id="@id/header_title"> <Transform android:scaleX="1.0" android:scaleY="1.0"

    /> </Constraint> </ConstraintSet> <ConstraintSet android:id="@+id/end"> <Constraint android:id="@id/header_title"> <Transform android:scaleX="0.7" android:scaleY="0.7" /> </Constraint> </ConstraintSet> </MotionScene> Nested MotionLayout Header @xml/collapsing_header_scene @kaeawc
  65. <MotionScene> <Transition …/> <ConstraintSet android:id="@+id/start"> <Constraint android:id="@id/header_layout"> <Layout android:layout_width="match_parent" android:layout_height="128dp"

    app:layout_constraintTop_toTopOf="parent" /> <PropertySet app:motionProgress="0" /> <Transform android:elevation="0dp" /> </Constraint> <Constraint android:id="@id/todo_list"> <Layout android:layout_width="match_parent" android:layout_height="0dp" Nested MotionLayout @xml/collapse_on_scroll_scene @kaeawc
  66. <MotionScene> <Transition …/> <ConstraintSet android:id="@+id/start"> <Constraint android:id="@id/header_layout"> <Layout android:layout_width="match_parent" android:layout_height="128dp"

    app:layout_constraintTop_toTopOf="parent" /> <PropertySet app:motionProgress="0" /> <Transform android:elevation="0dp" /> </Constraint> <Constraint android:id="@id/todo_list"> <Layout android:layout_width="match_parent" android:layout_height="0dp" app:layout_constraintTop_toBottomOf="@id/header_layout" app:layout_constraintBottom_toBottomOf="parent" /> </Constraint> </ConstraintSet> <ConstraintSet android:id="@+id/end" Nested MotionLayout @xml/collapse_on_scroll_scene @kaeawc
  67. <Layout android:layout_width="match_parent" android:layout_height="0dp" app:layout_constraintTop_toBottomOf="@id/header_layout" app:layout_constraintBottom_toBottomOf="parent" /> </Constraint> </ConstraintSet> <ConstraintSet android:id="@+id/end"

    app:deriveConstraintsFrom="@id/start"> <Constraint android:id="@id/header_layout"> <Layout android:layout_width="match_parent" android:layout_height="80dp" app:layout_constraintTop_toTopOf="parent" /> <PropertySet app:motionProgress="1"/> <Transform android:elevation="4dp" /> </Constraint> </ConstraintSet> </MotionScene> Nested MotionLayout @xml/collapse_on_scroll_scene @kaeawc
  68. </Constraint> </ConstraintSet> <ConstraintSet android:id="@+id/end" app:deriveConstraintsFrom="@id/start"> <Constraint android:id="@id/header_layout"> <Layout android:layout_width="match_parent" android:layout_height="80dp"

    app:layout_constraintTop_toTopOf="parent" /> <PropertySet app:motionProgress="1"/> <Transform android:elevation="4dp" /> </Constraint> </ConstraintSet> </MotionScene> Nested MotionLayout @xml/collapse_on_scroll_scene @kaeawc
  69. </ConstraintSet> <ConstraintSet android:id="@+id/end" app:deriveConstraintsFrom="@id/start"> <Constraint android:id="@id/header_layout"> <Layout android:layout_width="match_parent" android:layout_height="80dp" app:layout_constraintTop_toTopOf="parent"

    /> <PropertySet app:motionProgress="1"/> <Transform android:elevation="4dp" /> </Constraint> </ConstraintSet> </MotionScene> Nested MotionLayout @xml/collapse_on_scroll_scene @kaeawc
  70. <MotionScene> <Transition app:constraintSetStart="@id/start" app:constraintSetEnd="@id/end" /> <ConstraintSet android:id="@+id/start"> <Constraint android:id="@id/header_title"> <Layout

    android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginBottom="16dp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintBottom_toBottomOf="parent"/> </Constraint> </ConstraintSet> <ConstraintSet android:id="@+id/end" app:deriveConstraintsFrom="@id/start"> <Constraint android:id="@id/header_title"> <Transform android:scaleX="0.7" android:scaleY="0.7" Changing Text in MotionLayout @xml/collapsing_header_scene @kaeawc
  71. tionScene> <Transition app:constraintSetStart="@id/start" app:constraintSetEnd="@id/end" /> <ConstraintSet android:id="@+id/start"> <Constraint android:id="@id/header_title"> <Layout

    android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginBottom="16dp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintBottom_toBottomOf="parent"/> </Constraint> </ConstraintSet> <ConstraintSet android:id="@+id/end" app:deriveConstraintsFrom="@id/start"> <Constraint android:id="@id/header_title"> Changing Text in MotionLayout @xml/collapsing_header_scene @kaeawc
  72. <Transition app:constraintSetStart="@id/start" app:constraintSetEnd="@id/end" /> <ConstraintSet android:id="@+id/start"> <Constraint android:id="@id/header_title"> <Layout android:layout_width="wrap_content"

    android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginBottom="16dp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintBottom_toBottomOf="parent"/> </Constraint> </ConstraintSet> <ConstraintSet android:id="@+id/end" app:deriveConstraintsFrom="@id/start"> <Constraint android:id="@id/header_title"> <Transform android:scaleX="0.7" Changing Text in MotionLayout @xml/collapsing_header_scene @kaeawc
  73. <Transition app:constraintSetStart="@id/start" app:constraintSetEnd="@id/end" /> <ConstraintSet android:id="@+id/start"> <Constraint android:id="@id/header_title"> <Layout android:layout_width="match_parent"

    android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginBottom="16dp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintBottom_toBottomOf="parent"/> </Constraint> </ConstraintSet> <ConstraintSet android:id="@+id/end" app:deriveConstraintsFrom="@id/start"> <Constraint android:id="@id/header_title"> <Transform Changing Text in MotionLayout @xml/collapsing_header_scene As a bonus we can delete constraints when a view is using match_parent @kaeawc
  74. <Transition app:constraintSetStart="@id/start" app:constraintSetEnd="@id/end" /> <ConstraintSet android:id="@+id/start"> <Constraint android:id="@id/header_title"> <Layout android:layout_width="match_parent"

    android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginBottom="16dp" app:layout_constraintBottom_toBottomOf="parent"/> </Constraint> </ConstraintSet> <ConstraintSet android:id="@+id/end" app:deriveConstraintsFrom="@id/start"> <Constraint android:id="@id/header_title"> <Transform android:scaleX="0.7" android:scaleY="0.7" Changing Text in MotionLayout @xml/collapsing_header_scene As a bonus we can delete constraints when a view is using match_parent @kaeawc
  75. Changing Constraints Programmatically • MotionLayout is just an extension of

    ConstraintLayout • After making changes to a ConstraintSet we must call motionLayout.updateState(constraintSetId, constraintSet) motionLayout.setTransition(start, end) motionLayout.transitionToEnd() @kaeawc
  76. <ConstraintSet android:id="@+id/grid" /> <ConstraintSet android:id="@+id/zoomed_in" /> constraintSet.connect(viewId, TOP, R.id.boundaries, TOP,

    0) constraintSet.connect(viewId, START, R.id.boundaries, START, 0) constraintSet.connect(viewId, END, R.id.boundaries, END, 0) constraintSet.connect(viewId, BOTTOM, R.id.boundaries, BOTTOM, 0) constraintSet.setElevation(view.id, 1f) motion_layout.updateState(R.id.zoomed_in, constraintSet) Changing Constraints Programmatically PhotoGridActivity.kt @xml/grid_zoom_scene @kaeawc
  77. RecyclerView & MotionLayout Placeholder • Calculate ViewHolder View position •

    Update a placeholder view within the starting ConstraintSet • Show hidden MotionLayout with placeholder @kaeawc
  78. Constraints - Limitations • Some problems cannot be solved with

    derived Constraints. • Creating a very large number of ConstraintSets and Transitions is infeasible. @kaeawc
  79. Constraints - Limitations N!/2 States Each requires a handwritten ConstraintSet

    This 2x2 board has 12 states A 3x3 board would have 181440 states @kaeawc
  80. TabLayout & Many NestedScrollViews MainActivity.kt override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) view_pager?.apply { adapter = MainAdapter(supportFragmentManager) tab_layout?.setupWithViewPager(this) addOnPageChangeListener(object: ViewPager.OnPageChangeListener { override fun onPageScrollStateChanged(state: Int) {} override fun onPageScrolled(...) {} override fun onPageSelected(position: Int) { if (root_layout?.progress != 0f) { root_layout?.transitionToStart() } } }) } } internal fun onVerticalScroll(scroll: Int) { root_layout?.progress = Math.min(scroll / 1000f, 1f) } @kaeawc
  81. Why You Should Try MotionLayout • Reduces context switching •

    Makes some hard animations much easier • Allows us to make animations reentrant, continuous, & smooth @kaeawc
  82. Challenges in using MotionLayout • It’s still in beta •

    Lack of official learning materials • Debugging is a bit of trial and error https://codelabs.developers.google.com/codelabs/motion-layout/ But its so much easier now with MotionEditor @kaeawc