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 Slide

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

    View Slide

  3. { 360 | AnDev } 2018
    ConstraintLayout 2.0

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  11. { 360 | AnDev } 2018
    MotionLayout

    View Slide

  12. { 360 | AnDev } 2018
    MotionLayout
    ConstraintLayout
    ^

    View Slide

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

    View Slide

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

    View 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 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 Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  21. { 360 | AnDev } 2018
    Transitions

    View 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 Slide

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

    View Slide

  24. { 360 | AnDev } 2018

    View Slide

  25. { 360 | AnDev } 2018

    View 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 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 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 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 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 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 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 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 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 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 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 Slide

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

    View 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 Slide

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

    View Slide

  40. { 360 | AnDev } 2018

    View Slide

  41. { 360 | AnDev } 2018

    View 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 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 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 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 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 Slide

  47. { 360 | AnDev } 2018

    View Slide

  48. { 360 | AnDev } 2018

    View 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 Slide

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

    View 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 Slide



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


    View Slide

  53. { 360 | AnDev } 2018
    Utilities

    View Slide

  54. { 360 | AnDev } 2018
    Utilities
    ★ MockView

    View Slide

  55. { 360 | AnDev } 2018
    Utilities
    ★ MockView

    View Slide

  56. { 360 | AnDev } 2018
    Utilities
    ★ MockView

    View Slide

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

    View Slide

  58. { 360 | AnDev } 2018

    View Slide

  59. { 360 | AnDev } 2018

    View 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 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 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 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 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 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 Slide

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

    View Slide

  67. { 360 | AnDev } 2018
    Events

    View Slide

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

    View Slide

  69. { 360 | AnDev } 2018
    TransitionListener

    View Slide

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

    View Slide

  71. { 360 | AnDev } 2018
    OnSwipe

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  77. OnSwipe: Top
    touchAnchorSide : TOP

    View Slide

  78. OnSwipe: Top
    touchAnchorSide : TOP

    View Slide

  79. OnSwipe: Right
    touchAnchorSide : RIGHT

    View Slide

  80. OnSwipe: Right
    touchAnchorSide : RIGHT

    View Slide

  81. OnSwipe: Bottom
    touchAnchorSide : BOTTOM

    View Slide

  82. OnSwipe: Bottom
    touchAnchorSide : BOTTOM

    View Slide

  83. OnSwipe: Left
    touchAnchorSide : LEFT

    View Slide

  84. OnSwipe: Left
    touchAnchorSide : LEFT

    View Slide

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

    View Slide

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

    View Slide

  87. { 360 | AnDev } 2018
    OnClick

    View Slide


  88. mode

    • transitionToEnd
    • toggle
    • transitionToStart
    • jumpToEnd
    • jumpToStart
    OnClick

    View Slide


  89. mode

    • transitionToEnd
    • toggle
    • transitionToStart
    • jumpToEnd
    • jumpToStart
    OnClick

    View Slide

  90. { 360 | AnDev } 2018
    Keyframes

    View Slide

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

    View Slide

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

    View Slide

  93. { 360 | AnDev } 2018
    Position

    View Slide

  94. KeyFrameSet


    View Slide

  95. KeyFrameSet


    View Slide

  96. KeyFrameSet

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

    View Slide

  97. KeyFrameSet

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

    View Slide

  98. KeyFrameSet

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

    View Slide

  99. KeyFrameSet

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

    View Slide

  100. KeyFrameSet

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

    View Slide

  101. KeyFrameSet

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

    View Slide

  102. { 360 | AnDev } 2018
    Coordinate Systems

    View Slide

  103. { 360 | AnDev } 2018

    View Slide

  104. { 360 | AnDev } 2018

    View Slide

  105. { 360 | AnDev } 2018
    Start
    End

    View Slide

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

    View Slide

  107. { 360 | AnDev } 2018
    deltaRelative
    Y
    X

    View Slide

  108. { 360 | AnDev } 2018
    pathRelative
    X
    Y

    View Slide

  109. { 360 | AnDev } 2018
    Attributes

    View Slide

  110. KeyFrameSet


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

    View Slide

  111. KeyFrameSet


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

    View Slide

  112. KeyFrameSet


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

    View Slide

  113. KeyFrameSet


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

    View Slide

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

    View Slide

  115. { 360 | AnDev } 2018
    Cycles

    View Slide

  116. 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 Slide

  117. 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 Slide

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

    View Slide

  119. { 360 | AnDev } 2018
    Code
    KeyCycle
    Period

    View Slide

  120. 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 Slide

  121. 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 Slide

  122. { 360 | AnDev } 2018
    Motion

    View Slide

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

    View Slide

  124. { 360 | AnDev } 2018
    motion:progress

    View Slide

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

    View Slide

  126. { 360 | AnDev } 2018

    View Slide

  127. { 360 | AnDev } 2018

    View Slide

  128. { 360 | AnDev } 2018
    Create
    CollapsibleToolbar
    0

    View Slide

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

    View Slide

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

    View Slide

  131. { 360 | AnDev } 2018
    Replace
    CollapsingToolbarLayout
    1

    View Slide

  132. 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 Slide

  133. 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 Slide

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

    View Slide


  135. 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 Slide

  136. 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 Slide

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

    View Slide


  138. 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 Slide


  139. 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 Slide


  140. 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 Slide

  141. View Slide

  142. View Slide

  143. { 360 | AnDev } 2018
    DrawerLayout

    View Slide

  144. { 360 | AnDev } 2018

    View Slide

  145. { 360 | AnDev } 2018

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  149. { 360 | AnDev } 2018
    ViewPager

    View Slide

  150. { 360 | AnDev } 2018

    View Slide

  151. { 360 | AnDev } 2018

    View Slide

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

    View Slide

  153. { 360 | AnDev } 2018
    MotionLayout

    View Slide

  154. { 360 | AnDev } 2018

    View Slide

  155. { 360 | AnDev } 2018

    View Slide

  156. { 360 | AnDev } 2018

    View Slide

  157. { 360 | AnDev } 2018

    View Slide

  158. { 360 | AnDev } 2018

    View Slide

  159. 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 Slide

  160. 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 Slide

  161. 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 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="invisible"
    motion:layout_constraintTop_toBottomOf="parent"
    motion:layout_constraintRight_toRightOf="parent"
    android:layout_marginTop="8dp"
    android:layout_marginRight="8dp"/>

    View 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="invisible"
    motion:layout_constraintTop_toBottomOf="parent"
    motion:layout_constraintRight_toRightOf="parent"
    android:layout_marginTop="8dp"
    android:layout_marginRight="8dp"/>

    View 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="invisible"
    motion:layout_constraintTop_toBottomOf="parent"
    motion:layout_constraintRight_toRightOf="parent"
    android:layout_marginTop="8dp"
    android:layout_marginRight="8dp"/>

    View Slide


  165. 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 Slide

  166. 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 Slide

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

  168. 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 Slide

  169. { 360 | AnDev } 2018

    View Slide

  170. { 360 | AnDev } 2018

    View Slide

  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

    View Slide

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

    View Slide