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

ConstraintLayout 2.0 / MotionLayout

ConstraintLayout 2.0 / MotionLayout

Presentation on MotionLayout at 360 AnDev 2018 in Denver

B9012970f22b84c5b344ffa6f8a884d5?s=128

Nicolas Roard

July 20, 2018
Tweet

Transcript

  1. ConstraintLayout 2.0 Let’s use the new things! Nicolas Roard @camaelon

    John Hoford @johnhoford
  2. Let’s use the new things! Nicolas Roard @camaelon John Hoford

    @johnhoford MotionLayout
  3. { 360 | AnDev } 2018 ConstraintLayout 2.0

  4. { 360 | AnDev } 2018 ConstraintLayout 2.0 ★ Layout

  5. { 360 | AnDev } 2018 ConstraintLayout 2.0 ★ Layout

    ★ Helpers
  6. { 360 | AnDev } 2018 ConstraintLayout 2.0 ★ Layout

    ★ Helpers ★ Rendering
  7. { 360 | AnDev } 2018 ConstraintLayout 2.0 ★ Layout

    ★ Helpers ★ Rendering ★ Live Resize
  8. { 360 | AnDev } 2018 ConstraintLayout 2.0 ★ Layout

    ★ Helpers ★ Rendering ★ Live Resize ★ Motion
  9. { 360 | AnDev } 2018 ConstraintLayout 2.0 ★ Layout

    ★ Helpers ★ Rendering ★ Live Resize ★ Motion ★ API
  10. { 360 | AnDev } 2018 ConstraintLayout 2.0 ★ Layout

    ★ Helpers ★ Rendering ★ Live Resize ★ Motion ★ API
  11. { 360 | AnDev } 2018 MotionLayout

  12. { 360 | AnDev } 2018 MotionLayout ConstraintLayout ^

  13. { 360 | AnDev } 2018 MotionLayout: Why ★ Why

    ★ Audience ★ What can it do
  14. { 360 | AnDev } 2018 Animation Types Animated content

    Transition Layouts Motion Property Animation
  15. { 360 | AnDev } 2018 Animation Types Animated content

    Transition Layouts Motion Lottie, Kyrie, Animated Vector Drawable, GIFs, etc. TransitionManager CoordinatorLayout Property Animation Property animation framework (code)
  16. { 360 | AnDev } 2018 MotionLayout Animated content Transition

    Layouts Motion Lottie, Kyrie, Animated Vector Drawable, GIFs, etc. TransitionManager CoordinatorLayout Property Animation Property animation framework (code)
  17. { 360 | AnDev } 2018 It’s Declarative

  18. { 360 | AnDev } 2018 Declarative Approach ★ Better

    & Easier model ★ We can build tooling
  19. { 360 | AnDev } 2018 Declarative Approach ★ Better

    & Easier model ★ We can build tooling
  20. { 360 | AnDev } 2018 Key Concepts ★ Transition

    ★ Events ★ Keyframes ★ Motion
  21. { 360 | AnDev } 2018 Transitions

  22. { 360 | AnDev } 2018 ConstraintSet ★ Encapsulate all

    the rules for a layout ★ You can apply ConstraintSet to an existing layout ★ You can switch between multiple ConstraintSets ★ With TransitionManager, basic animation capabilities
  23. { 360 | AnDev } 2018 Start End Seekable Transition

    Start ConstraintSet End ConstraintSet Interpolation
  24. { 360 | AnDev } 2018

  25. { 360 | AnDev } 2018

  26. <?xml version="1.0" encoding="utf-8"?> <android.support.constraint.motion.MotionLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/motionLayout" app:layoutDescription=“@xml/scene" android:layout_width="match_parent"

    android:layout_height="match_parent"> <View android:id="@+id/button" android:background="@color/colorAccent" android:layout_width="64dp" android:layout_height="64dp"/> </android.support.constraint.motion.MotionLayout>
  27. <?xml version="1.0" encoding="utf-8"?> <android.support.constraint.motion.MotionLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/motionLayout" app:layoutDescription=“@xml/scene" android:layout_width="match_parent"

    android:layout_height="match_parent"> <View android:id="@+id/button" android:background="@color/colorAccent" android:layout_width="64dp" android:layout_height="64dp"/> </android.support.constraint.motion.MotionLayout>
  28. <?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"> <Transition motion:constraintSetStart="@+id/start" motion:constraintSetEnd="@+id/end" motion:duration="1000">

    </Transition> <ConstraintSet android:id="@+id/start"> <Constraint android:id="@+id/button" android:layout_width="64dp" android:layout_height="64dp" android:layout_marginStart="8dp" motion:layout_constraintBottom_toBottomOf="parent" motion:layout_constraintStart_toStartOf="parent" motion:layout_constraintTop_toTopOf="parent" />
  29. <?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"> <Transition motion:constraintSetStart="@+id/start" motion:constraintSetEnd="@+id/end" motion:duration="1000">

    </Transition> <ConstraintSet android:id="@+id/start"> <Constraint android:id="@+id/button" android:layout_width="64dp" android:layout_height="64dp" android:layout_marginStart="8dp" motion:layout_constraintBottom_toBottomOf="parent" motion:layout_constraintStart_toStartOf="parent" motion:layout_constraintTop_toTopOf="parent" />
  30. motion:constraintSetStart="@+id/start" motion:constraintSetEnd="@+id/end" motion:duration="1000"> </Transition> <ConstraintSet android:id="@+id/start"> <Constraint android:id="@+id/button" android:layout_width="64dp" android:layout_height="64dp"

    android:layout_marginStart="8dp" motion:layout_constraintBottom_toBottomOf="parent" motion:layout_constraintStart_toStartOf="parent" motion:layout_constraintTop_toTopOf="parent" /> </ConstraintSet> <ConstraintSet android:id="@+id/end"> <Constraint android:id="@+id/button" android:layout_width="64dp"
  31. motion:constraintSetStart="@+id/start" motion:constraintSetEnd="@+id/end" motion:duration="1000"> </Transition> <ConstraintSet android:id="@+id/start"> <Constraint android:id="@+id/button" android:layout_width="64dp" android:layout_height="64dp"

    android:layout_marginStart="8dp" motion:layout_constraintBottom_toBottomOf="parent" motion:layout_constraintStart_toStartOf="parent" motion:layout_constraintTop_toTopOf="parent" /> </ConstraintSet> <ConstraintSet android:id="@+id/end"> <Constraint android:id="@+id/button" android:layout_width="64dp"
  32. motion:constraintSetStart="@+id/start" motion:constraintSetEnd="@+id/end" motion:duration="1000"> </Transition> <ConstraintSet android:id="@+id/start"> <Constraint android:id="@+id/button" android:layout_width="64dp" android:layout_height="64dp"

    android:layout_marginStart="8dp" motion:layout_constraintBottom_toBottomOf="parent" motion:layout_constraintStart_toStartOf="parent" motion:layout_constraintTop_toTopOf="parent" /> </ConstraintSet> <ConstraintSet android:id="@+id/end"> <Constraint android:id="@+id/button" android:layout_width="64dp"
  33. android:layout_marginStart="8dp" motion:layout_constraintBottom_toBottomOf="parent" motion:layout_constraintStart_toStartOf="parent" motion:layout_constraintTop_toTopOf="parent" /> </ConstraintSet> <ConstraintSet android:id="@+id/end"> <Constraint android:id="@+id/button"

    android:layout_width="64dp" android:layout_height="64dp" android:layout_marginEnd="8dp" motion:layout_constraintBottom_toBottomOf="parent" motion:layout_constraintEnd_toEndOf="parent" motion:layout_constraintTop_toTopOf="parent" /> </ConstraintSet> </MotionScene>
  34. android:layout_marginStart="8dp" motion:layout_constraintBottom_toBottomOf="parent" motion:layout_constraintStart_toStartOf="parent" motion:layout_constraintTop_toTopOf="parent" /> </ConstraintSet> <ConstraintSet android:id="@+id/end"> <Constraint android:id="@+id/button"

    android:layout_width="64dp" android:layout_height="64dp" android:layout_marginEnd="8dp" motion:layout_constraintBottom_toBottomOf="parent" motion:layout_constraintEnd_toEndOf="parent" motion:layout_constraintTop_toTopOf="parent" /> </ConstraintSet> </MotionScene>
  35. android:layout_marginStart="8dp" motion:layout_constraintBottom_toBottomOf="parent" motion:layout_constraintStart_toStartOf="parent" motion:layout_constraintTop_toTopOf="parent" /> </ConstraintSet> <ConstraintSet android:id="@+id/end"> <Constraint android:id="@+id/button"

    android:layout_width="64dp" android:layout_height="64dp" android:layout_marginEnd="8dp" motion:layout_constraintBottom_toBottomOf="parent" motion:layout_constraintEnd_toEndOf="parent" motion:layout_constraintTop_toTopOf="parent" /> </ConstraintSet> </MotionScene>
  36. <?xml version="1.0" encoding="utf-8"?> <android.support.constraint.motion.MotionLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/motionLayout" app:layoutDescription=“@xml/scene" android:layout_width="match_parent"

    android:layout_height="match_parent"> <View android:id="@+id/button" android:background="@color/colorAccent" android:layout_width="64dp" android:layout_height="64dp"/> </android.support.constraint.motion.MotionLayout>
  37. android:layout_height="match_parent"> <View android:id="@+id/button" android:background="@color/colorAccent" android:layout_width="64dp" android:layout_height=“64dp"/> </android.support.constraint.motion.MotionLayout>

  38. android:layout_height="match_parent"> <View android:id="@+id/button" android:background="@color/colorAccent" android:layout_width="64dp" android:layout_height=“64dp"/> </android.support.constraint.motion.MotionLayout> <Button android:id=“@+id/run" android:layout_width="wrap_content"

    android:layout_height="wrap_content" android:layout_marginBottom="8dp" android:layout_marginEnd="8dp" android:text="Run" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" />
  39. class BasicTransitionActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState) setContentView(R.layout.motion) run.setOnClickListener { motionLayout.transitionToEnd() } } }
  40. { 360 | AnDev } 2018

  41. { 360 | AnDev } 2018

  42. <?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"> <Transition motion:constraintSetStart="@+id/start" motion:constraintSetEnd="@+id/end" motion:duration="1000">

    </Transition> <ConstraintSet android:id="@+id/start"> <Constraint android:id="@+id/button" android:layout_width="64dp" android:layout_height="64dp" android:layout_marginStart="8dp" motion:layout_constraintBottom_toBottomOf="parent" motion:layout_constraintStart_toStartOf="parent" motion:layout_constraintTop_toTopOf="parent" />
  43. <?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"> <Transition motion:constraintSetStart="@+id/start" motion:constraintSetEnd="@+id/end" motion:duration="1000">

    </Transition> <ConstraintSet android:id="@+id/start"> <Constraint android:id="@+id/button" android:layout_width="64dp" android:layout_height="64dp" android:layout_marginStart="8dp" motion:layout_constraintBottom_toBottomOf="parent"
  44. <?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"> <Transition motion:constraintSetStart="@+id/start" motion:constraintSetEnd="@+id/end" motion:duration="1000">

    </Transition> <ConstraintSet android:id="@+id/start"> <Constraint android:id="@+id/button" android:layout_width="64dp" android:layout_height="64dp" android:layout_marginStart="8dp" motion:layout_constraintBottom_toBottomOf="parent" <OnClick motion:target=“@id/run"/>
  45. xmlns:motion="http://schemas.android.com/apk/res-auto"> <Transition motion:constraintSetStart="@+id/start" motion:constraintSetEnd="@+id/end" motion:duration="1000"> </Transition> <ConstraintSet android:id="@+id/start"> <Constraint android:id="@+id/button"

    android:layout_width="64dp" android:layout_height="64dp" android:layout_marginStart="8dp"
  46. xmlns:motion="http://schemas.android.com/apk/res-auto"> <Transition motion:constraintSetStart="@+id/start" motion:constraintSetEnd="@+id/end" motion:duration="1000"> </Transition> <ConstraintSet android:id="@+id/start"> <Constraint android:id="@+id/button"

    android:layout_width="64dp" android:layout_height="64dp" android:layout_marginStart="8dp" <OnSwipe motion:dragDirection="dragRight" motion:touchAnchorId="@id/button" motion:touchAnchorSide="right" />
  47. { 360 | AnDev } 2018

  48. { 360 | AnDev } 2018

  49. { 360 | AnDev } 2018 MotionLayout XML attributes ★

    app:layoutDescription=”reference” ★ app:applyMotionScene=”boolean” [ default = true ] ★ app:showPaths=”boolean” [ default = false] ★ app:progress=”float” ★ app:currentState=”reference”
  50. { 360 | AnDev } 2018 Interpolated Attributes ★ Position,

    Dimensions ★ Visibility & Alpha ★ Translation, Rotation, Scale, Elevation ★ Custom attributes
  51. { 360 | AnDev } 2018 Custom Attributes ★ Extension

    to ConstraintSet ★ Define values for any attribute ★ Specify the type ★ Specify the setter name string color integer float dimension boolean
  52. <ConstraintSet ...> <Constraint ...> <CustomAttribute motion:attributeName="backgroundColor" motion:customColorValue="#D81B60" /> </Constraint> </ConstraintSet>

  53. { 360 | AnDev } 2018 Utilities

  54. { 360 | AnDev } 2018 Utilities ★ MockView

  55. { 360 | AnDev } 2018 Utilities ★ MockView

  56. { 360 | AnDev } 2018 Utilities ★ MockView

  57. { 360 | AnDev } 2018 Utilities ★ MockView ★

    ImageFilterView : Contrast, Saturation, Warmth, Crossfade
  58. { 360 | AnDev } 2018

  59. { 360 | AnDev } 2018

  60. <android.support.constraint.motion.MotionLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/motionLayout" app:layoutDescription="@xml/scene_04" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.constraint.utils.ImageFilterView android:id="@+id/image" android:background="@color/colorAccent"

    android:src="@drawable/roard" app:altSrc="@drawable/hoford" android:layout_width="64dp" android:layout_height="64dp"/> </android.support.constraint.motion.MotionLayout>
  61. <MotionScene xmlns:android="http://schemas.android.com/apk/res/android" xmlns:motion="http://schemas.android.com/apk/res-auto"> <Transition motion:constraintSetStart="@+id/start" motion:constraintSetEnd="@+id/end" motion:duration="1000"> <OnSwipe motion:dragDirection="dragRight" motion:touchAnchorId="@id/image"

    motion:touchAnchorSide="right" /> </Transition> <ConstraintSet android:id="@+id/start"> <Constraint android:id="@+id/image" android:layout_width="100dp" android:layout_height="100dp" android:layout_marginStart="8dp"
  62. motion:touchAnchorSide="right" /> </Transition> <ConstraintSet android:id="@+id/start"> <Constraint android:id="@+id/image" android:layout_width="100dp" android:layout_height="100dp" android:layout_marginStart="8dp"

    motion:layout_constraintBottom_toBottomOf="parent" motion:layout_constraintStart_toStartOf="parent" motion:layout_constraintTop_toTopOf="parent"> <CustomAttribute motion:attributeName="Crossfade" motion:customFloatValue="0" /> </Constraint> </ConstraintSet> <ConstraintSet android:id="@+id/end"> <Constraint
  63. motion:touchAnchorSide="right" /> </Transition> <ConstraintSet android:id="@+id/start"> <Constraint android:id="@+id/image" android:layout_width="100dp" android:layout_height="100dp" android:layout_marginStart="8dp"

    motion:layout_constraintBottom_toBottomOf="parent" motion:layout_constraintStart_toStartOf="parent" motion:layout_constraintTop_toTopOf="parent"> <CustomAttribute motion:attributeName="Crossfade" motion:customFloatValue="0" /> </Constraint> </ConstraintSet> <ConstraintSet android:id="@+id/end"> <Constraint
  64. </Constraint> </ConstraintSet> <ConstraintSet android:id="@+id/end"> <Constraint android:id="@id/image" android:layout_width="100dp" android:layout_height="100dp" android:layout_marginEnd="8dp" motion:layout_constraintBottom_toBottomOf="parent"

    motion:layout_constraintEnd_toEndOf="parent" motion:layout_constraintTop_toTopOf="parent"> <CustomAttribute motion:attributeName="Crossfade" motion:customFloatValue="1" /> </Constraint> </ConstraintSet> </MotionScene>
  65. </Constraint> </ConstraintSet> <ConstraintSet android:id="@+id/end"> <Constraint android:id="@id/image" android:layout_width="100dp" android:layout_height="100dp" android:layout_marginEnd="8dp" motion:layout_constraintBottom_toBottomOf="parent"

    motion:layout_constraintEnd_toEndOf="parent" motion:layout_constraintTop_toTopOf="parent"> <CustomAttribute motion:attributeName="Crossfade" motion:customFloatValue="1" /> </Constraint> </ConstraintSet> </MotionScene>
  66. { 360 | AnDev } 2018 Transition API ★ transitionToEnd()

    ★ transitionToStart() ★ transitionToState(int state) ★ setProgress(float value) [0 .. 1]
  67. { 360 | AnDev } 2018 Events

  68. { 360 | AnDev } 2018 Events ★ TransitionListener ★

    OnClick ★ OnSwipe
  69. { 360 | AnDev } 2018 TransitionListener

  70. public interface TransitionListener { void onTransitionChange(MotionLayout motionLayout, int startId, int

    endId, float progress); void onTransitionCompleted(MotionLayout motionLayout, int currentId); }
  71. { 360 | AnDev } 2018 OnSwipe

  72. <OnSwipe motion:dragDirection="dragUp" motion:touchAnchorId="@+id/widget" motion:touchAnchorSide=“top" />

  73. <OnSwipe motion:dragDirection="dragUp" motion:touchAnchorId="@+id/widget" motion:touchAnchorSide=“top" />

  74. dragDirection dragUp dragDown dragRight dragLeft touchAnchorId touchAnchorSide top bottom left

    right
  75. OnSwipe <OnSwipe motion:dragDirection="dragUp" motion:touchAnchorId="@+id/widget" motion:touchAnchorSide=“top" /> Create your animation OnSwipe

    “plays” it
  76. OnSwipe <OnSwipe motion:dragDirection="dragUp" motion:touchAnchorId="@+id/widget" motion:touchAnchorSide=“top" /> Create your animation OnSwipe

    “plays” it
  77. OnSwipe: Top touchAnchorSide : TOP

  78. OnSwipe: Top touchAnchorSide : TOP

  79. OnSwipe: Right touchAnchorSide : RIGHT

  80. OnSwipe: Right touchAnchorSide : RIGHT

  81. OnSwipe: Bottom touchAnchorSide : BOTTOM

  82. OnSwipe: Bottom touchAnchorSide : BOTTOM

  83. OnSwipe: Left touchAnchorSide : LEFT

  84. OnSwipe: Left touchAnchorSide : LEFT

  85. Touch Up / Release Takes in account velocity Choose a

    continuous velocity curve to reach the endpoints with a velocity of zero
  86. Touch Up / Release Takes in account velocity Choose a

    continuous velocity curve to reach the endpoints with a velocity of zero
  87. { 360 | AnDev } 2018 OnClick

  88. <OnClick motion:target="@+id/run" /> mode • transitionToEnd • toggle • transitionToStart

    • jumpToEnd • jumpToStart OnClick
  89. <OnClick motion:target="@+id/run" /> mode • transitionToEnd • toggle • transitionToStart

    • jumpToEnd • jumpToStart OnClick
  90. { 360 | AnDev } 2018 Keyframes

  91. { 360 | AnDev } 2018 Keyframe a rendez-vous in

    time
  92. { 360 | AnDev } 2018 Keyframes ★ Position ★

    Attributes ★ Cycles ★ …and more
  93. { 360 | AnDev } 2018 Position

  94. KeyFrameSet <KeyFrameSet> </KeyFrameSet>

  95. KeyFrameSet <KeyFrameSet> </KeyFrameSet>

  96. KeyFrameSet <KeyFrameSet> <KeyPosition motion:target="@+id/widget" motion:framePosition="50" motion:percentX=“1.0” /> </KeyFrameSet>

  97. KeyFrameSet <KeyFrameSet> <KeyPosition motion:target="@+id/widget" motion:framePosition="50" motion:percentX=“1.0” /> </KeyFrameSet>

  98. KeyFrameSet <KeyFrameSet> <KeyPosition motion:target="@+id/widget" motion:framePosition="50" motion:percentX=“1.0” motion:sizePercent=“0.0" /> </KeyFrameSet>

  99. KeyFrameSet <KeyFrameSet> <KeyPosition motion:target="@+id/widget" motion:framePosition="50" motion:percentX=“1.0” motion:sizePercent=“0.0" /> </KeyFrameSet>

  100. KeyFrameSet <KeyFrameSet> <KeyPosition motion:target="@+id/widget" motion:framePosition="50" motion:percentX=“1.0” motion:percentY=“0.3” motion:sizePercent=“0.0" /> </KeyFrameSet>

  101. KeyFrameSet <KeyFrameSet> <KeyPosition motion:target="@+id/widget" motion:framePosition="50" motion:percentX=“1.0” motion:percentY=“0.3” motion:sizePercent=“0.0" /> </KeyFrameSet>

  102. { 360 | AnDev } 2018 Coordinate Systems

  103. { 360 | AnDev } 2018

  104. { 360 | AnDev } 2018

  105. { 360 | AnDev } 2018 Start End

  106. { 360 | AnDev } 2018 parentRelative 0 X Y

  107. { 360 | AnDev } 2018 deltaRelative Y X

  108. { 360 | AnDev } 2018 pathRelative X Y

  109. { 360 | AnDev } 2018 Attributes

  110. KeyFrameSet <KeyFrameSet> <KeyPosition… /> <KeyAttributes motion:target="@+id/widget" motion:framePosition="25" android:rotation=“45” /> </KeyFrameSet>

  111. KeyFrameSet <KeyFrameSet> <KeyPosition… /> <KeyAttributes motion:target="@+id/widget" motion:framePosition="25" android:rotation=“45” /> </KeyFrameSet>

  112. KeyFrameSet <KeyFrameSet> <KeyPosition… /> <KeyAttributes motion:target="@+id/widget" motion:framePosition="25" android:rotation=“45”/> <KeyAttributes motion:target="@+id/widget"

    motion:framePosition="50" android:rotation=“0”/> </KeyFrameSet>
  113. KeyFrameSet <KeyFrameSet> <KeyPosition… /> <KeyAttributes motion:target="@+id/widget" motion:framePosition="25" android:rotation=“45”/> <KeyAttributes motion:target="@+id/widget"

    motion:framePosition="50" android:rotation=“0”/> </KeyFrameSet>
  114. KeyAttributes support motion:transitionEasing motion:curveFit android:visibility android:alpha android:elevation android:rotation android:rotationX android:rotationY

    android:scaleX android:scaleY android:translationX android:translationY android:translationZ motion:transitionPathRotate motion:progress + Custom Attributes <CustomAttribute motion:attributeName="BackgroundColor" motion:customColorValue="#D81B60"/>
  115. { 360 | AnDev } 2018 Cycles

  116. KeyCycle <KeyFrameSet> <KeyPosition…/> <KeyAttributes…/> <KeyAttributes…/> <KeyCycle motion:target="@+id/widget" motion:framePosition="0" motion:wavePeriod=“0” motion:translationX=“0.0"

    /> <KeyCycle motion:target="@+id/widget" motion:framePosition="50" motion:wavePeriod=“50” motion:translationX=“30dp" /> </KeyFrameSet>
  117. KeyCycle <KeyFrameSet> <KeyPosition…/> <KeyAttributes…/> <KeyAttributes…/> <KeyCycle motion:target="@+id/widget" motion:framePosition="0" motion:wavePeriod=“0” motion:translationX=“0.0"

    /> <KeyCycle motion:target="@+id/widget" motion:framePosition="50" motion:wavePeriod=“50” motion:translationX=“30dp" /> </KeyFrameSet>
  118. { 360 | AnDev } 2018 Code KeyCycle Value (attributes)

  119. { 360 | AnDev } 2018 Code KeyCycle Period

  120. KeyCycle <KeyFrameSet> <KeyPosition…/> <KeyAttributes…/> <KeyAttributes…/> <KeyCycle motion:target="@+id/widget" motion:framePosition="0" motion:wavePeriod=“0” motion:translationX=“0.0"

    /> <KeyCycle motion:target="@+id/widget" motion:framePosition="50" motion:wavePeriod=“50” motion:translationX=“30dp" /> </KeyFrameSet>
  121. KeyCycle <KeyFrameSet> <KeyPosition…/> <KeyAttributes…/> <KeyAttributes…/> <KeyCycle motion:target="@+id/widget" motion:framePosition="0" motion:wavePeriod=“0” motion:translationX=“0.0"

    /> <KeyCycle motion:target="@+id/widget" motion:framePosition="50" motion:wavePeriod=“50” motion:translationX=“30dp" /> </KeyFrameSet>
  122. { 360 | AnDev } 2018 Motion

  123. { 360 | AnDev } 2018 Motion ★ CoordinatorLayout ★

    DrawerLayout ★ ViewPager ★ MotionLayout
  124. { 360 | AnDev } 2018 motion:progress

  125. { 360 | AnDev } 2018 Collapsible Toolbar (CoordinatorLayout)

  126. { 360 | AnDev } 2018

  127. { 360 | AnDev } 2018

  128. { 360 | AnDev } 2018 Create CollapsibleToolbar 0

  129. class CollapsibleToolbar @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null,

    defStyleAttr: Int = 0 ) : MotionLayout(context, attrs, defStyleAttr), AppBarLayout.OnOffsetChangedListener { override fun onOffsetChanged(appBarLayout: AppBarLayout?, verticalOffset: Int) { progress = -verticalOffset / appBarLayout?.totalScrollRange?.toFloat()!! } override fun onAttachedToWindow() { super.onAttachedToWindow() (parent as? AppBarLayout)?.addOnOffsetChangedListener(this) } }
  130. class CollapsibleToolbar @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null,

    defStyleAttr: Int = 0 ) : MotionLayout(context, attrs, defStyleAttr), AppBarLayout.OnOffsetChangedListener { override fun onOffsetChanged(appBarLayout: AppBarLayout?, verticalOffset: Int) { progress = -verticalOffset / appBarLayout?.totalScrollRange?.toFloat()!! } override fun onAttachedToWindow() { super.onAttachedToWindow() (parent as? AppBarLayout)?.addOnOffsetChangedListener(this) } }
  131. { 360 | AnDev } 2018 Replace CollapsingToolbarLayout 1

  132. <android.support.design.widget.CoordinatorLayout android:id="@+id/content" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="false" android:background="@color/contentBackground"> <android.support.design.widget.AppBarLayout android:id="@+id/app_bar" android:layout_width="match_parent" android:layout_height="@dimen/app_bar_height">

    <android.support.design.widget.CollapsingToolbarLayout … /> </android.support.design.widget.AppBarLayout> <include layout="@layout/content_scrolling" /> </android.support.design.widget.CoordinatorLayout>
  133. <android.support.design.widget.CoordinatorLayout android:id="@+id/content" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="false" android:background="@color/contentBackground"> <android.support.design.widget.AppBarLayout android:id="@+id/app_bar" android:layout_width="match_parent" android:layout_height="@dimen/app_bar_height">

    <CollapsibleToolbar … /> </android.support.design.widget.AppBarLayout> <include layout="@layout/content_scrolling" /> </android.support.design.widget.CoordinatorLayout>
  134. { 360 | AnDev } 2018 Create a layout for

    the header 2
  135. <?xml version="1.0" encoding="utf-8"?> <com.google.androidstudio.motionlayoutexample.utils.CollapsibleToolbar xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/motionLayout" app:layoutDescription="@xml/scene_09" android:layout_width="match_parent"

    android:layout_height="match_parent" android:minHeight="50dp" android:fitsSystemWindows="false" app:layout_scrollFlags="scroll|enterAlways|snap|exitUntilCollapsed"> <ImageView android:id="@+id/background" android:layout_width="match_parent" android:layout_height="200dp" android:background="@color/colorAccent" android:scaleType="centerCrop"
  136. <ImageView android:id="@+id/background" android:layout_width="match_parent" android:layout_height="200dp" android:background="@color/colorAccent" android:scaleType="centerCrop" android:src="@drawable/monterey"/> <TextView android:id="@+id/label" android:layout_width="wrap_content"

    android:layout_height="wrap_content" android:transformPivotX="0dp" android:transformPivotY="0dp" android:text="Monterey" android:textColor="#FFF" android:textSize="32dp" /> </com.google.androidstudio.motionlayoutexample.utils.CollapsibleToolbar>
  137. { 360 | AnDev } 2018 Create a MotionScene 3

  138. <?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"> <Transition motion:constraintSetStart="@+id/start" motion:constraintSetEnd="@+id/end" />

    <ConstraintSet android:id="@+id/start"> <Constraint android:id="@+id/background" android:layout_width="match_parent" android:layout_height="match_parent" android:alpha="1.0" motion:layout_constraintBottom_toBottomOf="parent"/> <Constraint android:id="@+id/label" android:layout_width="wrap_content"
  139. <ConstraintSet android:id="@+id/start"> <Constraint android:id="@+id/background" android:layout_width="match_parent" android:layout_height="match_parent" android:alpha="1.0" motion:layout_constraintBottom_toBottomOf="parent"/> <Constraint android:id="@+id/label"

    android:layout_width="wrap_content" android:layout_height="wrap_content" android:rotation="-90.0" motion:layout_constraintBottom_toBottomOf="@+id/background" motion:layout_constraintStart_toStartOf="parent"/> </ConstraintSet> <ConstraintSet android:id="@+id/end"> <Constraint android:id="@+id/background"
  140. <ConstraintSet android:id="@+id/end"> <Constraint android:id="@+id/background" android:layout_width="match_parent" android:layout_height="match_parent" android:alpha="0.2" motion:layout_constraintBottom_toBottomOf="parent"/> <Constraint android:id="@+id/label"

    android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:layout_marginBottom="8dp" android:rotation="0.0" motion:layout_constraintBottom_toBottomOf="@+id/background" motion:layout_constraintStart_toStartOf="parent" /> </ConstraintSet> </MotionScene>
  141. None
  142. None
  143. { 360 | AnDev } 2018 DrawerLayout

  144. { 360 | AnDev } 2018

  145. { 360 | AnDev } 2018

  146. class DrawerContent @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null,

    defStyleAttr: Int = 0 ) : MotionLayout(context, attrs, defStyleAttr), DrawerLayout.DrawerListener { override fun onDrawerStateChanged(newState: Int) { } override fun onDrawerSlide(drawerView: View, slideOffset: Float) { progress = slideOffset } override fun onDrawerClosed(drawerView: View) { } override fun onDrawerOpened(drawerView: View) { } override fun onAttachedToWindow() { super.onAttachedToWindow() (parent as? DrawerLayout)?.addDrawerListener(this) } }
  147. class DrawerContent @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null,

    defStyleAttr: Int = 0 ) : MotionLayout(context, attrs, defStyleAttr), DrawerLayout.DrawerListener { override fun onDrawerStateChanged(newState: Int) { } override fun onDrawerSlide(drawerView: View, slideOffset: Float) { progress = slideOffset } override fun onDrawerClosed(drawerView: View) { } override fun onDrawerOpened(drawerView: View) { } override fun onAttachedToWindow() { super.onAttachedToWindow() (parent as? DrawerLayout)?.addDrawerListener(this) } }
  148. class DrawerContent @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null,

    defStyleAttr: Int = 0 ) : MotionLayout(context, attrs, defStyleAttr), DrawerLayout.DrawerListener { override fun onDrawerStateChanged(newState: Int) { } override fun onDrawerSlide(drawerView: View, slideOffset: Float) { progress = slideOffset } override fun onDrawerClosed(drawerView: View) { } override fun onDrawerOpened(drawerView: View) { } override fun onAttachedToWindow() { super.onAttachedToWindow() (parent as? DrawerLayout)?.addDrawerListener(this) } }
  149. { 360 | AnDev } 2018 ViewPager

  150. { 360 | AnDev } 2018

  151. { 360 | AnDev } 2018

  152. class ViewpagerHeader @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null,

    defStyleAttr: Int = 0 ) : MotionLayout(context, attrs, defStyleAttr), ViewPager.OnPageChangeListener { override fun onPageScrollStateChanged(state: Int) {} override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { var numPages = 3 progress = (position + positionOffset) / (numPages - 1) } override fun onPageSelected(position: Int) {} }
  153. { 360 | AnDev } 2018 MotionLayout

  154. { 360 | AnDev } 2018

  155. { 360 | AnDev } 2018

  156. { 360 | AnDev } 2018

  157. { 360 | AnDev } 2018

  158. { 360 | AnDev } 2018

  159. <android.support.constraint.motion.MotionLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto" app:layoutDescription="@xml/scene_19"> <include layout="@layout/motion_19_coordination_header" /> <include

    layout="@layout/content_scrolling" /> <android.support.design.widget.FloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_add_a_photo_24dp"/> </android.support.constraint.motion.MotionLayout>
  160. <MotionScene xmlns:android="http://schemas.android.com/apk/res/android" xmlns:motion="http://schemas.android.com/apk/res-auto"> <Transition motion:constraintSetEnd="@+id/end" motion:constraintSetStart="@+id/start" motion:duration="250" motion:interpolator="linear"> <OnSwipe motion:touchAnchorId="@+id/motionLayout"

    motion:touchAnchorSide="bottom" motion:dragDirection="dragUp" /> <ConstraintSet android:id="@+id/start"> <Constraint android:id="@id/motionLayout" android:layout_width="match_parent" android:layout_height="200dp"
  161. motion:touchAnchorSide="bottom" motion:dragDirection="dragUp" /> <ConstraintSet android:id="@+id/start"> <Constraint android:id="@id/motionLayout" android:layout_width="match_parent" android:layout_height="200dp" motion:layout_constraintTop_toTopOf="parent"

    /> <Constraint android:id="@id/scrollable" android:layout_width="match_parent" android:layout_height="0dp" motion:layout_constraintBottom_toBottomOf="parent" motion:layout_constraintTop_toBottomOf="@+id/motionLayout" /> <Constraint android:id="@id/fab" android:layout_width="wrap_content"
  162. <Constraint android:id="@id/scrollable" android:layout_width="match_parent" android:layout_height="0dp" motion:layout_constraintBottom_toBottomOf="parent" motion:layout_constraintTop_toBottomOf="@+id/motionLayout" /> <Constraint android:id="@id/fab" android:layout_width="wrap_content"

    android:layout_height="wrap_content" android:visibility="invisible" motion:layout_constraintTop_toBottomOf="parent" motion:layout_constraintRight_toRightOf="parent" android:layout_marginTop="8dp" android:layout_marginRight="8dp"/> </ConstraintSet>
  163. <Constraint android:id="@id/scrollable" android:layout_width="match_parent" android:layout_height="0dp" motion:layout_constraintBottom_toBottomOf="parent" motion:layout_constraintTop_toBottomOf="@+id/motionLayout" /> <Constraint android:id="@id/fab" android:layout_width="wrap_content"

    android:layout_height="wrap_content" android:visibility="invisible" motion:layout_constraintTop_toBottomOf="parent" motion:layout_constraintRight_toRightOf="parent" android:layout_marginTop="8dp" android:layout_marginRight="8dp"/> </ConstraintSet>
  164. <Constraint android:id="@id/scrollable" android:layout_width="match_parent" android:layout_height="0dp" motion:layout_constraintBottom_toBottomOf="parent" motion:layout_constraintTop_toBottomOf="@+id/motionLayout" /> <Constraint android:id="@id/fab" android:layout_width="wrap_content"

    android:layout_height="wrap_content" android:visibility="invisible" motion:layout_constraintTop_toBottomOf="parent" motion:layout_constraintRight_toRightOf="parent" android:layout_marginTop="8dp" android:layout_marginRight="8dp"/> </ConstraintSet>
  165. <ConstraintSet android:id="@+id/end"> <Constraint android:id="@id/motionLayout" android:layout_width="match_parent" android:layout_height="56dp" motion:layout_constraintTop_toTopOf="parent" motion:progress="1" /> <Constraint

    android:id="@id/scrollable" android:layout_width="match_parent" android:layout_height="0dp" motion:layout_constraintBottom_toBottomOf="parent" motion:layout_constraintTop_toBottomOf="@+id/motionLayout" /> <Constraint android:id="@id/fab" android:layout_width="wrap_content" android:layout_height="wrap_content"
  166. <Constraint android:id="@id/scrollable" android:layout_width="match_parent" android:layout_height="0dp" motion:layout_constraintBottom_toBottomOf="parent" motion:layout_constraintTop_toBottomOf="@+id/motionLayout" /> <Constraint android:id="@id/fab" android:layout_width="wrap_content"

    android:layout_height="wrap_content" android:visibility="visible" motion:layout_constraintBottom_toBottomOf="parent" motion:layout_constraintRight_toRightOf="parent" android:layout_marginBottom="8dp" android:layout_marginRight="8dp" /> </ConstraintSet> </Transition>
  167. <Constraint android:id="@id/scrollable" android:layout_width="match_parent" android:layout_height="0dp" motion:layout_constraintBottom_toBottomOf="parent" motion:layout_constraintTop_toBottomOf="@+id/motionLayout" /> <Constraint android:id="@id/fab" android:layout_width="wrap_content"

    android:layout_height="wrap_content" android:visibility="visible" motion:layout_constraintBottom_toBottomOf="parent" motion:layout_constraintRight_toRightOf="parent" android:layout_marginBottom="8dp" android:layout_marginRight="8dp" /> </ConstraintSet> </Transition>
  168. <Constraint android:id="@id/scrollable" android:layout_width="match_parent" android:layout_height="0dp" motion:layout_constraintBottom_toBottomOf="parent" motion:layout_constraintTop_toBottomOf="@+id/motionLayout" /> <Constraint android:id="@id/fab" android:layout_width="wrap_content"

    android:layout_height="wrap_content" android:visibility="visible" motion:layout_constraintBottom_toBottomOf="parent" motion:layout_constraintRight_toRightOf="parent" android:layout_marginBottom="8dp" android:layout_marginRight="8dp" /> </ConstraintSet> </Transition>
  169. { 360 | AnDev } 2018

  170. { 360 | AnDev } 2018

  171. { 360 | AnDev } 2018 Documentation ★ Online Documentation:

    https://developer.android.com/reference/android/ support/constraint/motion/package-summary ★ Medium Articles: ★ Introduction to MotionLayout - Part I : http://bit.ly/2O4AmIz ★ Introduction to MotionLayout - Part II : http://bit.ly/2uPuWbw ★ Introduction to MotionLayout - Part III : http://bit.ly/2zRjCSj ★ https://github.com/googlesamples/android-ConstraintLayoutExamples
  172. Thanks! Nicolas Roard @camaelon John Hoford @johnhoford