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

Nicolas Roard

July 20, 2018
Tweet

More Decks by Nicolas Roard

Other Decks in Programming

Transcript

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

    View full-size slide

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

    View full-size slide

  3. { 360 | AnDev } 2018
    ConstraintLayout 2.0

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  11. { 360 | AnDev } 2018
    MotionLayout

    View full-size slide

  12. { 360 | AnDev } 2018
    MotionLayout
    ConstraintLayout
    ^

    View full-size slide

  13. { 360 | AnDev } 2018
    MotionLayout: Why
    ★ Why
    ★ Audience
    ★ What can it do

    View full-size slide

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

    View full-size slide

  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)

    View full-size slide

  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)

    View full-size slide

  17. { 360 | AnDev } 2018
    It’s Declarative

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  20. { 360 | AnDev } 2018
    Key Concepts
    ★ Transition
    ★ Events
    ★ Keyframes
    ★ Motion

    View full-size slide

  21. { 360 | AnDev } 2018
    Transitions

    View full-size slide

  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

    View full-size slide

  23. { 360 | AnDev } 2018
    Start End
    Seekable Transition
    Start
    ConstraintSet
    End
    ConstraintSet
    Interpolation

    View full-size slide

  24. { 360 | AnDev } 2018

    View full-size slide

  25. { 360 | AnDev } 2018

    View full-size slide


  26. 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">
    android:id="@+id/button"
    android:background="@color/colorAccent"
    android:layout_width="64dp"
    android:layout_height="64dp"/>

    View full-size slide


  27. 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">
    android:id="@+id/button"
    android:background="@color/colorAccent"
    android:layout_width="64dp"
    android:layout_height="64dp"/>

    View full-size slide


  28. xmlns:motion="http://schemas.android.com/apk/res-auto">
    motion:constraintSetStart="@+id/start"
    motion:constraintSetEnd="@+id/end"
    motion:duration="1000">


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

    View full-size slide


  29. xmlns:motion="http://schemas.android.com/apk/res-auto">
    motion:constraintSetStart="@+id/start"
    motion:constraintSetEnd="@+id/end"
    motion:duration="1000">


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

    View full-size slide

  30. motion:constraintSetStart="@+id/start"
    motion:constraintSetEnd="@+id/end"
    motion:duration="1000">


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


    android:id="@+id/button"
    android:layout_width="64dp"

    View full-size slide

  31. motion:constraintSetStart="@+id/start"
    motion:constraintSetEnd="@+id/end"
    motion:duration="1000">


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


    android:id="@+id/button"
    android:layout_width="64dp"

    View full-size slide

  32. motion:constraintSetStart="@+id/start"
    motion:constraintSetEnd="@+id/end"
    motion:duration="1000">


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


    android:id="@+id/button"
    android:layout_width="64dp"

    View full-size slide

  33. android:layout_marginStart="8dp"
    motion:layout_constraintBottom_toBottomOf="parent"
    motion:layout_constraintStart_toStartOf="parent"
    motion:layout_constraintTop_toTopOf="parent" />


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


    View full-size slide

  34. android:layout_marginStart="8dp"
    motion:layout_constraintBottom_toBottomOf="parent"
    motion:layout_constraintStart_toStartOf="parent"
    motion:layout_constraintTop_toTopOf="parent" />


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


    View full-size slide

  35. android:layout_marginStart="8dp"
    motion:layout_constraintBottom_toBottomOf="parent"
    motion:layout_constraintStart_toStartOf="parent"
    motion:layout_constraintTop_toTopOf="parent" />


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


    View full-size slide


  36. 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">
    android:id="@+id/button"
    android:background="@color/colorAccent"
    android:layout_width="64dp"
    android:layout_height="64dp"/>

    View full-size slide

  37. android:layout_height="match_parent">
    android:id="@+id/button"
    android:background="@color/colorAccent"
    android:layout_width="64dp"
    android:layout_height=“64dp"/>

    View full-size slide

  38. android:layout_height="match_parent">
    android:id="@+id/button"
    android:background="@color/colorAccent"
    android:layout_width="64dp"
    android:layout_height=“64dp"/>

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

    View full-size slide

  39. class BasicTransitionActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.motion)
    run.setOnClickListener {
    motionLayout.transitionToEnd()
    }
    }
    }

    View full-size slide

  40. { 360 | AnDev } 2018

    View full-size slide

  41. { 360 | AnDev } 2018

    View full-size slide


  42. xmlns:motion="http://schemas.android.com/apk/res-auto">
    motion:constraintSetStart="@+id/start"
    motion:constraintSetEnd="@+id/end"
    motion:duration="1000">


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

    View full-size slide


  43. xmlns:motion="http://schemas.android.com/apk/res-auto">
    motion:constraintSetStart="@+id/start"
    motion:constraintSetEnd="@+id/end"
    motion:duration="1000">


    android:id="@+id/button"
    android:layout_width="64dp"
    android:layout_height="64dp"
    android:layout_marginStart="8dp"
    motion:layout_constraintBottom_toBottomOf="parent"

    View full-size slide


  44. xmlns:motion="http://schemas.android.com/apk/res-auto">
    motion:constraintSetStart="@+id/start"
    motion:constraintSetEnd="@+id/end"
    motion:duration="1000">


    android:id="@+id/button"
    android:layout_width="64dp"
    android:layout_height="64dp"
    android:layout_marginStart="8dp"
    motion:layout_constraintBottom_toBottomOf="parent"
    motion:target=“@id/run"/>

    View full-size slide

  45. xmlns:motion="http://schemas.android.com/apk/res-auto">
    motion:constraintSetStart="@+id/start"
    motion:constraintSetEnd="@+id/end"
    motion:duration="1000">


    android:id="@+id/button"
    android:layout_width="64dp"
    android:layout_height="64dp"
    android:layout_marginStart="8dp"

    View full-size slide

  46. xmlns:motion="http://schemas.android.com/apk/res-auto">
    motion:constraintSetStart="@+id/start"
    motion:constraintSetEnd="@+id/end"
    motion:duration="1000">


    android:id="@+id/button"
    android:layout_width="64dp"
    android:layout_height="64dp"
    android:layout_marginStart="8dp"
    motion:dragDirection="dragRight"
    motion:touchAnchorId="@id/button"
    motion:touchAnchorSide="right" />

    View full-size slide

  47. { 360 | AnDev } 2018

    View full-size slide

  48. { 360 | AnDev } 2018

    View full-size slide

  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”

    View full-size slide

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

    View full-size slide

  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

    View full-size slide



  52. motion:attributeName="backgroundColor"
    motion:customColorValue="#D81B60" />


    View full-size slide

  53. { 360 | AnDev } 2018
    Utilities

    View full-size slide

  54. { 360 | AnDev } 2018
    Utilities
    ★ MockView

    View full-size slide

  55. { 360 | AnDev } 2018
    Utilities
    ★ MockView

    View full-size slide

  56. { 360 | AnDev } 2018
    Utilities
    ★ MockView

    View full-size slide

  57. { 360 | AnDev } 2018
    Utilities
    ★ MockView
    ★ ImageFilterView : Contrast, Saturation, Warmth, Crossfade

    View full-size slide

  58. { 360 | AnDev } 2018

    View full-size slide

  59. { 360 | AnDev } 2018

    View full-size slide

  60. 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:id="@+id/image"
    android:background="@color/colorAccent"
    android:src="@drawable/roard"
    app:altSrc="@drawable/hoford"
    android:layout_width="64dp"
    android:layout_height="64dp"/>

    View full-size slide

  61. xmlns:motion="http://schemas.android.com/apk/res-auto">
    motion:constraintSetStart="@+id/start"
    motion:constraintSetEnd="@+id/end"
    motion:duration="1000">
    motion:dragDirection="dragRight"
    motion:touchAnchorId="@id/image"
    motion:touchAnchorSide="right" />


    android:id="@+id/image"
    android:layout_width="100dp"
    android:layout_height="100dp"
    android:layout_marginStart="8dp"

    View full-size slide

  62. motion:touchAnchorSide="right" />


    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">
    motion:attributeName="Crossfade"
    motion:customFloatValue="0" />



    View full-size slide

  63. motion:touchAnchorSide="right" />


    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">
    motion:attributeName="Crossfade"
    motion:customFloatValue="0" />



    View full-size slide




  64. 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">
    motion:attributeName="Crossfade"
    motion:customFloatValue="1" />



    View full-size slide




  65. 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">
    motion:attributeName="Crossfade"
    motion:customFloatValue="1" />



    View full-size slide

  66. { 360 | AnDev } 2018
    Transition API
    ★ transitionToEnd()
    ★ transitionToStart()
    ★ transitionToState(int state)
    ★ setProgress(float value) [0 .. 1]

    View full-size slide

  67. { 360 | AnDev } 2018
    Events

    View full-size slide

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

    View full-size slide

  69. { 360 | AnDev } 2018
    TransitionListener

    View full-size slide

  70. public interface TransitionListener {
    void onTransitionChange(MotionLayout motionLayout,
    int startId, int endId,
    float progress);
    void onTransitionCompleted(MotionLayout motionLayout,
    int currentId);
    }

    View full-size slide

  71. { 360 | AnDev } 2018
    OnSwipe

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  77. OnSwipe: Top
    touchAnchorSide : TOP

    View full-size slide

  78. OnSwipe: Top
    touchAnchorSide : TOP

    View full-size slide

  79. OnSwipe: Right
    touchAnchorSide : RIGHT

    View full-size slide

  80. OnSwipe: Right
    touchAnchorSide : RIGHT

    View full-size slide

  81. OnSwipe: Bottom
    touchAnchorSide : BOTTOM

    View full-size slide

  82. OnSwipe: Bottom
    touchAnchorSide : BOTTOM

    View full-size slide

  83. OnSwipe: Left
    touchAnchorSide : LEFT

    View full-size slide

  84. OnSwipe: Left
    touchAnchorSide : LEFT

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  87. { 360 | AnDev } 2018
    OnClick

    View full-size slide


  88. mode

    • transitionToEnd
    • toggle
    • transitionToStart
    • jumpToEnd
    • jumpToStart
    OnClick

    View full-size slide


  89. mode

    • transitionToEnd
    • toggle
    • transitionToStart
    • jumpToEnd
    • jumpToStart
    OnClick

    View full-size slide

  90. { 360 | AnDev } 2018
    Keyframes

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  93. { 360 | AnDev } 2018
    Position

    View full-size slide

  94. KeyFrameSet

    motion:target="@+id/widget"
    motion:framePosition="50"
    motion:percentX=“1.0” />

    View full-size slide

  95. KeyFrameSet

    motion:target="@+id/widget"
    motion:framePosition="50"
    motion:percentX=“1.0” />

    View full-size slide

  96. KeyFrameSet

    motion:target="@+id/widget"
    motion:framePosition="50"
    motion:percentX=“1.0”
    motion:sizePercent=“0.0"
    />

    View full-size slide

  97. KeyFrameSet

    motion:target="@+id/widget"
    motion:framePosition="50"
    motion:percentX=“1.0”
    motion:sizePercent=“0.0"
    />

    View full-size slide

  98. KeyFrameSet

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

    View full-size slide

  99. KeyFrameSet

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

    View full-size slide

  100. { 360 | AnDev } 2018
    Coordinate Systems

    View full-size slide

  101. { 360 | AnDev } 2018

    View full-size slide

  102. { 360 | AnDev } 2018

    View full-size slide

  103. { 360 | AnDev } 2018
    Start
    End

    View full-size slide

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

    View full-size slide

  105. { 360 | AnDev } 2018
    deltaRelative
    Y
    X

    View full-size slide

  106. { 360 | AnDev } 2018
    pathRelative
    X
    Y

    View full-size slide

  107. { 360 | AnDev } 2018
    Attributes

    View full-size slide

  108. KeyFrameSet


    motion:target="@+id/widget"
    motion:framePosition="25"
    android:rotation=“45”
    />

    View full-size slide

  109. KeyFrameSet


    motion:target="@+id/widget"
    motion:framePosition="25"
    android:rotation=“45”
    />

    View full-size slide

  110. KeyFrameSet


    motion:target="@+id/widget"
    motion:framePosition="25"
    android:rotation=“45”/>
    motion:target="@+id/widget"
    motion:framePosition="50"
    android:rotation=“0”/>

    View full-size slide

  111. KeyFrameSet


    motion:target="@+id/widget"
    motion:framePosition="25"
    android:rotation=“45”/>
    motion:target="@+id/widget"
    motion:framePosition="50"
    android:rotation=“0”/>

    View full-size slide

  112. 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
    motion:attributeName="BackgroundColor"
    motion:customColorValue="#D81B60"/>

    View full-size slide

  113. { 360 | AnDev } 2018
    Cycles

    View full-size slide

  114. KeyCycle



    motion:target="@+id/widget"
    motion:framePosition="0"
    motion:wavePeriod=“0”
    motion:translationX=“0.0"
    />
    motion:target="@+id/widget"
    motion:framePosition="50"
    motion:wavePeriod=“50”
    motion:translationX=“30dp"
    />

    View full-size slide

  115. KeyCycle



    motion:target="@+id/widget"
    motion:framePosition="0"
    motion:wavePeriod=“0”
    motion:translationX=“0.0"
    />
    motion:target="@+id/widget"
    motion:framePosition="50"
    motion:wavePeriod=“50”
    motion:translationX=“30dp"
    />

    View full-size slide

  116. { 360 | AnDev } 2018
    Code
    KeyCycle
    Value (attributes)

    View full-size slide

  117. { 360 | AnDev } 2018
    Code
    KeyCycle
    Period

    View full-size slide

  118. KeyCycle



    motion:target="@+id/widget"
    motion:framePosition="0"
    motion:wavePeriod=“0”
    motion:translationX=“0.0"
    />
    motion:target="@+id/widget"
    motion:framePosition="50"
    motion:wavePeriod=“50”
    motion:translationX=“30dp"
    />

    View full-size slide

  119. KeyCycle



    motion:target="@+id/widget"
    motion:framePosition="0"
    motion:wavePeriod=“0”
    motion:translationX=“0.0"
    />
    motion:target="@+id/widget"
    motion:framePosition="50"
    motion:wavePeriod=“50”
    motion:translationX=“30dp"
    />

    View full-size slide

  120. { 360 | AnDev } 2018
    Motion

    View full-size slide

  121. { 360 | AnDev } 2018
    Motion
    ★ CoordinatorLayout
    ★ DrawerLayout
    ★ ViewPager
    ★ MotionLayout

    View full-size slide

  122. { 360 | AnDev } 2018
    motion:progress

    View full-size slide

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

    View full-size slide

  124. { 360 | AnDev } 2018

    View full-size slide

  125. { 360 | AnDev } 2018

    View full-size slide

  126. { 360 | AnDev } 2018
    Create
    CollapsibleToolbar
    0

    View full-size slide

  127. 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)
    }
    }

    View full-size slide

  128. 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)
    }
    }

    View full-size slide

  129. { 360 | AnDev } 2018
    Replace
    CollapsingToolbarLayout
    1

    View full-size slide

  130. android:id="@+id/content"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="false"
    android:background="@color/contentBackground">
    android:id="@+id/app_bar"
    android:layout_width="match_parent"
    android:layout_height="@dimen/app_bar_height">




    View full-size slide

  131. android:id="@+id/content"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="false"
    android:background="@color/contentBackground">
    android:id="@+id/app_bar"
    android:layout_width="match_parent"
    android:layout_height="@dimen/app_bar_height">




    View full-size slide

  132. { 360 | AnDev } 2018
    Create a layout for the header
    2

    View full-size slide


  133. 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">
    android:id="@+id/background"
    android:layout_width="match_parent"
    android:layout_height="200dp"
    android:background="@color/colorAccent"
    android:scaleType="centerCrop"

    View full-size slide

  134. android:id="@+id/background"
    android:layout_width="match_parent"
    android:layout_height="200dp"
    android:background="@color/colorAccent"
    android:scaleType="centerCrop"
    android:src="@drawable/monterey"/>
    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" />

    View full-size slide

  135. { 360 | AnDev } 2018
    Create a
    MotionScene
    3

    View full-size slide


  136. xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:motion="http://schemas.android.com/apk/res-auto">
    motion:constraintSetStart="@+id/start"
    motion:constraintSetEnd="@+id/end" />

    android:id="@+id/background"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:alpha="1.0"
    motion:layout_constraintBottom_toBottomOf="parent"/>
    android:id="@+id/label"
    android:layout_width="wrap_content"

    View full-size slide


  137. android:id="@+id/background"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:alpha="1.0"
    motion:layout_constraintBottom_toBottomOf="parent"/>
    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"/>


    android:id="@+id/background"

    View full-size slide


  138. android:id="@+id/background"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:alpha="0.2"
    motion:layout_constraintBottom_toBottomOf="parent"/>
    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" />


    View full-size slide

  139. { 360 | AnDev } 2018
    DrawerLayout

    View full-size slide

  140. { 360 | AnDev } 2018

    View full-size slide

  141. { 360 | AnDev } 2018

    View full-size slide

  142. 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)
    }
    }

    View full-size slide

  143. 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)
    }
    }

    View full-size slide

  144. 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)
    }
    }

    View full-size slide

  145. { 360 | AnDev } 2018
    ViewPager

    View full-size slide

  146. { 360 | AnDev } 2018

    View full-size slide

  147. { 360 | AnDev } 2018

    View full-size slide

  148. 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) {}
    }

    View full-size slide

  149. { 360 | AnDev } 2018
    MotionLayout

    View full-size slide

  150. { 360 | AnDev } 2018

    View full-size slide

  151. { 360 | AnDev } 2018

    View full-size slide

  152. { 360 | AnDev } 2018

    View full-size slide

  153. { 360 | AnDev } 2018

    View full-size slide

  154. { 360 | AnDev } 2018

    View full-size slide

  155. 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">


    android:id="@+id/fab"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/ic_add_a_photo_24dp"/>

    View full-size slide

  156. xmlns:motion="http://schemas.android.com/apk/res-auto">
    motion:constraintSetEnd="@+id/end"
    motion:constraintSetStart="@+id/start"
    motion:duration="250"
    motion:interpolator="linear">
    motion:touchAnchorId="@+id/motionLayout"
    motion:touchAnchorSide="bottom"
    motion:dragDirection="dragUp" />

    android:id="@id/motionLayout"
    android:layout_width="match_parent"
    android:layout_height="200dp"

    View full-size slide

  157. motion:touchAnchorSide="bottom"
    motion:dragDirection="dragUp" />

    android:id="@id/motionLayout"
    android:layout_width="match_parent"
    android:layout_height="200dp"
    motion:layout_constraintTop_toTopOf="parent" />
    android:id="@id/scrollable"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    motion:layout_constraintBottom_toBottomOf="parent"
    motion:layout_constraintTop_toBottomOf="@+id/motionLayout" />
    android:id="@id/fab"
    android:layout_width="wrap_content"

    View full-size slide

  158. android:id="@id/scrollable"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    motion:layout_constraintBottom_toBottomOf="parent"
    motion:layout_constraintTop_toBottomOf="@+id/motionLayout" />
    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"/>

    View full-size slide

  159. android:id="@id/scrollable"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    motion:layout_constraintBottom_toBottomOf="parent"
    motion:layout_constraintTop_toBottomOf="@+id/motionLayout" />
    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"/>

    View full-size slide

  160. android:id="@id/scrollable"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    motion:layout_constraintBottom_toBottomOf="parent"
    motion:layout_constraintTop_toBottomOf="@+id/motionLayout" />
    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"/>

    View full-size slide


  161. android:id="@id/motionLayout"
    android:layout_width="match_parent"
    android:layout_height="56dp"
    motion:layout_constraintTop_toTopOf="parent"
    motion:progress="1" />
    android:id="@id/scrollable"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    motion:layout_constraintBottom_toBottomOf="parent"
    motion:layout_constraintTop_toBottomOf="@+id/motionLayout" />
    android:id="@id/fab"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"

    View full-size slide

  162. android:id="@id/scrollable"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    motion:layout_constraintBottom_toBottomOf="parent"
    motion:layout_constraintTop_toBottomOf="@+id/motionLayout" />
    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" />


    View full-size slide

  163. android:id="@id/scrollable"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    motion:layout_constraintBottom_toBottomOf="parent"
    motion:layout_constraintTop_toBottomOf="@+id/motionLayout" />
    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" />


    View full-size slide

  164. android:id="@id/scrollable"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    motion:layout_constraintBottom_toBottomOf="parent"
    motion:layout_constraintTop_toBottomOf="@+id/motionLayout" />
    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" />


    View full-size slide

  165. { 360 | AnDev } 2018

    View full-size slide

  166. { 360 | AnDev } 2018

    View full-size slide

  167. { 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

    View full-size slide

  168. Thanks!
    Nicolas Roard
    @camaelon
    John Hoford
    @johnhoford

    View full-size slide