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

Øredev 2017: Cool ConstraintLayout

Øredev 2017: Cool ConstraintLayout

Huyen's presentation at Øredev 2017 on cool things about and cool things to do with ConstraintLayout on Android.

Huyen Tue Dao

November 10, 2017
Tweet

More Decks by Huyen Tue Dao

Other Decks in Programming

Transcript

  1. HUYEN TUE DAO
    @QUEENCODEMONKEY
    COOL
    CONSTRAINT
    LAYOUT

    View Slide

  2. COOL THINGS ABOUT
    CONSTRAINT LAYOUT
    COOL THINGS TO DO
    WITH CONSTRAINT LAYOUT

    View Slide

  3. WHAT IS THE
    CONSTRAINT
    LAYOUT?

    View Slide

  4. RELATIONSHIPS BETWEEN VIEWS
    RELATIVE LAYOUT++
    + (≈LINEARLAYOUT)
    EXPRESSIVE
    PERFORMANT

    View Slide

  5. UNBUNDLED
    SUPPORTS API 9+
    UI BUILDER
    USABLE WITH XML ONLY
    1.1.0 BETA 3 → 2017.10.12

    View Slide

  6. HOW DOES IT
    WORK?

    View Slide

  7. CONSTRAINTS
    EQUATIONS
    SOLVER
    7
    HOW DOES IT WORK?

    View Slide

  8. HOW DOES IT WORK?
    CONSTRAINTS
    EQUATIONS
    SOLVER
    POSITION
    FIXED
    WRAP CONTENT
    MATCH CONSTRAINT
    RATIO
    PERCENT
    EDGE
    CENTER
    BASELINE
    CIRCULAR (1.1)
    DIMENSION OTHER
    8
    CHAINS
    CONSTRAINT SET
    PLACEHOLDERS
    GUIDELINES
    GROUPS
    BARRIERS

    View Slide

  9. CONSTRAINTS
    EQUATIONS
    SOLVER
    LINEAR SYSTEM OF EQUATIONS/INEQUALITIES
    9
    HOW DOES IT WORK?
    11 + … + 1n =
    11 + … + 1n ≤
    11 + … + 1n ≥

    View Slide

  10. CONSTRAINTS
    EQUATIONS
    SOLVER
    CASSOWARY
    LINEAR ARITHMETIC
    CONSTRAINT SOLVING
    ALGORITHM
    VIEW BOUNDS
    10
    HOW DOES IT WORK?

    View Slide

  11. CONSTRAINTLAYOUT, INSIDE AND OUT: PART 2 DAVE SMITH

    View Slide

  12. LET’S TAKE
    A QUICK
    TOUR

    View Slide

  13. View Slide

  14. CONSTRAINT
    LAYOUT
    CHEAT SHEET

    View Slide

  15. SOURCE
    TARGET
    EDGE CONSTRAINT

    app:layout_constraintLeft_toLeftOf="parent"

    app:layout_constraint_toOf=“a_view"

    View Slide

  16. CENTER CONSTRAINT

    app:layout_constraintHorizontal_bias="0.75"

    View Slide

  17. BASELINE CONSTRAINT
    app:layout_constraintBaseline_toBaselineOf="@+id/view"

    View Slide

  18. CIRCULAR CONSTRAINT
    app:layout_constraintCircle="@id/centerTextView"
    app:layout_constraintCircleAngle="90"
    app:layout_constraintCircleRadius="@dimen/circular_radius"

    View Slide

  19. SIZE CONSTRAINTS

    View Slide

  20. !!!
    app:layout_constraintWidth_percent=".75"
    app:layout_constraintHeight_percent=".5"
    !!!
    app:layout_constraintDimensionRatio="1:1"

    View Slide

  21. GUIDELINES

    View Slide

  22. android:layout_width="wrap_content"

    android:layout_height="wrap_content"

    android:id="@+id/guideline_start"

    android:orientation="vertical"

    app:layout_constraintGuide_begin="16dp" />



    android:layout_width="wrap_content"

    android:layout_height="wrap_content"

    android:id="@+id/horizontal"

    android:orientation="horizontal"

    app:layout_constraintGuide_percent="0.37" />

    View Slide

  23. BARRIERS

    View Slide

  24. android:id="@+id/barrier"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:barrierDirection="end"
    app:constraint_referenced_ids="title,artist" !/>

    View Slide

  25. GROUPS

    View Slide

  26. SO WHAT CAN
    WE DO WITH
    CONSTRAINT
    LAYOUT?

    View Slide

  27. public class SquareImageView extends ImageView {
    @Override

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

    super.onMeasure(widthMeasureSpec, heightMeasureSpec);


    final int size = Math.min(getMeasuredWidth(), getMeasuredHeight());

    setMeasuredDimension(size, size);

    }

    View Slide

  28. android:id=“@+id/square_image”

    android:layout_width="0dp"

    android:layout_height="0dp"

    app:layout_constraintDimensionRatio=“W,1:1"

    View Slide

  29. View Slide


  30. H,16:10

    app:layout_constraintDimensionRatio=“@string/hero_ratio"

    View Slide

  31. IS IT REALLY
    BETTER THAN
    RELATIVE
    LAYOUT?

    View Slide

  32. STYLING ANDROID
    CONSTRAINTLAYOUT
    PART 6

    View Slide

  33. View Slide

  34. private fun updateManualAnchors() {

    val size = anchorMinSize + seekBar.progress

    val halfSize = Math.round(size * 0.5f)

    val horizontalMargin =
    resources.getDimensionPixelSize(R.dimen.content_margin_horizontal) - halfSize

    val verticalMargin =
    resources.getDimensionPixelSize(R.dimen.alignment_rectangle_margin_vertical) - halfSize

    updateManualAnchor(frameAnchorTop, size, verticalMargin, 0, 0, 0)

    updateManualAnchor(frameAnchorStart, size, 0, horizontalMargin, 0, 0)

    updateManualAnchor(frameAnchorBottom, size, 0, 0, verticalMargin, 0)

    updateManualAnchor(frameAnchorEnd, size, 0, 0, 0, horizontalMargin)

    }


    private fun updateManualAnchor(anchor: View, size: Int,

    marginTop: Int, marginStart: Int,

    marginBottom: Int, marginEnd: Int) {

    val layoutParams = anchor.layoutParams as FrameLayout.LayoutParams
    layoutParams.width = size

    layoutParams.height = size

    layoutParams.topMargin = marginTop

    layoutParams.bottomMargin = marginBottom

    layoutParams.marginStart = marginStart

    layoutParams.marginEnd = marginEnd
    anchor.layoutParams = layoutParams

    }
    WITH CONSTRAINTLAYOUT
    JUST CHANGE THE SIZE…

    View Slide

  35. WHAT WAS THAT
    ABOUT LINEAR
    LAYOUT?

    View Slide

  36. CHAINS. CHAINS. CHAINS.
    FROM CONSTRAINTLAYOUT DOCUMENTATION

    View Slide

  37. SPREAD INSIDE CHAIN
    android:id=“@+id/spread_inside_head”

    app:layout_constraintHorizontal_chainStyle="spread_inside"

    … />

    View Slide

  38. android:id=“@+id/weighted_chain_head

    android:layout_width="0dp"

    android:layout_height="wrap_content"

    app:layout_constraintHorizontal_chainStyle="spread"

    app:layout_constraintHorizontal_weight=“1" />
    android:id=“@+id/next
    android:layout_width="0dp"

    android:layout_height="wrap_content"

    app:layout_constraintHorizontal_weight="2" />
    android:id=“@+id/last

    android:layout_width="0dp"

    android:layout_height="wrap_content"

    app:layout_constraintHorizontal_weight="1" />
    38
    WEIGHTED CHAIN

    View Slide

  39. android:id=“@+id/packed_chain_head”

    app:layout_constraintHorizontal_chainStyle="packed"

    app:layout_constraintHorizontal_bias="0.12" />
    39
    PACKED CHAIN

    View Slide

  40. View Slide

  41. View Slide

  42. A CONSTRAINT
    LAYOUT SPECIAL
    CONSTRAINT SET

    View Slide

  43. CONSTRAINT SET
    CREATE/SAVE/APPLY
    CREATE MANUALLY
    CLONE A LAYOUT FILE
    CLONE A CONSTRAINT LAYOUT
    AWESOME WITH TRANSITIONS
    DEFINE SET OF CONSTRAINTS
    PROGRAMMATICALLY

    View Slide

  44. View Slide

  45. View Slide

  46. // Inflate initial layout as usual.
    setContentView(R.layout.activity_constraintset_01)
    // Get references to controls

    constraintLayout = findViewById(R.id.constraint_layout) as ConstraintLayout


    // Load ConstraintSets.

    constraintSet01.clone(constraintLayout)

    constraintSet02.clone(this, R.layout.activity_constraintset_02)
    …some time later…
    // Toggle ConstraintSets.
    TransitionManager.beginDelayedTransition(constraintLayout)

    if (original) constraintSet02.applyTo(constraintLayout)

    else constraintSet01.applyTo(constraintLayout)

    original = !original
    44

    View Slide

  47. YES, LIST ITEMS
    CAN BE
    CONSTRAINT
    LAYOUTS

    View Slide

  48. CONSTRAINT SET
    IN LIEU OF
    CUSTOM
    VIEWGROUP

    View Slide

  49. CUSTOM
    VIEW GROUPS
    GENERALIZED, REUSABLE LAYOUTS
    PERFORMANT COMPOSITE LAYOUTS
    LAYOUT IS LOW-LEVEL (X, Y)
    EVEN SIMPLE ONES = MUCH CODE
    ADVANCED LAYOUTS… WOAH

    View Slide

  50. @Override

    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {

    MarginLayoutParams layoutParams = (MarginLayoutParams) icon.getLayoutParams();


    // Figure out the x-coordinate and y-coordinate of the icon.

    int x = getPaddingLeft() + layoutParams.leftMargin;

    int y = getPaddingTop() + layoutParams.topMargin;


    // Layout the icon.

    icon.layout(x, y, x + icon.getMeasuredWidth(), y + icon.getMeasuredHeight());


    // Calculate the x-coordinate of the title: icon's right coordinate +

    // the icon's right margin.

    x += icon.getMeasuredWidth() + layoutParams.rightMargin;


    // Add in the title's left margin.

    layoutParams = (MarginLayoutParams) titleView.getLayoutParams();

    x += layoutParams.leftMargin;


    // Calculate the y-coordinate of the title: this ViewGroup's top padding +

    // the title's top margin

    y = getPaddingTop() + layoutParams.topMargin;


    // Layout the title.

    titleView.layout(x, y, x + titleView.getMeasuredWidth(), y + titleView.getMeasuredHeight());


    // The subtitle has the same x-coordinate as the title. So no more calculating there.


    // Calculate the y-coordinate of the subtitle: the title's bottom coordinate +

    // the title's bottom margin.

    y += titleView.getMeasuredHeight() + layoutParams.bottomMargin;

    layoutParams = (MarginLayoutParams) subtitleView.getLayoutParams();


    // Add in the subtitle's top margin.

    y += layoutParams.topMargin;


    // Layout the subtitle.

    subtitleView.layout(x, y,

    x + subtitleView.getMeasuredWidth(), y + subtitleView.getMeasuredHeight());

    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    // Measure icon.
    measureChildWithMargins(icon, widthMeasureSpec, 0, heightMeasureSpec, 0);
    // Figure out how much width the icon used.
    MarginLayoutParams lp = (MarginLayoutParams) icon.getLayoutParams();
    int widthUsed = icon.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
    int heightUsed = 0;
    // Measure title
    measureChildWithMargins(
    titleView,
    // Pass width constraints and width already used.
    widthMeasureSpec, widthUsed,
    // Pass height constraints and height already used.
    heightMeasureSpec, heightUsed
    );
    // Measure the Subtitle.
    measureChildWithMargins(
    subtitleView,
    // Pass width constraints and width already used.
    widthMeasureSpec, widthUsed,
    // Pass height constraints and height already used.
    heightMeasureSpec, titleView.getMeasuredHeight());
    // Calculate this view's measured width and height.
    // Figure out how much total space the icon used.
    int iconWidth = icon.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
    int iconHeight = icon.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
    lp = (MarginLayoutParams) titleView.getLayoutParams();
    // Figure out how much total space the title used.
    int titleWidth = titleView.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
    int titleHeight = titleView.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
    lp = (MarginLayoutParams) subtitleView.getLayoutParams();
    // Figure out how much total space the subtitle used.
    int subtitleWidth = subtitleView.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
    int subtitleHeight = subtitleView.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
    // The width taken by the children + padding.
    int width = getPaddingTop() + getPaddingBottom() +
    iconWidth + Math.max(titleWidth, subtitleWidth);
    // The height taken by the children + padding.
    int height = getPaddingTop() + getPaddingBottom() +
    Math.max(iconHeight, titleHeight + subtitleHeight);
    // Reconcile the measured dimensions with the this view's constraints and
    // set the final measured width and height.
    setMeasuredDimension(
    resolveSize(width, widthMeasureSpec),
    resolveSize(height, heightMeasureSpec)
    );
    }

    View Slide

  51. CONSTRAINT
    SET IN LIEU OF
    CUSTOM
    VIEWGROUP

    View Slide

  52. private fun addRow(view: View) {

    constraintLayout.addView(view)


    val id = view.id

    constraintSet.constrainWidth(id, MATCH_CONSTRAINT)

    constraintSet.constrainHeight(id, rowHeight)


    // Constrain new view vertically.

    val target = if (lastRowId  PARENT_ID) TOP else BOTTOM

    constraintSet.connect(id, TOP, lastRowId, target)
    // Constrain new view horizontally.

    constraintSet.connect(id, START, PARENT_ID, START)

    constraintSet.connect(id, END, PARENT_ID, END)


    // Toggle elevation to elevate last added view.

    constraintSet.setElevation(id, resources.getDimension(R.dimen.elevation))

    constraintSet.setElevation(lastViewId, 0f)


    // Apply constraints.

    constraintSet.applyTo(constraintLayout)
    50

    View Slide

  53. private fun addRowItem(view: View) {

    …

    constraintLayout.addView(view)


    val id = view.id


    // Initialize width and height of new view.

    constraintSet.constrainWidth(id, MATCH_CONSTRAINT)

    constraintSet.constrainHeight(id, rowHeight)


    // Constrain new view vertically.

    val target = if (lastRowId  PARENT_ID) TOP else BOTTOM

    constraintSet.connect(id, TOP, lastRowId, target)
    51

    View Slide

  54. // Update current row.

    currentRowIds.add(id)

    // Re-create horizontal chain with other row items.

    constraintSet.createHorizontalChainRtl(

    PARENT_ID, START,

    PARENT_ID, END,

    currentRowIds.toIntArray(), FloatArray(currentRow.size, { 1f }),

    CHAIN_SPREAD_INSIDE)


    // Toggle elevation to elevate last added view.

    constraintSet.setElevation(id, resources.getDimension(R.dimen.elevation))

    constraintSet.setElevation(lastViewId, 0f)


    // Apply constraints.

    constraintSet.applyTo(constraintLayout)
    52

    View Slide

  55. SERIOUSLY
    COOL THINGS IN
    CONSTRAINT
    LAYOUT

    View Slide

  56. constraintLayout.update {
    guideline(R.id.content_start, VERTICAL_GUIDELINE, LEFT, getDimenSize(R.dimen.keyline_start))
    guideline(R.id.content_top, HORIZONTAL_GUIDELINE, TOP, getDimenSize(R.dimen.content_padding_top))
    constrain(R.id.image) {
    width = MATCH_CONSTRAINT_SPREAD
    height = MATCH_CONSTRAINT_SPREAD
    start = START of PARENT_ID
    top = TOP of PARENT_ID
    end = END of PARENT_ID
    bottom = BOTTOM of PARENT_ID
    }
    constrain(R.id.title) {
    width = WRAP_CONTENT
    height = WRAP_CONTENT
    end = END of R.id.content_start
    top = TOP of R.id.content_top
    }
    constrain(R.id.artist) {
    width = WRAP_CONTENT
    height = WRAP_CONTENT
    start = END of R.id.title
    top = BOTTOM of R.id.title
    }
    }
    54
    CONSTRAINT SET DSL
    github.com/queencodemonkey/the-road-to-kotlintown-ii

    View Slide

  57. ANDROID DEVELOPERS BACKSTAGE
    EPISODE 50: CONSTRAINT LAYOUT

    View Slide

  58. CONSTRAINTLAYOUT CREW
    JOHN HOFORD
    @JOHNHOFORD
    NICOLAS ROARD
    @CAMAELON
    ADVANCED ANIMATIONS & CONSTRAINTLAYOUT
    DROIDCON SAN FRANCISCO 2017

    View Slide

  59. View Slide

  60. THANK YOU!
    SPEAKERDECK.COM/QUEENCODEMONKEY
    YOUTUBE.COM/ANDROIDDIALOGS
    RANDOMLYTYPING.COM
    HUYEN TUE DAO
    @QUEENCODEMONKEY
    QUEENCODEMONKEY/PRESENTATION-COOL-CONSTRAINTLAYOUT

    View Slide

  61. REFERENCES
    59
    CONSTRAINT LAYOUT APIS DOCUMENTATION
    https://developer.android.com/reference/android/support/constraint/package-summary.html
    BUILD A RESPONSIVE UI WITH CONSTRAINTLAYOUT
    developer.android.com/training/constraint-layout/index.html
    ANDROID DEVELOPERS BACKSTAGE: EPISODE 50: CONSTRAINT LAYOUT
    androidbackstage.blogspot.com/2016/06/episode-50-constraint-layout.html
    USING CONSTRAINTLAYOUT TO DESIGN YOUR VIEWS | CODE LABS
    codelabs.developers.google.com/codelabs/constraint-layout/index.html
    ANDROID LAYOUTS: A NEW WORLD | GOOGLE I/O 2016
    https://youtu.be/sO9aX87hq9c
    ADVANCED ANIMATIONS & CONSTRAINTLAYOUT
    https://speakerdeck.com/camaelon/advanced-animations-and-constraintlayout
    ADVANCED CONSTRAINTLAYOUT
    http://cfp.devoxx.us/2017/talk/XOD-8930/Advanced_ConstraintLayout

    View Slide

  62. REFERENCES
    60
    CONSTRAINTLAYOUT
    https://speakerdeck.com/writtmeyer/constraintlayout-1
    CONSTRAINTLAYOUT – PART 1
    blog.stylingandroid.com/constraintlayout-part-1/
    CONSTRAINTLAYOUT, INSIDE AND OUT: PART 1
    http://wiresareobsolete.com/2016/07/constraintlayout-part-1/
    SOME THOUGHTS ON ANDROID’S NEW CONSTRAINTLAYOUT AND ANDROID STUDIO’S NEW DESIGN EDITOR
    http://www.grokkingandroid.com/thoughts-on-constraintlayout-and-design-editor/
    BUILD A UI WITH LAYOUT EDITOR
    developer.android.com/studio/write/layout-editor.html
    WHAT'S NEW IN ANDROID DEVELOPMENT TOOLS | GOOGLE I/O 2016
    https://youtu.be/csaXml4xtN8
    THE EXPERTS' GUIDE TO ANDROID DEVELOPMENT TOOLS | GOOGLE I/O 2016
    https://youtu.be/hHnTIMjd1Y8
    JOHN HOFORD & NICOLAS ROARD : CONSTRAINTLAYOUT | ANDROID DIALOGS
    https://youtu.be/hHnTIMjd1Y8

    View Slide