Share
Embed
Ø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
Transcript
62 slides
-
COOL THINGS ABOUT CONSTRAINT LAYOUT COOL THINGS TO DO WITH
CONSTRAINT LAYOUT -
UNBUNDLED SUPPORTS API 9+ UI BUILDER USABLE WITH XML ONLY
1.1.0 BETA 3 → 2017.10.12 -
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 -
CONSTRAINTS EQUATIONS SOLVER LINEAR SYSTEM OF EQUATIONS/INEQUALITIES 9 HOW DOES
IT WORK? 11 + … + 1n = 11 + … + 1n ≤ 11 + … + 1n ≥ -
CONSTRAINTS EQUATIONS SOLVER CASSOWARY LINEAR ARITHMETIC CONSTRAINT SOLVING ALGORITHM VIEW
BOUNDS 10 HOW DOES IT WORK? -
!!!<!-- Match Constraint Modifiers —> !!!<!-- Min/Max —> app:layout_constraintWidth_max="200dp" app:layout_constraintHeight_min="wrap"
!!!<!-- Percent !!--> app:layout_constraintWidth_percent=".75" app:layout_constraintHeight_percent=".5" !!!<!-- Ratio !!--> app:layout_constraintDimensionRatio="1:1" -
<android.support.constraint.Guideline android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/guideline_start" android:orientation="vertical" app:layout_constraintGuide_begin="16dp" /> <android.support.constraint.Guideline
android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/horizontal" android:orientation="horizontal" app:layout_constraintGuide_percent="0.37" /> -
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); } -
<!-- Dimension ratios --> <string name="dimension_ratio_home_hero">H,16:10</string> <!-- In action in
XML --> app:layout_constraintDimensionRatio=“@string/hero_ratio" -
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… -
<TextView 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" /> <TextView android:id=“@+id/next android:layout_width="0dp"
android:layout_height="wrap_content" app:layout_constraintHorizontal_weight="2" /> <TextView android:id=“@+id/last android:layout_width="0dp" android:layout_height="wrap_content" app:layout_constraintHorizontal_weight="1" /> 38 WEIGHTED CHAIN -
CONSTRAINT SET CREATE/SAVE/APPLY CREATE MANUALLY CLONE A LAYOUT FILE CLONE
A CONSTRAINT LAYOUT AWESOME WITH TRANSITIONS DEFINE SET OF CONSTRAINTS PROGRAMMATICALLY -
// 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 -
CUSTOM VIEW GROUPS GENERALIZED, REUSABLE LAYOUTS PERFORMANT COMPOSITE LAYOUTS LAYOUT
IS LOW-LEVEL (X, Y) EVEN SIMPLE ONES = MUCH CODE ADVANCED LAYOUTS… WOAH -
@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) ); } -
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 -
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 -
// 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 -
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 -
CONSTRAINTLAYOUT CREW JOHN HOFORD @JOHNHOFORD NICOLAS ROARD @CAMAELON ADVANCED ANIMATIONS
& CONSTRAINTLAYOUT DROIDCON SAN FRANCISCO 2017 -
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 -
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