1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.constraintlayout.widget;
18 
19 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
20 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
21 
22 import static androidx.constraintlayout.widget.ConstraintLayout.LayoutParams.MATCH_CONSTRAINT_SPREAD;
23 import static androidx.constraintlayout.widget.ConstraintLayout.LayoutParams.MATCH_CONSTRAINT_WRAP;
24 import static androidx.constraintlayout.widget.ConstraintLayout.LayoutParams.PARENT_ID;
25 import static androidx.constraintlayout.widget.ConstraintLayout.LayoutParams.UNSET;
26 
27 import android.annotation.SuppressLint;
28 import android.annotation.TargetApi;
29 import android.content.Context;
30 import android.content.pm.ApplicationInfo;
31 import android.content.res.Resources;
32 import android.content.res.TypedArray;
33 import android.graphics.Canvas;
34 import android.graphics.Color;
35 import android.graphics.Paint;
36 import android.os.Build;
37 import android.util.AttributeSet;
38 import android.util.Log;
39 import android.util.SparseArray;
40 import android.util.SparseIntArray;
41 import android.view.View;
42 import android.view.ViewGroup;
43 
44 import androidx.constraintlayout.core.LinearSystem;
45 import androidx.constraintlayout.core.Metrics;
46 import androidx.constraintlayout.core.widgets.ConstraintAnchor;
47 import androidx.constraintlayout.core.widgets.ConstraintWidget;
48 import androidx.constraintlayout.core.widgets.ConstraintWidgetContainer;
49 import androidx.constraintlayout.core.widgets.Guideline;
50 import androidx.constraintlayout.core.widgets.Optimizer;
51 import androidx.constraintlayout.core.widgets.analyzer.BasicMeasure;
52 
53 import org.jspecify.annotations.NonNull;
54 import org.jspecify.annotations.Nullable;
55 
56 import java.util.ArrayList;
57 import java.util.HashMap;
58 
59 /**
60  * A {@code ConstraintLayout} is a {@link android.view.ViewGroup} which allows you
61  * to position and size widgets in a flexible way.
62  * <p>
63  * <b>Note:</b> {@code ConstraintLayout} is available as a support library that you can use
64  * on Android systems starting with API level 9 (Gingerbread).
65  * As such, we are planning on enriching its API and capabilities over time.
66  * This documentation will reflect those changes.
67  * </p>
68  * <p>
69  * There are currently various types of constraints that you can use:
70  * <ul>
71  * <li>Relative positioning</li>
72  * <li>Margins</li>
73  * <li>Centering positioning</li>
74  * <li>Circular positioning</li>
75  * <li>Visibility behavior</li>
76  * <li>Dimension constraints</li>
77  * <li>Chains</li>
78  * <li>Virtual Helpers objects</li>
79  * <li>Optimizer</li>
80  * </ul>
81  * </p>
82  *
83  * <p>
84  * Note that you cannot have a circular dependency in constraints.
85  * </p>
86  * <p>
87  * Also see {@link ConstraintLayout.LayoutParams
88  * ConstraintLayout.LayoutParams} for layout attributes
89  * </p>
90  *
91  * <h2>Developer Guide</h2>
92  *
93  * <h3 id="RelativePositioning"> Relative positioning </h3>
94  * <p>
95  * Relative positioning is one of the basic building blocks of creating layouts in ConstraintLayout.
96  * Those constraints allow you to position a given widget relative to another one. You can constrain
97  * a widget on the horizontal and vertical axis:
98  * <ul>
99  * <li>Horizontal Axis: left, right, start and end sides</li>
100  * <li>Vertical Axis: top, bottom sides and text baseline</li>
101  * </ul>
102  * <p>
103  * The general concept is to constrain a given side of a widget to another side of any other widget.
104  * <p>
105  * For example, in order to position button B to the right of button A:
106  * </p>
107  * <p>
108  * you would need to do:
109  * </p>
110  * <pre>{@code
111  *         <Button android:id="@+id/buttonA" ... />
112  *         <Button android:id="@+id/buttonB" ...
113  *                 app:layout_constraintLeft_toRightOf="@+id/buttonA" />
114  *         }
115  *     </pre>
116  * This tells the system that we want the left side of button
117  * B to be constrained to the right side of button A.
118  * Such a position constraint means that the system will try to have
119  * both sides share the same location.
120  *
121  * <p>Here is the list of available constraints:</p>
122  * <ul>
123  * <li>{@code layout_constraintLeft_toLeftOf}</li>
124  * <li>{@code layout_constraintLeft_toRightOf}</li>
125  * <li>{@code layout_constraintRight_toLeftOf}</li>
126  * <li>{@code layout_constraintRight_toRightOf}</li>
127  * <li>{@code layout_constraintTop_toTopOf}</li>
128  * <li>{@code layout_constraintTop_toBottomOf}</li>
129  * <li>{@code layout_constraintBottom_toTopOf}</li>
130  * <li>{@code layout_constraintBottom_toBottomOf}</li>
131  * <li>{@code layout_constraintBaseline_toBaselineOf}</li>
132  * <li>{@code layout_constraintStart_toEndOf}</li>
133  * <li>{@code layout_constraintStart_toStartOf}</li>
134  * <li>{@code layout_constraintEnd_toStartOf}</li>
135  * <li>{@code layout_constraintEnd_toEndOf}</li>
136  * </ul>
137  * <p>
138  * They all take a reference {@code id} to another widget, or the
139  * {@code parent} (which will reference the parent container, i.e. the ConstraintLayout):
140  * <pre>{@code
141  *         <Button android:id="@+id/buttonB" ...
142  *                 app:layout_constraintLeft_toLeftOf="parent" />
143  *         }
144  *     </pre>
145  *
146  * </p>
147  *
148  * <h3 id="Margins"> Margins </h3>
149  * <p>If side margins are set, they will be applied to the corresponding constraints
150  * (if they exist), enforcing the margin as a space between the target and the source side.
151  * The usual layout margin attributes can be used to this effect:</p>
152  * <ul>
153  * <li>{@code android:layout_marginStart}</li>
154  * <li>{@code android:layout_marginEnd}</li>
155  * <li>{@code android:layout_marginLeft}</li>
156  * <li>{@code android:layout_marginTop}</li>
157  * <li>{@code android:layout_marginRight}</li>
158  * <li>{@code android:layout_marginBottom}</li>
159  * <li>{@code layout_marginBaseline}</li>
160  * </ul>
161  * <p>Note that a margin can only be positive or equal to zero,
162  * and takes a {@code Dimension}.</p>
163  * <h3 id="GoneMargin"> Margins when connected to a GONE widget</h3>
164  * <p>When a position constraint target's visibility is {@code View.GONE},
165  * you can also indicate a different
166  * margin value to be used using the following attributes:</p>
167  * <ul>
168  * <li>{@code layout_goneMarginStart}</li>
169  * <li>{@code layout_goneMarginEnd}</li>
170  * <li>{@code layout_goneMarginLeft}</li>
171  * <li>{@code layout_goneMarginTop}</li>
172  * <li>{@code layout_goneMarginRight}</li>
173  * <li>{@code layout_goneMarginBottom}</li>
174  * <li>{@code layout_goneMarginBaseline}</li>
175  * </ul>
176  * </p>
177  *
178  * </p>
179  * <h3 id="CenteringPositioning"> Centering positioning and bias</h3>
180  * <p>
181  * A useful aspect of {@code ConstraintLayout} is in how it deals with "impossible" constraints.
182  * For example, if
183  * we have something like:
184  * <pre>{@code
185  *         <androidx.constraintlayout.widget.ConstraintLayout ...>
186  *             <Button android:id="@+id/button" ...
187  *                 app:layout_constraintLeft_toLeftOf="parent"
188  *                 app:layout_constraintRight_toRightOf="parent"/>
189  *         </>
190  *         }
191  *     </pre>
192  * </p>
193  * <p>
194  * Unless the {@code ConstraintLayout} happens to have the exact same size as the
195  * {@code Button}, both constraints
196  * cannot be satisfied at the same time (both sides cannot be where we want them to be).
197  * </p>
198  * <p>
199  * What happens in this case is that the constraints act like opposite forces
200  * pulling the widget apart equally; such that the widget will end up being centered
201  * in the parent container.
202  * This will apply similarly for vertical constraints.
203  * </p>
204  * <b>Bias</b>
205  * <p>
206  * The default when encountering such opposite constraints is to center the widget;
207  * but you can tweak
208  * the positioning to favor one side over another using the bias attributes:
209  * <ul>
210  * <li>{@code layout_constraintHorizontal_bias}</li>
211  * <li>{@code layout_constraintVertical_bias}</li>
212  * </ul>
213  * <p>
214  * For example the following will make the left side with a 30% bias instead of the default 50%,
215  * such that the left side will be
216  * shorter, with the widget leaning more toward the left side:
217  * </p>
218  * <pre>{@code
219  *         <androidx.constraintlayout.widget.ConstraintLayout ...>
220  *             <Button android:id="@+id/button" ...
221  *                 app:layout_constraintHorizontal_bias="0.3"
222  *                 app:layout_constraintLeft_toLeftOf="parent"
223  *                 app:layout_constraintRight_toRightOf="parent"/>
224  *         </>
225  *         }
226  *     </pre>
227  * Using bias, you can craft User Interfaces that will better adapt to screen sizes changes.
228  * </p>
229  * </p>
230  *
231  * <h3 id="CircularPositioning"> Circular positioning (<b>Added in 1.1</b>)</h3>
232  * <p>
233  * You can constrain a widget center relative to another widget center,
234  * at an angle and a distance. This allows
235  * you to position a widget on a circle. The following attributes can be used:
236  * <ul>
237  * <li>{@code layout_constraintCircle} : references another widget id</li>
238  * <li>{@code layout_constraintCircleRadius} : the distance to the other widget center</li>
239  * <li>{@code layout_constraintCircleAngle} : which angle the widget should be at
240  * (in degrees, from 0 to 360)</li>
241  * </ul>
242  * <pre>{@code
243  *  <Button android:id="@+id/buttonA" ... />
244  *  <Button android:id="@+id/buttonB" ...
245  *      app:layout_constraintCircle="@+id/buttonA"
246  *      app:layout_constraintCircleRadius="100dp"
247  *      app:layout_constraintCircleAngle="45" />
248  *         }
249  *     </pre>
250  * </p>
251  * <h3 id="VisibilityBehavior"> Visibility behavior </h3>
252  * <p>
253  * {@code ConstraintLayout} has a specific handling of widgets being marked as {@code View.GONE}.
254  * <p>{@code GONE} widgets, as usual, are not going to be displayed and
255  * are not part of the layout itself (i.e. their actual dimensions
256  * will not be changed if marked as {@code GONE}).
257  *
258  * <p>But in terms of the layout computations, {@code GONE} widgets are still part of it,
259  * with an important distinction:
260  * <ul>
261  * <li> For the layout pass, their dimension will be considered as zero
262  * (basically, they will be resolved to a point)</li>
263  * <li> If they have constraints to other widgets they will still be respected,
264  * but any margins will be as if equals to zero</li>
265  * </ul>
266  *
267  * <p>This specific behavior allows to build layouts where you can
268  * temporarily mark widgets as being {@code GONE},
269  * without breaking the layout, which can be particularly useful
270  * when doing simple layout animations.</p>
271  * <p><b>Note: </b>The margin used will be the margin that B had
272  * defined when connecting to A.
273  * In some cases, this might not be the margin you want
274  * (e.g. A had a 100dp margin to the side of its container,
275  * B only a 16dp to A, marking
276  * A as gone, B will have a margin of 16dp to the container).
277  * For this reason, you can specify an alternate
278  * margin value to be used when the connection is to a widget being marked as gone
279  * (see <a href="#GoneMargin">the section above about the gone margin attributes</a>).
280  * </p>
281  *
282  * <h3 id="DimensionConstraints"> Dimensions constraints </h3>
283  * <b>Minimum dimensions on ConstraintLayout</b>
284  * <p>
285  * You can define minimum and maximum sizes for the {@code ConstraintLayout} itself:
286  * <ul>
287  * <li>{@code android:minWidth} set the minimum width for the layout</li>
288  * <li>{@code android:minHeight} set the minimum height for the layout</li>
289  * <li>{@code android:maxWidth} set the maximum width for the layout</li>
290  * <li>{@code android:maxHeight} set the maximum height for the layout</li>
291  * </ul>
292  * Those minimum and maximum dimensions will be used by
293  * {@code ConstraintLayout} when its dimensions are set to {@code WRAP_CONTENT}.
294  * </p>
295  * <b>Widgets dimension constraints</b>
296  * <p>
297  * The dimension of the widgets can be specified by setting the
298  * {@code android:layout_width} and
299  * {@code android:layout_height} attributes in 3 different ways:
300  * <ul>
301  * <li>Using a specific dimension (either a literal value such as
302  * {@code 123dp} or a {@code Dimension} reference)</li>
303  * <li>Using {@code WRAP_CONTENT}, which will ask the widget to compute its own size</li>
304  * <li>Using {@code 0dp}, which is the equivalent of "{@code MATCH_CONSTRAINT}"</li>
305  * </ul>
306  * <p> The first two works in a similar fashion as other layouts.
307  * The last one will resize the widget in such a way as
308  * matching the constraints that are set. If margins are set, they will be taken in account
309  * in the computation.</p>
310  * <p>
311  * <b>Important: </b> {@code MATCH_PARENT} is not recommended for widgets
312  * contained in a {@code ConstraintLayout}. Similar behavior can
313  * be defined by using {@code MATCH_CONSTRAINT} with the corresponding
314  * left/right or top/bottom constraints being set to {@code "parent"}.
315  * </p>
316  * </p>
317  * <b>WRAP_CONTENT : enforcing constraints (<i>Added in 1.1</i>)</b>
318  * <p>
319  * If a dimension is set to {@code WRAP_CONTENT}, in versions before 1.1
320  * they will be treated as a literal dimension -- meaning, constraints will
321  * not limit the resulting dimension. While in general this is enough (and faster),
322  * in some situations, you might want to use {@code WRAP_CONTENT},
323  * yet keep enforcing constraints to limit the resulting dimension. In that case,
324  * you can add one of the corresponding attribute:
325  * <ul>
326  * <li>{@code app:layout_constrainedWidth="true|false"}</li>
327  * <li>{@code app:layout_constrainedHeight="true|false"}</li>
328  * </ul>
329  * </p>
330  * <b>MATCH_CONSTRAINT dimensions (<i>Added in 1.1</i>)</b>
331  * <p>
332  * When a dimension is set to {@code MATCH_CONSTRAINT},
333  * the default behavior is to have the resulting size take all the available space.
334  * Several additional modifiers are available:
335  * </p>
336  * <ul>
337  * <li>{@code layout_constraintWidth_min} and {@code layout_constraintHeight_min} :
338  * will set the minimum size for this dimension</li>
339  * <li>{@code layout_constraintWidth_max} and {@code layout_constraintHeight_max} :
340  * will set the maximum size for this dimension</li>
341  * <li>{@code layout_constraintWidth_percent} and {@code layout_constraintHeight_percent} :
342  * will set the size of this dimension as a percentage of the parent</li>
343  * </ul>
344  * <b>Min and Max</b>
345  * <p>The value indicated for min and max can be either a dimension in Dp,
346  * or "wrap", which will use the same value as what {@code WRAP_CONTENT} would do.</p>
347  * <b>Percent dimension</b>
348  * <p>To use percent, you need to set the following</p>
349  * <ul>
350  * <li>The dimension should be set to {@code MATCH_CONSTRAINT} (0dp)</li>
351  * <li>The default should be set to percent {@code app:layout_constraintWidth_default="percent"}
352  * or {@code app:layout_constraintHeight_default="percent"}</li>
353  * <li>Then set the {@code layout_constraintWidth_percent}
354  * or {@code layout_constraintHeight_percent} attributes to a value between 0 and 1</li>
355  * </ul>
356  * <b>Ratio</b>
357  * <p>
358  * You can also define one dimension of a widget as a ratio of the other one.
359  * In order to do that, you
360  * need to have at least one constrained dimension be set to
361  * {@code 0dp} (i.e., {@code MATCH_CONSTRAINT}), and set the
362  * attribute {@code layout_constraintDimensionRatio} to a given ratio.
363  * For example:
364  * <pre>
365  *         {@code
366  *           <Button android:layout_width="wrap_content"
367  *                   android:layout_height="0dp"
368  *                   app:layout_constraintDimensionRatio="1:1" />
369  *         }
370  *     </pre>
371  * will set the height of the button to be the same as its width.
372  * </p>
373  * <p> The ratio can be expressed either as:
374  * <ul>
375  * <li>a float value, representing a ratio between width and height</li>
376  * <li>a ratio in the form "width:height"</li>
377  * </ul>
378  * </p>
379  * <p>
380  * You can also use ratio if both dimensions are set to
381  * {@code MATCH_CONSTRAINT} (0dp). In this case the system sets the
382  * largest dimensions that satisfies all constraints and maintains
383  * the aspect ratio specified. To constrain one specific side
384  * based on the dimensions of another, you can pre append
385  * {@code W,}" or {@code H,} to constrain the width or height
386  * respectively.
387  * For example,
388  * If one dimension is constrained by two targets
389  * (e.g. width is 0dp and centered on parent) you can indicate which
390  * side should be constrained, by adding the letter
391  * {@code W} (for constraining the width) or {@code H}
392  * (for constraining the height) in front of the ratio, separated
393  * by a comma:
394  * <pre>
395  *         {@code
396  *           <Button android:layout_width="0dp"
397  *                   android:layout_height="0dp"
398  *                   app:layout_constraintDimensionRatio="H,16:9"
399  *                   app:layout_constraintBottom_toBottomOf="parent"
400  *                   app:layout_constraintTop_toTopOf="parent"/>
401  *         }
402  *     </pre>
403  * will set the height of the button following a 16:9 ratio,
404  * while the width of the button will match the constraints
405  * to its parent.
406  *
407  * </p>
408  *
409  * <h3 id="Chains">Chains</h3>
410  * <p>Chains provide group-like behavior in a single axis (horizontally or vertically).
411  * The other axis can be constrained independently.</p>
412  * <b>Creating a chain</b>
413  * <p>
414  * A set of widgets are considered a chain if they are linked together via a
415  * bi-directional connection.
416  * </p>
417  * <b>Chain heads</b>
418  * <p>
419  * Chains are controlled by attributes set on the first element of the chain
420  * (the "head" of the chain):
421  * </p>
422  * <p>The head is the left-most widget for horizontal chains,
423  * and the top-most widget for vertical chains.</p>
424  * <b>Margins in chains</b>
425  * <p>If margins are specified on connections, they will be taken into account.
426  * In the case of spread chains, margins will be deducted from the allocated space.</p>
427  * <b>Chain Style</b>
428  * <p>When setting the attribute {@code layout_constraintHorizontal_chainStyle} or
429  * {@code layout_constraintVertical_chainStyle} on the first element of a chain,
430  * the behavior of the chain will change according to the specified style
431  * (default is {@code CHAIN_SPREAD}).
432  * <ul>
433  * <li>{@code CHAIN_SPREAD} -- the elements will be spread out (default style)</li>
434  * <li>Weighted chain -- in {@code CHAIN_SPREAD} mode,
435  * if some widgets are set to {@code MATCH_CONSTRAINT}, they will split the available space</li>
436  * <li>{@code CHAIN_SPREAD_INSIDE} -- similar,
437  * but the endpoints of the chain will not be spread out</li>
438  * <li>{@code CHAIN_PACKED} -- the elements of the chain will be packed together.
439  * The horizontal or vertical
440  * bias attribute of the child will then affect the positioning of the packed elements</li>
441  * </ul>
442  * <b>Weighted chains</b>
443  * <p>The default behavior of a chain is to spread the elements equally in the available space.
444  * If one or more elements are using {@code MATCH_CONSTRAINT}, they
445  * will use the available empty space (equally divided among themselves).
446  * The attribute {@code layout_constraintHorizontal_weight} and
447  * {@code layout_constraintVertical_weight}
448  * will control how the space will be distributed among the elements using
449  * {@code MATCH_CONSTRAINT}. For example,
450  * on a chain containing two elements using {@code MATCH_CONSTRAINT},
451  * with the first element using a weight of 2 and the second a weight of 1,
452  * the space occupied by the first element will be twice that of the second element.</p>
453  *
454  * <b>Margins and chains (<i>in 1.1</i>)</b>
455  * <p>When using margins on elements in a chain, the margins are additive.</p>
456  * <p>For example, on a horizontal chain, if one element defines
457  * a right margin of 10dp and the next element
458  * defines a left margin of 5dp, the resulting margin between those
459  * two elements is 15dp.</p>
460  * <p>An item plus its margins are considered together when calculating
461  * leftover space used by chains
462  * to position items. The leftover space does not contain the margins.</p>
463  *
464  * <h3 id="VirtualHelpers"> Virtual Helper objects </h3>
465  * <p>In addition to the intrinsic capabilities detailed previously,
466  * you can also use special helper objects
467  * in {@code ConstraintLayout} to help you with your layout. Currently, the
468  * {@link Guideline} object allows you to create Horizontal and Vertical
469  * guidelines which are positioned relative to the {@code ConstraintLayout}
470  * container. Widgets can then be positioned by constraining them to such
471  * guidelines. In <b>1.1</b>, {@link Barrier} and {@link Group} were added too.
472  * </p>
473  *
474  * <h3 id="Optimizer">Optimizer (<i><b>in 1.1</b></i>)</h3>
475  * <p>
476  * In 1.1 we exposed the constraints optimizer. You can decide which optimizations
477  * are applied by adding the tag <i>app:layout_optimizationLevel</i>
478  * to the ConstraintLayout element.
479  * <ul>
480  * <li><b>none</b> : no optimizations are applied</li>
481  * <li><b>standard</b> : Default. Optimize direct and barrier constraints only</li>
482  * <li><b>direct</b> : optimize direct constraints</li>
483  * <li><b>barrier</b> : optimize barrier constraints</li>
484  * <li><b>chain</b> : optimize chain constraints (experimental)</li>
485  * <li><b>dimensions</b> : optimize dimensions measures (experimental),
486  * reducing the number of measures of match constraints elements</li>
487  * </ul>
488  * </p>
489  * <p>This attribute is a mask, so you can decide to turn on or off
490  * specific optimizations by listing the ones you want.
491  * For example: <i>app:layout_optimizationLevel="direct|barrier|chain"</i> </p>
492  */
493 public class ConstraintLayout extends ViewGroup {
494     /**
495      *
496      */
497     public static final String VERSION = "ConstraintLayout-2.2.0-alpha04";
498     private static final String TAG = "ConstraintLayout";
499 
500     private static final boolean USE_CONSTRAINTS_HELPER = true;
501     private static final boolean DEBUG = LinearSystem.FULL_DEBUG;
502     private static final boolean DEBUG_DRAW_CONSTRAINTS = false;
503     private static final boolean OPTIMIZE_HEIGHT_CHANGE = false;
504 
505     SparseArray<View> mChildrenByIds = new SparseArray<>();
506 
507     // This array keep a list of helper objects if they are present
508     private ArrayList<ConstraintHelper> mConstraintHelpers = new ArrayList<>(4);
509 
510     protected ConstraintWidgetContainer mLayoutWidget = new ConstraintWidgetContainer();
511 
512     private int mMinWidth = 0;
513     private int mMinHeight = 0;
514     private int mMaxWidth = Integer.MAX_VALUE;
515     private int mMaxHeight = Integer.MAX_VALUE;
516 
517     protected boolean mDirtyHierarchy = true;
518     private int mOptimizationLevel = Optimizer.OPTIMIZATION_STANDARD;
519     private ConstraintSet mConstraintSet = null;
520     protected ConstraintLayoutStates mConstraintLayoutSpec = null;
521 
522     private int mConstraintSetId = -1;
523 
524     private HashMap<String, Integer> mDesignIds = new HashMap<>();
525 
526     // Cache last measure
527     private int mLastMeasureWidth = -1;
528     private int mLastMeasureHeight = -1;
529     int mLastMeasureWidthSize = -1;
530     int mLastMeasureHeightSize = -1;
531     int mLastMeasureWidthMode = MeasureSpec.UNSPECIFIED;
532     int mLastMeasureHeightMode = MeasureSpec.UNSPECIFIED;
533     private SparseArray<ConstraintWidget> mTempMapIdToWidget = new SparseArray<>();
534 
535     /**
536      *
537      */
538     public static final int DESIGN_INFO_ID = 0;
539    // private ConstraintsChangedListener mConstraintsChangedListener;
540     private Metrics mMetrics;
541 
542     private static SharedValues sSharedValues = null;
543 
544     /**
545      * Returns the SharedValues instance, creating it if it doesn't exist.
546      *
547      * @return the SharedValues instance
548      */
getSharedValues()549     public static SharedValues getSharedValues() {
550         if (sSharedValues == null) {
551             sSharedValues = new SharedValues();
552         }
553         return sSharedValues;
554     }
555 
556     /**
557      *
558      */
setDesignInformation(int type, Object value1, Object value2)559     public void setDesignInformation(int type, Object value1, Object value2) {
560         if (type == DESIGN_INFO_ID
561                 && value1 instanceof String
562                 && value2 instanceof Integer) {
563             if (mDesignIds == null) {
564                 mDesignIds = new HashMap<>();
565             }
566             String name = (String) value1;
567             int index = name.indexOf("/");
568             if (index != -1) {
569                 name = name.substring(index + 1);
570             }
571             int id = (Integer) value2;
572             mDesignIds.put(name, id);
573         }
574     }
575 
576     /**
577      *
578      */
getDesignInformation(int type, Object value)579     public Object getDesignInformation(int type, Object value) {
580         if (type == DESIGN_INFO_ID && value instanceof String) {
581             String name = (String) value;
582             if (mDesignIds != null && mDesignIds.containsKey(name)) {
583                 return mDesignIds.get(name);
584             }
585         }
586         return null;
587     }
588 
ConstraintLayout(@onNull Context context)589     public ConstraintLayout(@NonNull Context context) {
590         super(context);
591         init(null, 0, 0);
592     }
593 
ConstraintLayout(@onNull Context context, @Nullable AttributeSet attrs)594     public ConstraintLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
595         super(context, attrs);
596         init(attrs, 0, 0);
597     }
598 
ConstraintLayout(@onNull Context context, @Nullable AttributeSet attrs, int defStyleAttr)599     public ConstraintLayout(@NonNull Context context,
600                             @Nullable AttributeSet attrs,
601                             int defStyleAttr) {
602         super(context, attrs, defStyleAttr);
603         init(attrs, defStyleAttr, 0);
604     }
605 
606     @TargetApi(Build.VERSION_CODES.LOLLIPOP)
ConstraintLayout(@onNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes)607     public ConstraintLayout(@NonNull Context context,
608                             @Nullable AttributeSet attrs,
609                             int defStyleAttr,
610                             int defStyleRes) {
611         super(context, attrs, defStyleAttr, defStyleRes);
612         init(attrs, defStyleAttr, defStyleRes);
613     }
614 
615     /**
616      *
617      */
618     @Override
setId(int id)619     public void setId(int id) {
620         mChildrenByIds.remove(getId());
621         super.setId(id);
622         mChildrenByIds.put(getId(), this);
623     }
624 
625     // -------------------------------------------------------------------------------------------
626     // Measure widgets callbacks
627     // -------------------------------------------------------------------------------------------
628 
629 
630     // -------------------------------------------------------------------------------------------
631 
632     class Measurer implements BasicMeasure.Measurer {
633         ConstraintLayout mLayout;
634         int mPaddingTop;
635         int mPaddingBottom;
636         int mPaddingWidth;
637         int mPaddingHeight;
638         int mLayoutWidthSpec;
639         int mLayoutHeightSpec;
640 
captureLayoutInfo(int widthSpec, int heightSpec, int top, int bottom, int width, int height)641         public void captureLayoutInfo(int widthSpec,
642                                       int heightSpec,
643                                       int top,
644                                       int bottom,
645                                       int width,
646                                       int height) {
647             mPaddingTop = top;
648             mPaddingBottom = bottom;
649             mPaddingWidth = width;
650             mPaddingHeight = height;
651             mLayoutWidthSpec = widthSpec;
652             mLayoutHeightSpec = heightSpec;
653         }
654 
Measurer(ConstraintLayout l)655         Measurer(ConstraintLayout l) {
656             mLayout = l;
657         }
658 
659         @SuppressLint("WrongCall")
660         @Override
measure(ConstraintWidget widget, BasicMeasure.Measure measure)661         public final void measure(ConstraintWidget widget,
662                                   BasicMeasure.Measure measure) {
663             if (widget == null) {
664                 return;
665             }
666             if (widget.getVisibility() == GONE && !widget.isInPlaceholder()) {
667                 measure.measuredWidth = 0;
668                 measure.measuredHeight = 0;
669                 measure.measuredBaseline = 0;
670                 return;
671             }
672             if (widget.getParent() == null) {
673                 return;
674             }
675 
676             long startMeasure = 0;
677             long endMeasure;
678 
679             if (mMetrics != null) {
680                 mMetrics.mNumberOfMeasures++;
681                 startMeasure = System.nanoTime();
682             }
683 
684             ConstraintWidget.DimensionBehaviour horizontalBehavior = measure.horizontalBehavior;
685             ConstraintWidget.DimensionBehaviour verticalBehavior = measure.verticalBehavior;
686 
687             int horizontalDimension = measure.horizontalDimension;
688             int verticalDimension = measure.verticalDimension;
689 
690             int horizontalSpec = 0;
691             int verticalSpec = 0;
692 
693             int heightPadding = mPaddingTop + mPaddingBottom;
694             int widthPadding = mPaddingWidth;
695 
696             View child = (View) widget.getCompanionWidget();
697 
698             switch (horizontalBehavior) {
699                 case FIXED: {
700                     horizontalSpec = MeasureSpec.makeMeasureSpec(horizontalDimension,
701                             MeasureSpec.EXACTLY);
702                 }
703                 break;
704                 case WRAP_CONTENT: {
705                     horizontalSpec = getChildMeasureSpec(mLayoutWidthSpec,
706                             widthPadding, WRAP_CONTENT);
707                 }
708                 break;
709                 case MATCH_PARENT: {
710                     // Horizontal spec must account for margin as well as padding here.
711                     horizontalSpec = getChildMeasureSpec(mLayoutWidthSpec,
712                             widthPadding + widget.getHorizontalMargin(),
713                             LayoutParams.MATCH_PARENT);
714                 }
715                 break;
716                 case MATCH_CONSTRAINT: {
717                     horizontalSpec = getChildMeasureSpec(mLayoutWidthSpec,
718                             widthPadding, WRAP_CONTENT);
719                     boolean shouldDoWrap = widget.mMatchConstraintDefaultWidth
720                             == MATCH_CONSTRAINT_WRAP;
721                     if (measure.measureStrategy == BasicMeasure.Measure.TRY_GIVEN_DIMENSIONS
722                             || measure.measureStrategy
723                             == BasicMeasure.Measure.USE_GIVEN_DIMENSIONS) {
724                         // the solver gives us our new dimension,
725                         // but if we previously had it measured with
726                         // a wrap, it can be incorrect if the other side was also variable.
727                         // So in that case, we have to double-check the
728                         // other side is stable (else we can't
729                         // just assume the wrap value will be correct).
730                         boolean otherDimensionStable = child.getMeasuredHeight()
731                                 == widget.getHeight();
732                         boolean useCurrent = measure.measureStrategy
733                                 == BasicMeasure.Measure.USE_GIVEN_DIMENSIONS
734                                             || !shouldDoWrap
735                                             || (shouldDoWrap && otherDimensionStable)
736                                             || child instanceof Placeholder
737                                             || widget.isResolvedHorizontally();
738                         if (useCurrent) {
739                             horizontalSpec = MeasureSpec.makeMeasureSpec(widget.getWidth(),
740                                     MeasureSpec.EXACTLY);
741                         }
742                     }
743                 }
744                 break;
745             }
746 
747             switch (verticalBehavior) {
748                 case FIXED: {
749                     verticalSpec = MeasureSpec.makeMeasureSpec(verticalDimension,
750                             MeasureSpec.EXACTLY);
751                 }
752                 break;
753                 case WRAP_CONTENT: {
754                     verticalSpec = getChildMeasureSpec(mLayoutHeightSpec,
755                             heightPadding, WRAP_CONTENT);
756                 }
757                 break;
758                 case MATCH_PARENT: {
759                     // Vertical spec must account for margin as well as padding here.
760                     verticalSpec = getChildMeasureSpec(mLayoutHeightSpec,
761                             heightPadding + widget.getVerticalMargin(),
762                             LayoutParams.MATCH_PARENT);
763                 }
764                 break;
765                 case MATCH_CONSTRAINT: {
766                     verticalSpec = getChildMeasureSpec(mLayoutHeightSpec,
767                             heightPadding, WRAP_CONTENT);
768                     boolean shouldDoWrap = widget.mMatchConstraintDefaultHeight
769                             == MATCH_CONSTRAINT_WRAP;
770                     if (measure.measureStrategy == BasicMeasure.Measure.TRY_GIVEN_DIMENSIONS
771                             || measure.measureStrategy
772                             == BasicMeasure.Measure.USE_GIVEN_DIMENSIONS) {
773                         // the solver gives us our new dimension,
774                         // but if we previously had it measured with
775                         // a wrap, it can be incorrect if the other side was also variable.
776                         // So in that case, we have to double-check
777                         // the other side is stable (else we can't
778                         // just assume the wrap value will be correct).
779                         boolean otherDimensionStable = child.getMeasuredWidth()
780                                 == widget.getWidth();
781                         boolean useCurrent = measure.measureStrategy
782                                 == BasicMeasure.Measure.USE_GIVEN_DIMENSIONS
783                                             || !shouldDoWrap
784                                             || (shouldDoWrap && otherDimensionStable)
785                                             || (child instanceof Placeholder)
786                                             || widget.isResolvedVertically();
787                         if (useCurrent) {
788                             verticalSpec = MeasureSpec.makeMeasureSpec(widget.getHeight(),
789                                     MeasureSpec.EXACTLY);
790                         }
791                     }
792                 }
793                 break;
794             }
795 
796             ConstraintWidgetContainer container = (ConstraintWidgetContainer) widget.getParent();
797             if (container != null && Optimizer.enabled(mOptimizationLevel,
798                     Optimizer.OPTIMIZATION_CACHE_MEASURES)) {
799                 if (child.getMeasuredWidth() == widget.getWidth()
800                         // note: the container check replicates legacy behavior, but we might want
801                         // to not enforce that in 3.0
802                         && child.getMeasuredWidth() < container.getWidth()
803                         && child.getMeasuredHeight() == widget.getHeight()
804                         && child.getMeasuredHeight() < container.getHeight()
805                         && child.getBaseline() == widget.getBaselineDistance()
806                         && !widget.isMeasureRequested()
807                 ) {
808                     boolean similar = isSimilarSpec(widget.getLastHorizontalMeasureSpec(),
809                             horizontalSpec, widget.getWidth())
810                             && isSimilarSpec(widget.getLastVerticalMeasureSpec(),
811                             verticalSpec, widget.getHeight());
812                     if (similar) {
813                         measure.measuredWidth = widget.getWidth();
814                         measure.measuredHeight = widget.getHeight();
815                         measure.measuredBaseline = widget.getBaselineDistance();
816                         // if the dimensions of the solver widget are already the
817                         // same as the real view, no need to remeasure.
818                         if (DEBUG) {
819                             System.out.println("SKIPPED " + widget);
820                         }
821                         return;
822                     }
823                 }
824             }
825 
826             boolean horizontalMatchConstraints = (horizontalBehavior
827                     == ConstraintWidget.DimensionBehaviour.MATCH_CONSTRAINT);
828             boolean verticalMatchConstraints = (verticalBehavior
829                     == ConstraintWidget.DimensionBehaviour.MATCH_CONSTRAINT);
830 
831             boolean verticalDimensionKnown = (verticalBehavior
832                     == ConstraintWidget.DimensionBehaviour.MATCH_PARENT
833                     || verticalBehavior == ConstraintWidget.DimensionBehaviour.FIXED);
834             boolean horizontalDimensionKnown = (horizontalBehavior
835                     == ConstraintWidget.DimensionBehaviour.MATCH_PARENT
836                     || horizontalBehavior == ConstraintWidget.DimensionBehaviour.FIXED);
837             boolean horizontalUseRatio = horizontalMatchConstraints && widget.mDimensionRatio > 0;
838             boolean verticalUseRatio = verticalMatchConstraints && widget.mDimensionRatio > 0;
839 
840             if (child == null) {
841                 return;
842             }
843             LayoutParams params = (LayoutParams) child.getLayoutParams();
844 
845             int width = 0;
846             int height = 0;
847             int baseline = 0;
848 
849             if ((measure.measureStrategy == BasicMeasure.Measure.TRY_GIVEN_DIMENSIONS
850                     || measure.measureStrategy == BasicMeasure.Measure.USE_GIVEN_DIMENSIONS)
851                     || !(horizontalMatchConstraints
852                     && widget.mMatchConstraintDefaultWidth == MATCH_CONSTRAINT_SPREAD
853                     && verticalMatchConstraints
854                     && widget.mMatchConstraintDefaultHeight == MATCH_CONSTRAINT_SPREAD)) {
855 
856                 if (child instanceof VirtualLayout
857                         && widget instanceof androidx.constraintlayout.core.widgets.VirtualLayout) {
858                     androidx.constraintlayout.core.widgets.VirtualLayout layout =
859                             (androidx.constraintlayout.core.widgets.VirtualLayout) widget;
860                     ((VirtualLayout) child).onMeasure(layout, horizontalSpec, verticalSpec);
861                 } else {
862                     child.measure(horizontalSpec, verticalSpec);
863                 }
864                 widget.setLastMeasureSpec(horizontalSpec, verticalSpec);
865 
866                 int w = child.getMeasuredWidth();
867                 int h = child.getMeasuredHeight();
868                 baseline = child.getBaseline();
869 
870                 width = w;
871                 height = h;
872 
873                 if (DEBUG) {
874                     String measurement = MeasureSpec.toString(horizontalSpec)
875                             + " x " + MeasureSpec.toString(verticalSpec)
876                             + " => " + width + " x " + height;
877                     System.out.println("    (M) measure "
878                             + " (" + widget.getDebugName() + ") : " + measurement);
879                 }
880 
881                 if (widget.mMatchConstraintMinWidth > 0) {
882                     width = Math.max(widget.mMatchConstraintMinWidth, width);
883                 }
884                 if (widget.mMatchConstraintMaxWidth > 0) {
885                     width = Math.min(widget.mMatchConstraintMaxWidth, width);
886                 }
887                 if (widget.mMatchConstraintMinHeight > 0) {
888                     height = Math.max(widget.mMatchConstraintMinHeight, height);
889                 }
890                 if (widget.mMatchConstraintMaxHeight > 0) {
891                     height = Math.min(widget.mMatchConstraintMaxHeight, height);
892                 }
893 
894                 boolean optimizeDirect = Optimizer.enabled(mOptimizationLevel,
895                         Optimizer.OPTIMIZATION_DIRECT);
896                 if (!optimizeDirect) {
897                     if (horizontalUseRatio && verticalDimensionKnown) {
898                         float ratio = widget.mDimensionRatio;
899                         width = (int) (0.5f + height * ratio);
900                     } else if (verticalUseRatio && horizontalDimensionKnown) {
901                         float ratio = widget.mDimensionRatio;
902                         height = (int) (0.5f + width / ratio);
903                     }
904                 }
905 
906                 if (w != width || h != height) {
907                     if (w != width) {
908                         horizontalSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
909                     }
910                     if (h != height) {
911                         verticalSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
912                     }
913                     child.measure(horizontalSpec, verticalSpec);
914 
915                     widget.setLastMeasureSpec(horizontalSpec, verticalSpec);
916                     width = child.getMeasuredWidth();
917                     height = child.getMeasuredHeight();
918                     baseline = child.getBaseline();
919                     if (DEBUG) {
920                         String measurement2 = MeasureSpec.toString(horizontalSpec)
921                                 + " x " + MeasureSpec.toString(verticalSpec)
922                                 + " => " + width + " x " + height;
923                         System.out.println("measure (b) " + widget.getDebugName()
924                                 + " : " + measurement2);
925                     }
926                 }
927 
928             }
929 
930             boolean hasBaseline = baseline != -1;
931 
932             measure.measuredNeedsSolverPass = (width != measure.horizontalDimension)
933                     || (height != measure.verticalDimension);
934             if (params.mNeedsBaseline) {
935                 hasBaseline = true;
936             }
937             if (hasBaseline && baseline != -1 && widget.getBaselineDistance() != baseline) {
938                 measure.measuredNeedsSolverPass = true;
939             }
940             measure.measuredWidth = width;
941             measure.measuredHeight = height;
942             measure.measuredHasBaseline = hasBaseline;
943             measure.measuredBaseline = baseline;
944             if (mMetrics != null) {
945                 endMeasure = System.nanoTime();
946                 mMetrics.measuresWidgetsDuration += (endMeasure - startMeasure);
947             }
948         }
949 
950         /**
951          * Returns true if the previous measure spec is equivalent to the new one.
952          * - if it's the same...
953          * - if it's not, but the previous was AT_MOST or UNSPECIFIED and the new one
954          *   is EXACTLY with the same size.
955          *
956          * @param lastMeasureSpec
957          * @param spec
958          * @param widgetSize
959          * @return
960          */
isSimilarSpec(int lastMeasureSpec, int spec, int widgetSize)961         private boolean isSimilarSpec(int lastMeasureSpec, int spec, int widgetSize) {
962             if (lastMeasureSpec == spec) {
963                 return true;
964             }
965             int lastMode = MeasureSpec.getMode(lastMeasureSpec);
966             int mode = MeasureSpec.getMode(spec);
967             int size = MeasureSpec.getSize(spec);
968             return mode == MeasureSpec.EXACTLY
969                     && (lastMode == MeasureSpec.AT_MOST || lastMode == MeasureSpec.UNSPECIFIED)
970                     && widgetSize == size;
971         }
972 
973         @Override
didMeasures()974         public final void didMeasures() {
975             final int widgetsCount = mLayout.getChildCount();
976             for (int i = 0; i < widgetsCount; i++) {
977                 final View child = mLayout.getChildAt(i);
978                 if (child instanceof Placeholder) {
979                     ((Placeholder) child).updatePostMeasure(mLayout);
980                 }
981             }
982             // TODO refactor into an updatePostMeasure interface
983             final int helperCount = mLayout.mConstraintHelpers.size();
984             if (helperCount > 0) {
985                 for (int i = 0; i < helperCount; i++) {
986                     ConstraintHelper helper = mLayout.mConstraintHelpers.get(i);
987                     helper.updatePostMeasure(mLayout);
988                 }
989             }
990         }
991     }
992 
993     Measurer mMeasurer = new Measurer(this);
994 
init(AttributeSet attrs, int defStyleAttr, int defStyleRes)995     private void init(AttributeSet attrs, int defStyleAttr, int defStyleRes) {
996         mLayoutWidget.setCompanionWidget(this);
997         mLayoutWidget.setMeasurer(mMeasurer);
998         mChildrenByIds.put(getId(), this);
999         mConstraintSet = null;
1000         if (attrs != null) {
1001             TypedArray a = getContext().obtainStyledAttributes(attrs,
1002                     R.styleable.ConstraintLayout_Layout, defStyleAttr, defStyleRes);
1003             final int count = a.getIndexCount();
1004             for (int i = 0; i < count; i++) {
1005                 int attr = a.getIndex(i);
1006                 if (attr == R.styleable.ConstraintLayout_Layout_android_minWidth) {
1007                     mMinWidth = a.getDimensionPixelOffset(attr, mMinWidth);
1008                 } else if (attr == R.styleable.ConstraintLayout_Layout_android_minHeight) {
1009                     mMinHeight = a.getDimensionPixelOffset(attr, mMinHeight);
1010                 } else if (attr == R.styleable.ConstraintLayout_Layout_android_maxWidth) {
1011                     mMaxWidth = a.getDimensionPixelOffset(attr, mMaxWidth);
1012                 } else if (attr == R.styleable.ConstraintLayout_Layout_android_maxHeight) {
1013                     mMaxHeight = a.getDimensionPixelOffset(attr, mMaxHeight);
1014                 } else if (attr == R.styleable.ConstraintLayout_Layout_layout_optimizationLevel) {
1015                     mOptimizationLevel = a.getInt(attr, mOptimizationLevel);
1016                 } else if (attr == R.styleable.ConstraintLayout_Layout_layoutDescription) {
1017                     int id = a.getResourceId(attr, 0);
1018                     if (id != 0) {
1019                         try {
1020                             parseLayoutDescription(id);
1021                         } catch (Resources.NotFoundException e) {
1022                             mConstraintLayoutSpec = null;
1023                         }
1024                     }
1025                 } else if (attr == R.styleable.ConstraintLayout_Layout_constraintSet) {
1026                     int id = a.getResourceId(attr, 0);
1027                     try {
1028                         mConstraintSet = new ConstraintSet();
1029                         mConstraintSet.load(getContext(), id);
1030                     } catch (Resources.NotFoundException e) {
1031                         mConstraintSet = null;
1032                     }
1033                     mConstraintSetId = id;
1034                 }
1035             }
1036             a.recycle();
1037         }
1038         mLayoutWidget.setOptimizationLevel(mOptimizationLevel);
1039     }
1040 
1041     /**
1042      * Subclasses can override the handling of layoutDescription
1043      *
1044      * @param id
1045      */
parseLayoutDescription(int id)1046     protected void parseLayoutDescription(int id) {
1047         mConstraintLayoutSpec = new ConstraintLayoutStates(getContext(), this, id);
1048     }
1049 
1050     /**
1051      *
1052      */
1053     @Override
onViewAdded(View view)1054     public void onViewAdded(View view) {
1055         super.onViewAdded(view);
1056         ConstraintWidget widget = getViewWidget(view);
1057         if (view instanceof androidx.constraintlayout.widget.Guideline) {
1058             if (!(widget instanceof Guideline)) {
1059                 LayoutParams layoutParams = (LayoutParams) view.getLayoutParams();
1060                 layoutParams.mWidget = new Guideline();
1061                 layoutParams.mIsGuideline = true;
1062                 ((Guideline) layoutParams.mWidget).setOrientation(layoutParams.orientation);
1063             }
1064         }
1065         if (view instanceof ConstraintHelper) {
1066             ConstraintHelper helper = (ConstraintHelper) view;
1067             helper.validateParams();
1068             LayoutParams layoutParams = (LayoutParams) view.getLayoutParams();
1069             layoutParams.mIsHelper = true;
1070             if (!mConstraintHelpers.contains(helper)) {
1071                 mConstraintHelpers.add(helper);
1072             }
1073         }
1074         mChildrenByIds.put(view.getId(), view);
1075         mDirtyHierarchy = true;
1076     }
1077 
1078     /**
1079      *
1080      */
1081     @Override
onViewRemoved(View view)1082     public void onViewRemoved(View view) {
1083         super.onViewRemoved(view);
1084         mChildrenByIds.remove(view.getId());
1085         ConstraintWidget widget = getViewWidget(view);
1086         mLayoutWidget.remove(widget);
1087         mConstraintHelpers.remove(view);
1088         mDirtyHierarchy = true;
1089     }
1090 
1091     /**
1092      * Set the min width for this view
1093      *
1094      * @param value
1095      */
setMinWidth(int value)1096     public void setMinWidth(int value) {
1097         if (value == mMinWidth) {
1098             return;
1099         }
1100         mMinWidth = value;
1101         requestLayout();
1102     }
1103 
1104     /**
1105      * Set the min height for this view
1106      *
1107      * @param value
1108      */
setMinHeight(int value)1109     public void setMinHeight(int value) {
1110         if (value == mMinHeight) {
1111             return;
1112         }
1113         mMinHeight = value;
1114         requestLayout();
1115     }
1116 
1117     /**
1118      * The minimum width of this view.
1119      *
1120      * @return The minimum width of this view
1121      * @see #setMinWidth(int)
1122      */
getMinWidth()1123     public int getMinWidth() {
1124         return mMinWidth;
1125     }
1126 
1127     /**
1128      * The minimum height of this view.
1129      *
1130      * @return The minimum height of this view
1131      * @see #setMinHeight(int)
1132      */
getMinHeight()1133     public int getMinHeight() {
1134         return mMinHeight;
1135     }
1136 
1137     /**
1138      * Set the max width for this view
1139      *
1140      * @param value
1141      */
setMaxWidth(int value)1142     public void setMaxWidth(int value) {
1143         if (value == mMaxWidth) {
1144             return;
1145         }
1146         mMaxWidth = value;
1147         requestLayout();
1148     }
1149 
1150     /**
1151      * Set the max height for this view
1152      *
1153      * @param value
1154      */
setMaxHeight(int value)1155     public void setMaxHeight(int value) {
1156         if (value == mMaxHeight) {
1157             return;
1158         }
1159         mMaxHeight = value;
1160         requestLayout();
1161     }
1162 
1163     /*
1164      * The maximum width of this view.
1165      *
1166      * @return The maximum width of this view
1167      *
1168      * @see #setMaxWidth(int)
1169      */
getMaxWidth()1170     public int getMaxWidth() {
1171         return mMaxWidth;
1172     }
1173 
1174     /**
1175      * The maximum height of this view.
1176      *
1177      * @return The maximum height of this view
1178      * @see #setMaxHeight(int)
1179      */
getMaxHeight()1180     public int getMaxHeight() {
1181         return mMaxHeight;
1182     }
1183 
updateHierarchy()1184     private boolean updateHierarchy() {
1185         final int count = getChildCount();
1186 
1187         boolean recompute = false;
1188         for (int i = 0; i < count; i++) {
1189             final View child = getChildAt(i);
1190             if (child.isLayoutRequested()) {
1191                 recompute = true;
1192                 break;
1193             }
1194         }
1195         if (recompute) {
1196             setChildrenConstraints();
1197         }
1198         return recompute;
1199     }
1200 
setChildrenConstraints()1201     private void setChildrenConstraints() {
1202         final boolean isInEditMode = DEBUG || isInEditMode();
1203 
1204         final int count = getChildCount();
1205 
1206         // Make sure everything is fully reset before anything else
1207         for (int i = 0; i < count; i++) {
1208             View child = getChildAt(i);
1209             ConstraintWidget widget = getViewWidget(child);
1210             if (widget == null) {
1211                 continue;
1212             }
1213             widget.reset();
1214         }
1215 
1216         if (isInEditMode) {
1217             // In design mode, let's make sure we keep track of the ids; in Studio, a build step
1218             // might not have been done yet, so asking the system for ids can break. So to be safe,
1219             // we save the current ids, which helpers can ask for.
1220             for (int i = 0; i < count; i++) {
1221                 final View view = getChildAt(i);
1222                 try {
1223                     String IdAsString = getResources().getResourceName(view.getId());
1224                     setDesignInformation(DESIGN_INFO_ID, IdAsString, view.getId());
1225                     int slashIndex = IdAsString.indexOf('/');
1226                     if (slashIndex != -1) {
1227                         IdAsString = IdAsString.substring(slashIndex + 1);
1228                     }
1229                     getTargetWidget(view.getId()).setDebugName(IdAsString);
1230                 } catch (Resources.NotFoundException e) {
1231                     // nothing
1232                 }
1233             }
1234         } else if (DEBUG) {
1235             mLayoutWidget.setDebugName("root");
1236             for (int i = 0; i < count; i++) {
1237                 final View view = getChildAt(i);
1238                 try {
1239                     String IdAsString = getResources().getResourceName(view.getId());
1240                     setDesignInformation(DESIGN_INFO_ID, IdAsString, view.getId());
1241                     int slashIndex = IdAsString.indexOf('/');
1242                     if (slashIndex != -1) {
1243                         IdAsString = IdAsString.substring(slashIndex + 1);
1244                     }
1245                     getTargetWidget(view.getId()).setDebugName(IdAsString);
1246                 } catch (Resources.NotFoundException e) {
1247                     // nothing
1248                 }
1249             }
1250         }
1251 
1252         if (USE_CONSTRAINTS_HELPER && mConstraintSetId != -1) {
1253             for (int i = 0; i < count; i++) {
1254                 final View child = getChildAt(i);
1255                 if (child.getId() == mConstraintSetId && child instanceof Constraints) {
1256                     mConstraintSet = ((Constraints) child).getConstraintSet();
1257                 }
1258             }
1259         }
1260 
1261         if (mConstraintSet != null) {
1262             mConstraintSet.applyToInternal(this, true);
1263         }
1264 
1265         mLayoutWidget.removeAllChildren();
1266 
1267         final int helperCount = mConstraintHelpers.size();
1268         if (helperCount > 0) {
1269             for (int i = 0; i < helperCount; i++) {
1270                 ConstraintHelper helper = mConstraintHelpers.get(i);
1271                 helper.updatePreLayout(this);
1272             }
1273         }
1274 
1275         // TODO refactor into an updatePreLayout interface
1276         for (int i = 0; i < count; i++) {
1277             View child = getChildAt(i);
1278             if (child instanceof Placeholder) {
1279                 ((Placeholder) child).updatePreLayout(this);
1280             }
1281         }
1282 
1283         mTempMapIdToWidget.clear();
1284         mTempMapIdToWidget.put(PARENT_ID, mLayoutWidget);
1285         mTempMapIdToWidget.put(getId(), mLayoutWidget);
1286         for (int i = 0; i < count; i++) {
1287             final View child = getChildAt(i);
1288             ConstraintWidget widget = getViewWidget(child);
1289             mTempMapIdToWidget.put(child.getId(), widget);
1290         }
1291 
1292         for (int i = 0; i < count; i++) {
1293             final View child = getChildAt(i);
1294             ConstraintWidget widget = getViewWidget(child);
1295             if (widget == null) {
1296                 continue;
1297             }
1298             final LayoutParams layoutParams = (LayoutParams) child.getLayoutParams();
1299             mLayoutWidget.add(widget);
1300             applyConstraintsFromLayoutParams(isInEditMode, child, widget,
1301                     layoutParams, mTempMapIdToWidget);
1302         }
1303     }
1304 
1305 
applyConstraintsFromLayoutParams(boolean isInEditMode, View child, ConstraintWidget widget, LayoutParams layoutParams, SparseArray<ConstraintWidget> idToWidget)1306     protected void applyConstraintsFromLayoutParams(boolean isInEditMode,
1307                                                     View child,
1308                                                     ConstraintWidget widget,
1309                                                     LayoutParams layoutParams,
1310                                                     SparseArray<ConstraintWidget> idToWidget) {
1311 
1312         layoutParams.validate();
1313         layoutParams.helped = false;
1314 
1315         widget.setVisibility(child.getVisibility());
1316         if (layoutParams.mIsInPlaceholder) {
1317             widget.setInPlaceholder(true);
1318             widget.setVisibility(View.GONE);
1319         }
1320         widget.setCompanionWidget(child);
1321 
1322         if (child instanceof ConstraintHelper) {
1323             ConstraintHelper helper = (ConstraintHelper) child;
1324             helper.resolveRtl(widget, mLayoutWidget.isRtl());
1325         }
1326         if (layoutParams.mIsGuideline) {
1327             Guideline guideline = (Guideline) widget;
1328             int resolvedGuideBegin = layoutParams.mResolvedGuideBegin;
1329             int resolvedGuideEnd = layoutParams.mResolvedGuideEnd;
1330             float resolvedGuidePercent = layoutParams.mResolvedGuidePercent;
1331             if (resolvedGuidePercent != UNSET) {
1332                 guideline.setGuidePercent(resolvedGuidePercent);
1333             } else if (resolvedGuideBegin != UNSET) {
1334                 guideline.setGuideBegin(resolvedGuideBegin);
1335             } else if (resolvedGuideEnd != UNSET) {
1336                 guideline.setGuideEnd(resolvedGuideEnd);
1337             }
1338         } else {
1339             // Get the left/right constraints resolved for RTL
1340             int resolvedLeftToLeft = layoutParams.mResolvedLeftToLeft;
1341             int resolvedLeftToRight = layoutParams.mResolvedLeftToRight;
1342             int resolvedRightToLeft = layoutParams.mResolvedRightToLeft;
1343             int resolvedRightToRight = layoutParams.mResolvedRightToRight;
1344             int resolveGoneLeftMargin = layoutParams.mResolveGoneLeftMargin;
1345             int resolveGoneRightMargin = layoutParams.mResolveGoneRightMargin;
1346             float resolvedHorizontalBias = layoutParams.mResolvedHorizontalBias;
1347 
1348             // Circular constraint
1349             if (layoutParams.circleConstraint != UNSET) {
1350                 ConstraintWidget target = idToWidget.get(layoutParams.circleConstraint);
1351                 if (target != null) {
1352                     widget.connectCircularConstraint(target,
1353                             layoutParams.circleAngle, layoutParams.circleRadius);
1354                 }
1355             } else {
1356                 // Left constraint
1357                 if (resolvedLeftToLeft != UNSET) {
1358                     ConstraintWidget target = idToWidget.get(resolvedLeftToLeft);
1359                     if (target != null) {
1360                         widget.immediateConnect(ConstraintAnchor.Type.LEFT, target,
1361                                 ConstraintAnchor.Type.LEFT, layoutParams.leftMargin,
1362                                 resolveGoneLeftMargin);
1363                     }
1364                 } else if (resolvedLeftToRight != UNSET) {
1365                     ConstraintWidget target = idToWidget.get(resolvedLeftToRight);
1366                     if (target != null) {
1367                         widget.immediateConnect(ConstraintAnchor.Type.LEFT, target,
1368                                 ConstraintAnchor.Type.RIGHT, layoutParams.leftMargin,
1369                                 resolveGoneLeftMargin);
1370                     }
1371                 }
1372 
1373                 // Right constraint
1374                 if (resolvedRightToLeft != UNSET) {
1375                     ConstraintWidget target = idToWidget.get(resolvedRightToLeft);
1376                     if (target != null) {
1377                         widget.immediateConnect(ConstraintAnchor.Type.RIGHT, target,
1378                                 ConstraintAnchor.Type.LEFT, layoutParams.rightMargin,
1379                                 resolveGoneRightMargin);
1380                     }
1381                 } else if (resolvedRightToRight != UNSET) {
1382                     ConstraintWidget target = idToWidget.get(resolvedRightToRight);
1383                     if (target != null) {
1384                         widget.immediateConnect(ConstraintAnchor.Type.RIGHT, target,
1385                                 ConstraintAnchor.Type.RIGHT, layoutParams.rightMargin,
1386                                 resolveGoneRightMargin);
1387                     }
1388                 }
1389 
1390                 // Top constraint
1391                 if (layoutParams.topToTop != UNSET) {
1392                     ConstraintWidget target = idToWidget.get(layoutParams.topToTop);
1393                     if (target != null) {
1394                         widget.immediateConnect(ConstraintAnchor.Type.TOP, target,
1395                                 ConstraintAnchor.Type.TOP, layoutParams.topMargin,
1396                                 layoutParams.goneTopMargin);
1397                     }
1398                 } else if (layoutParams.topToBottom != UNSET) {
1399                     ConstraintWidget target = idToWidget.get(layoutParams.topToBottom);
1400                     if (target != null) {
1401                         widget.immediateConnect(ConstraintAnchor.Type.TOP, target,
1402                                 ConstraintAnchor.Type.BOTTOM, layoutParams.topMargin,
1403                                 layoutParams.goneTopMargin);
1404                     }
1405                 }
1406 
1407                 // Bottom constraint
1408                 if (layoutParams.bottomToTop != UNSET) {
1409                     ConstraintWidget target = idToWidget.get(layoutParams.bottomToTop);
1410                     if (target != null) {
1411                         widget.immediateConnect(ConstraintAnchor.Type.BOTTOM, target,
1412                                 ConstraintAnchor.Type.TOP, layoutParams.bottomMargin,
1413                                 layoutParams.goneBottomMargin);
1414                     }
1415                 } else if (layoutParams.bottomToBottom != UNSET) {
1416                     ConstraintWidget target = idToWidget.get(layoutParams.bottomToBottom);
1417                     if (target != null) {
1418                         widget.immediateConnect(ConstraintAnchor.Type.BOTTOM, target,
1419                                 ConstraintAnchor.Type.BOTTOM, layoutParams.bottomMargin,
1420                                 layoutParams.goneBottomMargin);
1421                     }
1422                 }
1423 
1424                 // Baseline constraint
1425                 if (layoutParams.baselineToBaseline != UNSET) {
1426                     setWidgetBaseline(widget, layoutParams, idToWidget,
1427                             layoutParams.baselineToBaseline, ConstraintAnchor.Type.BASELINE);
1428                 } else if (layoutParams.baselineToTop != UNSET) {
1429                     setWidgetBaseline(widget, layoutParams, idToWidget,
1430                             layoutParams.baselineToTop, ConstraintAnchor.Type.TOP);
1431                 } else if (layoutParams.baselineToBottom != UNSET) {
1432                     setWidgetBaseline(widget, layoutParams, idToWidget,
1433                             layoutParams.baselineToBottom, ConstraintAnchor.Type.BOTTOM);
1434                 }
1435 
1436                 if (resolvedHorizontalBias >= 0) {
1437                     widget.setHorizontalBiasPercent(resolvedHorizontalBias);
1438                 }
1439                 if (layoutParams.verticalBias >= 0) {
1440                     widget.setVerticalBiasPercent(layoutParams.verticalBias);
1441                 }
1442             }
1443 
1444             if (isInEditMode && ((layoutParams.editorAbsoluteX != UNSET)
1445                     || (layoutParams.editorAbsoluteY != UNSET))) {
1446                 widget.setOrigin(layoutParams.editorAbsoluteX, layoutParams.editorAbsoluteY);
1447             }
1448 
1449             // FIXME: need to agree on the correct magic value for this
1450             //  rather than simply using zero.
1451             if (!layoutParams.mHorizontalDimensionFixed) {
1452                 if (layoutParams.width == MATCH_PARENT) {
1453                     if (layoutParams.constrainedWidth) {
1454                         widget.setHorizontalDimensionBehaviour(ConstraintWidget
1455                                 .DimensionBehaviour.MATCH_CONSTRAINT);
1456                     } else {
1457                         widget.setHorizontalDimensionBehaviour(ConstraintWidget
1458                                 .DimensionBehaviour.MATCH_PARENT);
1459                     }
1460                     widget.getAnchor(ConstraintAnchor.Type.LEFT).mMargin = layoutParams.leftMargin;
1461                     widget.getAnchor(ConstraintAnchor.Type.RIGHT).mMargin =
1462                             layoutParams.rightMargin;
1463                 } else {
1464                     widget.setHorizontalDimensionBehaviour(ConstraintWidget
1465                             .DimensionBehaviour.MATCH_CONSTRAINT);
1466                     widget.setWidth(0);
1467                 }
1468             } else {
1469                 widget.setHorizontalDimensionBehaviour(ConstraintWidget
1470                         .DimensionBehaviour.FIXED);
1471                 widget.setWidth(layoutParams.width);
1472                 if (layoutParams.width == WRAP_CONTENT) {
1473                     widget.setHorizontalDimensionBehaviour(ConstraintWidget
1474                             .DimensionBehaviour.WRAP_CONTENT);
1475                 }
1476             }
1477             if (!layoutParams.mVerticalDimensionFixed) {
1478                 if (layoutParams.height == MATCH_PARENT) {
1479                     if (layoutParams.constrainedHeight) {
1480                         widget.setVerticalDimensionBehaviour(ConstraintWidget
1481                                 .DimensionBehaviour.MATCH_CONSTRAINT);
1482                     } else {
1483                         widget.setVerticalDimensionBehaviour(ConstraintWidget
1484                                 .DimensionBehaviour.MATCH_PARENT);
1485                     }
1486                     widget.getAnchor(ConstraintAnchor.Type.TOP).mMargin = layoutParams.topMargin;
1487                     widget.getAnchor(ConstraintAnchor.Type.BOTTOM).mMargin =
1488                             layoutParams.bottomMargin;
1489                 } else {
1490                     widget.setVerticalDimensionBehaviour(ConstraintWidget
1491                             .DimensionBehaviour.MATCH_CONSTRAINT);
1492                     widget.setHeight(0);
1493                 }
1494             } else {
1495                 widget.setVerticalDimensionBehaviour(ConstraintWidget.DimensionBehaviour.FIXED);
1496                 widget.setHeight(layoutParams.height);
1497                 if (layoutParams.height == WRAP_CONTENT) {
1498                     widget.setVerticalDimensionBehaviour(ConstraintWidget
1499                             .DimensionBehaviour.WRAP_CONTENT);
1500                 }
1501             }
1502 
1503             widget.setDimensionRatio(layoutParams.dimensionRatio);
1504             widget.setHorizontalWeight(layoutParams.horizontalWeight);
1505             widget.setVerticalWeight(layoutParams.verticalWeight);
1506             widget.setHorizontalChainStyle(layoutParams.horizontalChainStyle);
1507             widget.setVerticalChainStyle(layoutParams.verticalChainStyle);
1508             widget.setWrapBehaviorInParent(layoutParams.wrapBehaviorInParent);
1509             widget.setHorizontalMatchStyle(layoutParams.matchConstraintDefaultWidth,
1510                     layoutParams.matchConstraintMinWidth, layoutParams.matchConstraintMaxWidth,
1511                     layoutParams.matchConstraintPercentWidth);
1512             widget.setVerticalMatchStyle(layoutParams.matchConstraintDefaultHeight,
1513                     layoutParams.matchConstraintMinHeight, layoutParams.matchConstraintMaxHeight,
1514                     layoutParams.matchConstraintPercentHeight);
1515         }
1516     }
1517 
setWidgetBaseline(ConstraintWidget widget, LayoutParams layoutParams, SparseArray<ConstraintWidget> idToWidget, int baselineTarget, ConstraintAnchor.Type type)1518     private void setWidgetBaseline(ConstraintWidget widget,
1519                                    LayoutParams layoutParams,
1520                                    SparseArray<ConstraintWidget> idToWidget,
1521                                    int baselineTarget,
1522                                    ConstraintAnchor.Type type) {
1523         View view = mChildrenByIds.get(baselineTarget);
1524         ConstraintWidget target = idToWidget.get(baselineTarget);
1525         if (target != null && view != null && view.getLayoutParams() instanceof LayoutParams) {
1526             layoutParams.mNeedsBaseline = true;
1527             if (type == ConstraintAnchor.Type.BASELINE) { // baseline to baseline
1528                 LayoutParams targetParams = (LayoutParams) view.getLayoutParams();
1529                 targetParams.mNeedsBaseline = true;
1530                 targetParams.mWidget.setHasBaseline(true);
1531             }
1532             ConstraintAnchor baseline = widget.getAnchor(ConstraintAnchor.Type.BASELINE);
1533             ConstraintAnchor targetAnchor = target.getAnchor(type);
1534             baseline.connect(targetAnchor, layoutParams.baselineMargin,
1535                     layoutParams.goneBaselineMargin, true);
1536             widget.setHasBaseline(true);
1537             widget.getAnchor(ConstraintAnchor.Type.TOP).reset();
1538             widget.getAnchor(ConstraintAnchor.Type.BOTTOM).reset();
1539         }
1540     }
1541 
getTargetWidget(int id)1542     private ConstraintWidget getTargetWidget(int id) {
1543         if (id == LayoutParams.PARENT_ID) {
1544             return mLayoutWidget;
1545         } else {
1546             View view = mChildrenByIds.get(id);
1547             if (view == null) {
1548                 view = findViewById(id);
1549                 if (view != null && view != this && view.getParent() == this) {
1550                     onViewAdded(view);
1551                 }
1552             }
1553             if (view == this) {
1554                 return mLayoutWidget;
1555             }
1556             return view == null ? null : ((LayoutParams) view.getLayoutParams()).mWidget;
1557         }
1558     }
1559 
1560     /**
1561      * @param view
1562      * @return
1563      *
1564      */
getViewWidget(View view)1565     public final ConstraintWidget getViewWidget(View view) {
1566         if (view == this) {
1567             return mLayoutWidget;
1568         }
1569         if (view != null) {
1570             if (view.getLayoutParams() instanceof LayoutParams) {
1571                 return ((LayoutParams) view.getLayoutParams()).mWidget;
1572             }
1573             view.setLayoutParams(generateLayoutParams(view.getLayoutParams()));
1574             if (view.getLayoutParams() instanceof LayoutParams) {
1575                 return ((LayoutParams) view.getLayoutParams()).mWidget;
1576             }
1577         }
1578         return null;
1579     }
1580 
1581     /**
1582      * @param metrics
1583      * Fills metrics object
1584      */
fillMetrics(Metrics metrics)1585     public void fillMetrics(Metrics metrics) {
1586         mMetrics = metrics;
1587         mLayoutWidget.fillMetrics(metrics);
1588     }
1589 
1590     private int mOnMeasureWidthMeasureSpec = 0;
1591     private int mOnMeasureHeightMeasureSpec = 0;
1592 
1593     /**
1594      * Handles measuring a layout
1595      *
1596      * @param layout
1597      * @param optimizationLevel
1598      * @param widthMeasureSpec
1599      * @param heightMeasureSpec
1600      */
resolveSystem(ConstraintWidgetContainer layout, int optimizationLevel, int widthMeasureSpec, int heightMeasureSpec)1601     protected void resolveSystem(ConstraintWidgetContainer layout,
1602                                  int optimizationLevel,
1603                                  int widthMeasureSpec,
1604                                  int heightMeasureSpec) {
1605         int widthMode = MeasureSpec.getMode(widthMeasureSpec);
1606         int widthSize = MeasureSpec.getSize(widthMeasureSpec);
1607         int heightMode = MeasureSpec.getMode(heightMeasureSpec);
1608         int heightSize = MeasureSpec.getSize(heightMeasureSpec);
1609 
1610         int paddingY = Math.max(0, getPaddingTop());
1611         int paddingBottom = Math.max(0, getPaddingBottom());
1612         int paddingHeight = paddingY + paddingBottom;
1613         int paddingWidth = getPaddingWidth();
1614         int paddingX;
1615         mMeasurer.captureLayoutInfo(widthMeasureSpec, heightMeasureSpec, paddingY, paddingBottom,
1616                 paddingWidth, paddingHeight);
1617 
1618         int paddingStart = Math.max(0, getPaddingStart());
1619         int paddingEnd = Math.max(0, getPaddingEnd());
1620         if (paddingStart > 0 || paddingEnd > 0) {
1621             if (isRtl()) {
1622                 paddingX = paddingEnd;
1623             } else {
1624                 paddingX = paddingStart;
1625             }
1626         } else {
1627             paddingX = Math.max(0, getPaddingLeft());
1628         }
1629 
1630         widthSize -= paddingWidth;
1631         heightSize -= paddingHeight;
1632 
1633         setSelfDimensionBehaviour(layout, widthMode, widthSize, heightMode, heightSize);
1634 
1635         layout.measure(optimizationLevel, widthMode, widthSize, heightMode, heightSize,
1636                 mLastMeasureWidth, mLastMeasureHeight, paddingX, paddingY);
1637     }
1638 
1639     /**
1640      * Handles calling setMeasuredDimension()
1641      *
1642      * @param widthMeasureSpec
1643      * @param heightMeasureSpec
1644      * @param measuredWidth
1645      * @param measuredHeight
1646      * @param isWidthMeasuredTooSmall
1647      * @param isHeightMeasuredTooSmall
1648      */
resolveMeasuredDimension(int widthMeasureSpec, int heightMeasureSpec, int measuredWidth, int measuredHeight, boolean isWidthMeasuredTooSmall, boolean isHeightMeasuredTooSmall)1649     protected void resolveMeasuredDimension(int widthMeasureSpec,
1650                                             int heightMeasureSpec,
1651                                             int measuredWidth,
1652                                             int measuredHeight,
1653                                             boolean isWidthMeasuredTooSmall,
1654                                             boolean isHeightMeasuredTooSmall) {
1655         int childState = 0;
1656         int heightPadding = mMeasurer.mPaddingHeight;
1657         int widthPadding = mMeasurer.mPaddingWidth;
1658 
1659         int androidLayoutWidth = measuredWidth + widthPadding;
1660         int androidLayoutHeight = measuredHeight + heightPadding;
1661 
1662         int resolvedWidthSize = resolveSizeAndState(androidLayoutWidth,
1663                 widthMeasureSpec, childState);
1664         int resolvedHeightSize = resolveSizeAndState(androidLayoutHeight, heightMeasureSpec,
1665                 childState << MEASURED_HEIGHT_STATE_SHIFT);
1666         resolvedWidthSize &= MEASURED_SIZE_MASK;
1667         resolvedHeightSize &= MEASURED_SIZE_MASK;
1668         resolvedWidthSize = Math.min(mMaxWidth, resolvedWidthSize);
1669         resolvedHeightSize = Math.min(mMaxHeight, resolvedHeightSize);
1670         if (isWidthMeasuredTooSmall) {
1671             resolvedWidthSize |= MEASURED_STATE_TOO_SMALL;
1672         }
1673         if (isHeightMeasuredTooSmall) {
1674             resolvedHeightSize |= MEASURED_STATE_TOO_SMALL;
1675         }
1676         setMeasuredDimension(resolvedWidthSize, resolvedHeightSize);
1677         mLastMeasureWidth = resolvedWidthSize;
1678         mLastMeasureHeight = resolvedHeightSize;
1679     }
1680 
1681     /**
1682      * {@inheritDoc}
1683      */
1684     @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)1685     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
1686         long time = 0;
1687         if (mMetrics != null) {
1688             time = System.nanoTime();
1689             mMetrics.mChildCount = getChildCount();
1690             mMetrics.mMeasureCalls++;
1691         }
1692         mDirtyHierarchy |= dynamicUpdateConstraints(widthMeasureSpec,  heightMeasureSpec);
1693 
1694         @SuppressWarnings({"PointlessBooleanExpression", "ConstantConditions",
1695                 "ComplexBooleanConstant"})  // TODO re-enable
1696         boolean sameSpecsAsPreviousMeasure =
1697                 false && (mOnMeasureWidthMeasureSpec == widthMeasureSpec
1698                 && mOnMeasureHeightMeasureSpec == heightMeasureSpec);
1699         //noinspection ConstantConditions
1700         if (!mDirtyHierarchy && !sameSpecsAsPreviousMeasure) {
1701             // it's possible that, if we are already marked for a relayout,
1702             // a view would not call to request a layout;
1703             // in that case we'd miss updating the hierarchy correctly
1704             // (window insets change may do that -- we receive
1705             // a second onMeasure before onLayout).
1706             // We have to iterate on our children to verify that none set a request layout flag...
1707             final int count = getChildCount();
1708             for (int i = 0; i < count; i++) {
1709                 final View child = getChildAt(i);
1710                 if (child.isLayoutRequested()) {
1711                     if (DEBUG) {
1712                         System.out.println("### CHILD " + child
1713                                 + " REQUESTED LAYOUT, FORCE DIRTY HIERARCHY");
1714                     }
1715                     mDirtyHierarchy = true;
1716                     break;
1717                 }
1718             }
1719         }
1720 
1721         if (!mDirtyHierarchy) {
1722             //noinspection ConstantConditions
1723             if (sameSpecsAsPreviousMeasure) {
1724                 resolveMeasuredDimension(widthMeasureSpec, heightMeasureSpec,
1725                         mLayoutWidget.getWidth(), mLayoutWidget.getHeight(),
1726                         mLayoutWidget.isWidthMeasuredTooSmall(),
1727                         mLayoutWidget.isHeightMeasuredTooSmall());
1728                 if (mMetrics != null) {
1729                     mMetrics.mMeasureDuration +=  System.nanoTime() - time;
1730                 }
1731                 return;
1732             }
1733             if (OPTIMIZE_HEIGHT_CHANGE
1734                     && mOnMeasureWidthMeasureSpec == widthMeasureSpec
1735                     && MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY
1736                     && MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST
1737                     && MeasureSpec.getMode(mOnMeasureHeightMeasureSpec) == MeasureSpec.AT_MOST) {
1738                 int newSize = MeasureSpec.getSize(heightMeasureSpec);
1739                 if (DEBUG) {
1740                     System.out.println("### COMPATIBLE REQ " + newSize
1741                             + " >= ? " + mLayoutWidget.getHeight());
1742                 }
1743                 if (newSize >= mLayoutWidget.getHeight()
1744                         && !mLayoutWidget.isHeightMeasuredTooSmall()) {
1745                     mOnMeasureWidthMeasureSpec = widthMeasureSpec;
1746                     mOnMeasureHeightMeasureSpec = heightMeasureSpec;
1747                     resolveMeasuredDimension(widthMeasureSpec, heightMeasureSpec,
1748                             mLayoutWidget.getWidth(), mLayoutWidget.getHeight(),
1749                             mLayoutWidget.isWidthMeasuredTooSmall(),
1750                             mLayoutWidget.isHeightMeasuredTooSmall());
1751                     if (mMetrics != null) {
1752                         mMetrics.mMeasureDuration +=  System.nanoTime() - time;
1753                     }
1754                     return;
1755                 }
1756             }
1757         }
1758         mOnMeasureWidthMeasureSpec = widthMeasureSpec;
1759         mOnMeasureHeightMeasureSpec = heightMeasureSpec;
1760 
1761         if (DEBUG) {
1762             System.out.println("### ON MEASURE " + mDirtyHierarchy
1763                     + " of " + mLayoutWidget.getDebugName()
1764                     + " onMeasure width: " + MeasureSpec.toString(widthMeasureSpec)
1765                     + " height: " + MeasureSpec.toString(heightMeasureSpec) + this);
1766         }
1767 
1768         mLayoutWidget.setRtl(isRtl());
1769 
1770         if (mDirtyHierarchy) {
1771             mDirtyHierarchy = false;
1772             if (updateHierarchy()) {
1773                 mLayoutWidget.updateHierarchy();
1774             }
1775         }
1776         mLayoutWidget.fillMetrics(mMetrics);
1777 
1778         resolveSystem(mLayoutWidget, mOptimizationLevel, widthMeasureSpec, heightMeasureSpec);
1779         resolveMeasuredDimension(widthMeasureSpec, heightMeasureSpec,
1780                 mLayoutWidget.getWidth(), mLayoutWidget.getHeight(),
1781                 mLayoutWidget.isWidthMeasuredTooSmall(), mLayoutWidget.isHeightMeasuredTooSmall());
1782 
1783         if (mMetrics != null) {
1784             mMetrics.mMeasureDuration +=  System.nanoTime() - time;
1785         }
1786         if (DEBUG) {
1787             time = System.nanoTime() - time;
1788             System.out.println(mLayoutWidget.getDebugName() + " (" + getChildCount()
1789                     + ") DONE onMeasure width: " + MeasureSpec.toString(widthMeasureSpec)
1790                     + " height: " + MeasureSpec.toString(heightMeasureSpec) + " => "
1791                     + mLastMeasureWidth + " x " + mLastMeasureHeight
1792                     + " lasted " + time
1793             );
1794         }
1795     }
1796 
isRtl()1797     protected boolean isRtl() {
1798         boolean isRtlSupported = (getContext().getApplicationInfo().flags
1799                 & ApplicationInfo.FLAG_SUPPORTS_RTL) != 0;
1800         return isRtlSupported && (View.LAYOUT_DIRECTION_RTL == getLayoutDirection());
1801     }
1802 
1803     /**
1804      * Compute the padding width, taking in account RTL start/end padding if available and present.
1805      * @return padding width
1806      */
getPaddingWidth()1807     private int getPaddingWidth() {
1808         int widthPadding = Math.max(0, getPaddingLeft()) + Math.max(0, getPaddingRight());
1809         int rtlPadding = Math.max(0, getPaddingStart()) + Math.max(0, getPaddingEnd());
1810         if (rtlPadding > 0) {
1811             widthPadding = rtlPadding;
1812         }
1813         return widthPadding;
1814     }
1815 
setSelfDimensionBehaviour(ConstraintWidgetContainer layout, int widthMode, int widthSize, int heightMode, int heightSize)1816     protected void setSelfDimensionBehaviour(ConstraintWidgetContainer layout,
1817                                              int widthMode,
1818                                              int widthSize,
1819                                              int heightMode,
1820                                              int heightSize) {
1821 
1822         int heightPadding = mMeasurer.mPaddingHeight;
1823         int widthPadding = mMeasurer.mPaddingWidth;
1824 
1825         ConstraintWidget.DimensionBehaviour widthBehaviour =
1826                 ConstraintWidget.DimensionBehaviour.FIXED;
1827         ConstraintWidget.DimensionBehaviour heightBehaviour =
1828                 ConstraintWidget.DimensionBehaviour.FIXED;
1829 
1830         int desiredWidth = 0;
1831         int desiredHeight = 0;
1832         final int childCount = getChildCount();
1833 
1834         switch (widthMode) {
1835             case MeasureSpec.AT_MOST: {
1836                 widthBehaviour = ConstraintWidget.DimensionBehaviour.WRAP_CONTENT;
1837                 desiredWidth = widthSize;
1838                 if (childCount == 0) {
1839                     desiredWidth = Math.max(0, mMinWidth);
1840                 }
1841             }
1842             break;
1843             case MeasureSpec.UNSPECIFIED: {
1844                 widthBehaviour = ConstraintWidget.DimensionBehaviour.WRAP_CONTENT;
1845                 if (childCount == 0) {
1846                     desiredWidth = Math.max(0, mMinWidth);
1847                 }
1848             }
1849             break;
1850             case MeasureSpec.EXACTLY: {
1851                 desiredWidth = Math.min(mMaxWidth - widthPadding, widthSize);
1852             }
1853         }
1854         switch (heightMode) {
1855             case MeasureSpec.AT_MOST: {
1856                 heightBehaviour = ConstraintWidget.DimensionBehaviour.WRAP_CONTENT;
1857                 desiredHeight = heightSize;
1858                 if (childCount == 0) {
1859                     desiredHeight = Math.max(0, mMinHeight);
1860                 }
1861             }
1862             break;
1863             case MeasureSpec.UNSPECIFIED: {
1864                 heightBehaviour = ConstraintWidget.DimensionBehaviour.WRAP_CONTENT;
1865                 if (childCount == 0) {
1866                     desiredHeight = Math.max(0, mMinHeight);
1867                 }
1868             }
1869             break;
1870             case MeasureSpec.EXACTLY: {
1871                 desiredHeight = Math.min(mMaxHeight - heightPadding, heightSize);
1872             }
1873         }
1874 
1875         if (desiredWidth != layout.getWidth() || desiredHeight != layout.getHeight()) {
1876             layout.invalidateMeasures();
1877         }
1878         layout.setX(0);
1879         layout.setY(0);
1880         layout.setMaxWidth(mMaxWidth - widthPadding);
1881         layout.setMaxHeight(mMaxHeight - heightPadding);
1882         layout.setMinWidth(0);
1883         layout.setMinHeight(0);
1884         layout.setHorizontalDimensionBehaviour(widthBehaviour);
1885         layout.setWidth(desiredWidth);
1886         layout.setVerticalDimensionBehaviour(heightBehaviour);
1887         layout.setHeight(desiredHeight);
1888         layout.setMinWidth(mMinWidth - widthPadding);
1889         layout.setMinHeight(mMinHeight - heightPadding);
1890     }
1891 
1892     /**
1893      * Set the State of the ConstraintLayout, causing it to load a particular ConstraintSet.
1894      * For states with variants the variant with matching width and height
1895      * constraintSet will be chosen
1896      *
1897      * @param id           the constraint set state
1898      * @param screenWidth  the width of the screen in pixels
1899      * @param screenHeight the height of the screen in pixels
1900      */
setState(int id, int screenWidth, int screenHeight)1901     public void setState(int id, int screenWidth, int screenHeight) {
1902         if (mConstraintLayoutSpec != null) {
1903             mConstraintLayoutSpec.updateConstraints(id, screenWidth, screenHeight);
1904         }
1905     }
1906 
1907     /**
1908      * {@inheritDoc}
1909      */
1910     @Override
onLayout(boolean changed, int left, int top, int right, int bottom)1911     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
1912         if (mMetrics != null) {
1913             mMetrics.mNumberOfLayouts++;
1914         }
1915         if (DEBUG) {
1916             System.out.println(mLayoutWidget.getDebugName() + " onLayout changed: "
1917                     + changed + " left: " + left + " top: " + top
1918                     + " right: " + right + " bottom: " + bottom
1919                     + " (" + (right - left) + " x " + (bottom - top) + ")");
1920         }
1921         final int widgetsCount = getChildCount();
1922         final boolean isInEditMode = isInEditMode();
1923         for (int i = 0; i < widgetsCount; i++) {
1924             final View child = getChildAt(i);
1925             LayoutParams params = (LayoutParams) child.getLayoutParams();
1926             ConstraintWidget widget = params.mWidget;
1927 
1928             if (child.getVisibility() == GONE
1929                     && !params.mIsGuideline
1930                     && !params.mIsHelper
1931                     && !params.mIsVirtualGroup
1932                     && !isInEditMode) {
1933                 // If we are in edit mode, let's layout the widget
1934                 // so that they are at "the right place"
1935                 // visually in the editor (as we get our positions from layoutlib)
1936                 continue;
1937             }
1938             if (params.mIsInPlaceholder) {
1939                 continue;
1940             }
1941             int l = widget.getX();
1942             int t = widget.getY();
1943             int r = l + widget.getWidth();
1944             int b = t + widget.getHeight();
1945 
1946             if (DEBUG) {
1947                 if (child.getVisibility() != View.GONE
1948                         && (child.getMeasuredWidth() != widget.getWidth()
1949                         || child.getMeasuredHeight() != widget.getHeight())) {
1950                     int deltaX = Math.abs(child.getMeasuredWidth() - widget.getWidth());
1951                     int deltaY = Math.abs(child.getMeasuredHeight() - widget.getHeight());
1952                     if (deltaX > 1 || deltaY > 1) {
1953                         System.out.println("child " + child
1954                                 + " measuredWidth " + child.getMeasuredWidth()
1955                                 + " vs " + widget.getWidth()
1956                                 + " x measureHeight " + child.getMeasuredHeight()
1957                                 + " vs " + widget.getHeight());
1958                     }
1959                 }
1960             }
1961 
1962             child.layout(l, t, r, b);
1963             if (child instanceof Placeholder) {
1964                 Placeholder holder = (Placeholder) child;
1965                 View content = holder.getContent();
1966                 if (content != null) {
1967                     content.setVisibility(VISIBLE);
1968                     content.layout(l, t, r, b);
1969                 }
1970             }
1971         }
1972         final int helperCount = mConstraintHelpers.size();
1973         if (helperCount > 0) {
1974             for (int i = 0; i < helperCount; i++) {
1975                 ConstraintHelper helper = mConstraintHelpers.get(i);
1976                 helper.updatePostLayout(this);
1977             }
1978         }
1979     }
1980 
1981     /**
1982      * Set the optimization for the layout resolution.
1983      * <p>
1984      * The optimization can be any of the following:
1985      * <ul>
1986      * <li>Optimizer.OPTIMIZATION_NONE</li>
1987      * <li>Optimizer.OPTIMIZATION_STANDARD</li>
1988      * <li>a mask composed of specific optimizations</li>
1989      * </ul>
1990      * The mask can be composed of any combination of the following:
1991      * <ul>
1992      * <li>Optimizer.OPTIMIZATION_DIRECT  </li>
1993      * <li>Optimizer.OPTIMIZATION_BARRIER  </li>
1994      * <li>Optimizer.OPTIMIZATION_CHAIN  (experimental) </li>
1995      * <li>Optimizer.OPTIMIZATION_DIMENSIONS  (experimental) </li>
1996      * </ul>
1997      * Note that the current implementation of
1998      * Optimizer.OPTIMIZATION_STANDARD is as a mask of DIRECT and BARRIER.
1999      * </p>
2000      *
2001      * @param level optimization level
2002      */
setOptimizationLevel(int level)2003     public void setOptimizationLevel(int level) {
2004         mOptimizationLevel = level;
2005         mLayoutWidget.setOptimizationLevel(level);
2006     }
2007 
2008     /**
2009      * Return the current optimization level for the layout resolution
2010      *
2011      * @return the current level
2012      */
getOptimizationLevel()2013     public int getOptimizationLevel() {
2014         return mLayoutWidget.getOptimizationLevel();
2015     }
2016 
2017     /**
2018      *
2019      */
2020     @Override
generateLayoutParams(AttributeSet attrs)2021     public LayoutParams generateLayoutParams(AttributeSet attrs) {
2022         return new LayoutParams(getContext(), attrs);
2023     }
2024 
2025     /**
2026      * {@inheritDoc}
2027      */
2028     @Override
generateDefaultLayoutParams()2029     protected LayoutParams generateDefaultLayoutParams() {
2030         return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
2031     }
2032 
2033     /**
2034      * {@inheritDoc}
2035      */
2036     @Override
generateLayoutParams(ViewGroup.LayoutParams p)2037     protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
2038         return new LayoutParams(p);
2039     }
2040 
2041     /**
2042      * {@inheritDoc}
2043      */
2044     @Override
checkLayoutParams(ViewGroup.LayoutParams p)2045     protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
2046         return p instanceof LayoutParams;
2047     }
2048 
2049     /**
2050      * Sets a ConstraintSet object to manage constraints.
2051      * The ConstraintSet overrides LayoutParams of child views.
2052      *
2053      * @param set Layout children using ConstraintSet
2054      */
setConstraintSet(ConstraintSet set)2055     public void setConstraintSet(ConstraintSet set) {
2056         mConstraintSet = set;
2057     }
2058 
2059     /**
2060      * @param id the view id
2061      * @return the child view, can return null
2062      * Return a direct child view by its id if it exists
2063      */
getViewById(int id)2064     public View getViewById(int id) {
2065         return mChildrenByIds.get(id);
2066     }
2067 
2068     /**
2069      *
2070      */
2071     @Override
dispatchDraw(Canvas canvas)2072     protected void dispatchDraw(Canvas canvas) {
2073         if (mConstraintHelpers != null) {
2074             final int helperCount = mConstraintHelpers.size();
2075             if (helperCount > 0) {
2076                 for (int i = 0; i < helperCount; i++) {
2077                     ConstraintHelper helper = mConstraintHelpers.get(i);
2078                     helper.updatePreDraw(this);
2079                 }
2080             }
2081         }
2082 
2083         super.dispatchDraw(canvas);
2084 
2085         if (DEBUG || isInEditMode()) {
2086             float cw = getWidth();
2087             float ch = getHeight();
2088             float ow = 1080;
2089             float oh = 1920;
2090             final int count = getChildCount();
2091             for (int i = 0; i < count; i++) {
2092                 View child = getChildAt(i);
2093                 if (child.getVisibility() == GONE) {
2094                     continue;
2095                 }
2096                 Object tag = child.getTag();
2097                 if (tag != null && tag instanceof String) {
2098                     String coordinates = (String) tag;
2099                     @SuppressWarnings("StringSplitter")
2100                     String[] split = coordinates.split(",");
2101                     if (split.length == 4) {
2102                         int x = Integer.parseInt(split[0]);
2103                         int y = Integer.parseInt(split[1]);
2104                         int w = Integer.parseInt(split[2]);
2105                         int h = Integer.parseInt(split[3]);
2106                         x = (int) ((x / ow) * cw);
2107                         y = (int) ((y / oh) * ch);
2108                         w = (int) ((w / ow) * cw);
2109                         h = (int) ((h / oh) * ch);
2110                         Paint paint = new Paint();
2111                         paint.setColor(Color.RED);
2112                         canvas.drawLine(x, y, x + w, y, paint);
2113                         canvas.drawLine(x + w, y, x + w, y + h, paint);
2114                         canvas.drawLine(x + w, y + h, x, y + h, paint);
2115                         canvas.drawLine(x, y + h, x, y, paint);
2116                         paint.setColor(Color.GREEN);
2117                         canvas.drawLine(x, y, x + w, y + h, paint);
2118                         canvas.drawLine(x, y + h, x + w, y, paint);
2119                     }
2120                 }
2121             }
2122         }
2123         if (DEBUG_DRAW_CONSTRAINTS) {
2124             final int count = getChildCount();
2125             for (int i = 0; i < count; i++) {
2126                 View child = getChildAt(i);
2127                 if (child.getVisibility() == GONE) {
2128                     continue;
2129                 }
2130                 ConstraintWidget widget = getViewWidget(child);
2131                 if (widget.mTop.isConnected()) {
2132                     ConstraintWidget target = widget.mTop.mTarget.mOwner;
2133                     int x1 = widget.getX() + widget.getWidth() / 2;
2134                     int y1 = widget.getY();
2135                     int x2 = target.getX() + target.getWidth() / 2;
2136                     int y2 = 0;
2137                     if (widget.mTop.mTarget.mType == ConstraintAnchor.Type.TOP) {
2138                         y2 = target.getY();
2139                     } else {
2140                         y2 = target.getY() + target.getHeight();
2141                     }
2142                     Paint paint = new Paint();
2143                     paint.setColor(Color.RED);
2144                     paint.setStrokeWidth(4);
2145                     canvas.drawLine(x1, y1, x2, y2, paint);
2146                 }
2147                 if (widget.mBottom.isConnected()) {
2148                     ConstraintWidget target = widget.mBottom.mTarget.mOwner;
2149                     int x1 = widget.getX() + widget.getWidth() / 2;
2150                     int y1 = widget.getY() + widget.getHeight();
2151                     int x2 = target.getX() + target.getWidth() / 2;
2152                     int y2 = 0;
2153                     if (widget.mBottom.mTarget.mType == ConstraintAnchor.Type.TOP) {
2154                         y2 = target.getY();
2155                     } else {
2156                         y2 = target.getY() + target.getHeight();
2157                     }
2158                     Paint paint = new Paint();
2159                     paint.setStrokeWidth(4);
2160                     paint.setColor(Color.RED);
2161                     canvas.drawLine(x1, y1, x2, y2, paint);
2162                 }
2163             }
2164         }
2165     }
2166 
2167     /**
2168      * Notify of constraints changed
2169      * @param constraintsChangedListener
2170      */
setOnConstraintsChanged(ConstraintsChangedListener constraintsChangedListener)2171     public void setOnConstraintsChanged(ConstraintsChangedListener constraintsChangedListener) {
2172        // this.mConstraintsChangedListener = constraintsChangedListener;
2173         if (mConstraintLayoutSpec != null) {
2174             mConstraintLayoutSpec.setOnConstraintsChanged(constraintsChangedListener);
2175         }
2176     }
2177 
2178     /**
2179      * Load a layout description file from the resources.
2180      *
2181      * @param layoutDescription The resource id, or 0 to reset the layout description.
2182      */
loadLayoutDescription(int layoutDescription)2183     public void loadLayoutDescription(int layoutDescription) {
2184         if (layoutDescription != 0) {
2185             try {
2186                 mConstraintLayoutSpec = new ConstraintLayoutStates(getContext(),
2187                         this, layoutDescription);
2188             } catch (Resources.NotFoundException e) {
2189                 mConstraintLayoutSpec = null;
2190             }
2191         } else {
2192             mConstraintLayoutSpec = null;
2193         }
2194     }
2195 
2196     /**
2197      * This class contains the different attributes specifying
2198      * how a view want to be laid out inside
2199      * a {@link ConstraintLayout}. For building up constraints at run time,
2200      * using {@link ConstraintSet} is recommended.
2201      */
2202     public static class LayoutParams extends ViewGroup.MarginLayoutParams {
2203         /**
2204          * Dimension will be controlled by constraints.
2205          */
2206         public static final int MATCH_CONSTRAINT = 0;
2207 
2208         /**
2209          * References the id of the parent.
2210          */
2211         public static final int PARENT_ID = 0;
2212 
2213         /**
2214          * Defines an id that is not set.
2215          */
2216         public static final int UNSET = -1;
2217 
2218 
2219         /**
2220          * Defines an id that is not set.
2221          */
2222         public static final int GONE_UNSET = Integer.MIN_VALUE;
2223 
2224 
2225         /**
2226          * The horizontal orientation.
2227          */
2228         public static final int HORIZONTAL = ConstraintWidget.HORIZONTAL;
2229 
2230         /**
2231          * The vertical orientation.
2232          */
2233         public static final int VERTICAL = ConstraintWidget.VERTICAL;
2234 
2235         /**
2236          * The left side of a view.
2237          */
2238         public static final int LEFT = 1;
2239 
2240         /**
2241          * The right side of a view.
2242          */
2243         public static final int RIGHT = 2;
2244 
2245         /**
2246          * The top of a view.
2247          */
2248         public static final int TOP = 3;
2249 
2250         /**
2251          * The bottom side of a view.
2252          */
2253         public static final int BOTTOM = 4;
2254 
2255         /**
2256          * The baseline of the text in a view.
2257          */
2258         public static final int BASELINE = 5;
2259 
2260         /**
2261          * The left side of a view in left to right languages.
2262          * In right to left languages it corresponds to the right side of the view
2263          */
2264         public static final int START = 6;
2265 
2266         /**
2267          * The right side of a view in left to right languages.
2268          * In right to left languages it corresponds to the left side of the view
2269          */
2270         public static final int END = 7;
2271 
2272         /**
2273          * Circle reference from a view.
2274          */
2275         public static final int CIRCLE = 8;
2276 
2277         /**
2278          * Set matchConstraintDefault* default to the wrap content size.
2279          * Use to set the matchConstraintDefaultWidth and matchConstraintDefaultHeight
2280          */
2281         public static final int MATCH_CONSTRAINT_WRAP = ConstraintWidget.MATCH_CONSTRAINT_WRAP;
2282 
2283         /**
2284          * Set matchConstraintDefault* spread as much as possible within its constraints.
2285          * Use to set the matchConstraintDefaultWidth and matchConstraintDefaultHeight
2286          */
2287         public static final int MATCH_CONSTRAINT_SPREAD = ConstraintWidget.MATCH_CONSTRAINT_SPREAD;
2288 
2289         /**
2290          * Set matchConstraintDefault* percent to be based
2291          * on a percent of another dimension (by default, the parent)
2292          * Use to set the matchConstraintDefaultWidth and matchConstraintDefaultHeight
2293          */
2294         public static final int MATCH_CONSTRAINT_PERCENT =
2295                 ConstraintWidget.MATCH_CONSTRAINT_PERCENT;
2296 
2297         /**
2298          * Chain spread style
2299          */
2300         public static final int CHAIN_SPREAD = ConstraintWidget.CHAIN_SPREAD;
2301 
2302         /**
2303          * Chain spread inside style
2304          */
2305         public static final int CHAIN_SPREAD_INSIDE = ConstraintWidget.CHAIN_SPREAD_INSIDE;
2306 
2307         /**
2308          * Chain packed style
2309          */
2310         public static final int CHAIN_PACKED = ConstraintWidget.CHAIN_PACKED;
2311 
2312         /**
2313          * The distance of child (guideline) to the top or left edge of its parent.
2314          */
2315         public int guideBegin = UNSET;
2316 
2317         /**
2318          * The distance of child (guideline) to the bottom or right edge of its parent.
2319          */
2320         public int guideEnd = UNSET;
2321 
2322         /**
2323          * The ratio of the distance to the parent's sides
2324          */
2325         public float guidePercent = UNSET;
2326 
2327         /**
2328          * The ratio of the distance to the parent's sides
2329          */
2330         public boolean guidelineUseRtl = true;
2331 
2332         /**
2333          * Constrains the left side of a child to the left side of
2334          * a target child (contains the target child id).
2335          */
2336         public int leftToLeft = UNSET;
2337 
2338         /**
2339          * Constrains the left side of a child to the right side of
2340          * a target child (contains the target child id).
2341          */
2342         public int leftToRight = UNSET;
2343 
2344         /**
2345          * Constrains the right side of a child to the left side of
2346          * a target child (contains the target child id).
2347          */
2348         public int rightToLeft = UNSET;
2349 
2350         /**
2351          * Constrains the right side of a child to the right side of
2352          * a target child (contains the target child id).
2353          */
2354         public int rightToRight = UNSET;
2355 
2356         /**
2357          * Constrains the top side of a child to the top side of
2358          * a target child (contains the target child id).
2359          */
2360         public int topToTop = UNSET;
2361 
2362         /**
2363          * Constrains the top side of a child to the bottom side of
2364          * a target child (contains the target child id).
2365          */
2366         public int topToBottom = UNSET;
2367 
2368         /**
2369          * Constrains the bottom side of a child to the top side of
2370          * a target child (contains the target child id).
2371          */
2372         public int bottomToTop = UNSET;
2373 
2374         /**
2375          * Constrains the bottom side of a child to the bottom side of
2376          * a target child (contains the target child id).
2377          */
2378         public int bottomToBottom = UNSET;
2379 
2380         /**
2381          * Constrains the baseline of a child to the baseline of
2382          * a target child (contains the target child id).
2383          */
2384         public int baselineToBaseline = UNSET;
2385 
2386         /**
2387          * Constrains the baseline of a child to the top of
2388          * a target child (contains the target child id).
2389          */
2390         public int baselineToTop = UNSET;
2391 
2392         /**
2393          * Constrains the baseline of a child to the bottom of
2394          * a target child (contains the target child id).
2395          */
2396         public int baselineToBottom = UNSET;
2397 
2398         /**
2399          * Constrains the center of a child to the center of
2400          * a target child (contains the target child id).
2401          */
2402         public int circleConstraint = UNSET;
2403 
2404         /**
2405          * The radius used for a circular constraint
2406          */
2407         public int circleRadius = 0;
2408 
2409         /**
2410          * The angle used for a circular constraint]
2411          */
2412         public float circleAngle = 0;
2413 
2414         /**
2415          * Constrains the start side of a child to the end side of
2416          * a target child (contains the target child id).
2417          */
2418         public int startToEnd = UNSET;
2419 
2420         /**
2421          * Constrains the start side of a child to the start side of
2422          * a target child (contains the target child id).
2423          */
2424         public int startToStart = UNSET;
2425 
2426         /**
2427          * Constrains the end side of a child to the start side of
2428          * a target child (contains the target child id).
2429          */
2430         public int endToStart = UNSET;
2431 
2432         /**
2433          * Constrains the end side of a child to the end side of
2434          * a target child (contains the target child id).
2435          */
2436         public int endToEnd = UNSET;
2437 
2438         /**
2439          * The left margin to use when the target is gone.
2440          */
2441         public int goneLeftMargin = GONE_UNSET;
2442 
2443         /**
2444          * The top margin to use when the target is gone.
2445          */
2446         public int goneTopMargin = GONE_UNSET;
2447 
2448         /**
2449          * The right margin to use when the target is gone
2450          */
2451         public int goneRightMargin = GONE_UNSET;
2452 
2453         /**
2454          * The bottom margin to use when the target is gone.
2455          */
2456         public int goneBottomMargin = GONE_UNSET;
2457 
2458         /**
2459          * The start margin to use when the target is gone.
2460          */
2461         public int goneStartMargin = GONE_UNSET;
2462 
2463         /**
2464          * The end margin to use when the target is gone.
2465          */
2466         public int goneEndMargin = GONE_UNSET;
2467 
2468         /**
2469          * The baseline margin to use when the target is gone.
2470          */
2471         public int goneBaselineMargin = GONE_UNSET;
2472 
2473         /**
2474          * The baseline margin.
2475          */
2476         public int baselineMargin = 0;
2477 
2478         ///////////////////////////////////////////////////////////////////////////////////////////
2479         // Layout margins handling TODO: re-activate in 3.0
2480         ///////////////////////////////////////////////////////////////////////////////////////////
2481 
2482         /**
2483          * The left margin.
2484          */
2485         // public int leftMargin = 0;
2486 
2487         /**
2488          * The right margin.
2489          */
2490         // public int rightMargin = 0;
2491 
2492         // int originalLeftMargin = 0;
2493         // int originalRightMargin = 0;
2494 
2495         /**
2496          * The top margin.
2497          */
2498         // public int topMargin = 0;
2499 
2500         /**
2501          * The bottom margin.
2502          */
2503         // public int bottomMargin = 0;
2504 
2505         /**
2506          * The start margin.
2507          */
2508         // public int startMargin = UNSET;
2509 
2510         /**
2511          * The end margin.
2512          */
2513         // public int endMargin = UNSET;
2514 
2515         // boolean isRtl = false;
2516         // int layoutDirection = View.LAYOUT_DIRECTION_LTR;
2517 
2518         boolean mWidthSet = true; // need to be set to false when we reactivate this in 3.0
2519         boolean mHeightSet = true; // need to be set to false when we reactivate this in 3.0
2520 
2521         ///////////////////////////////////////////////////////////////////////////////////////////
2522 
2523         /**
2524          * The ratio between two connections when
2525          * the left and right (or start and end) sides are constrained.
2526          */
2527         public float horizontalBias = 0.5f;
2528 
2529         /**
2530          * The ratio between two connections when the top and bottom sides are constrained.
2531          */
2532         public float verticalBias = 0.5f;
2533 
2534         /**
2535          * The ratio information.
2536          */
2537         public String dimensionRatio = null;
2538 
2539         /**
2540          * The ratio between the width and height of the child.
2541          */
2542         float mDimensionRatioValue = 0;
2543 
2544         /**
2545          * The child's side to constrain using dimensRatio.
2546          */
2547         int mDimensionRatioSide = VERTICAL;
2548 
2549         /**
2550          * The child's weight that we can use to distribute the available horizontal space
2551          * in a chain, if the dimension behaviour is set to MATCH_CONSTRAINT
2552          */
2553         public float horizontalWeight = UNSET;
2554 
2555         /**
2556          * The child's weight that we can use to distribute the available vertical space
2557          * in a chain, if the dimension behaviour is set to MATCH_CONSTRAINT
2558          */
2559         public float verticalWeight = UNSET;
2560 
2561         /**
2562          * If the child is the start of a horizontal chain, this attribute will drive how
2563          * the elements of the chain will be positioned. The possible values are:
2564          * <ul>
2565          * <li>{@link #CHAIN_SPREAD} -- the elements will be spread out</li>
2566          * <li>{@link #CHAIN_SPREAD_INSIDE} -- similar, but the endpoints of the chain will not
2567          * be spread out</li>
2568          * <li>{@link #CHAIN_PACKED} -- the elements of the chain will be packed together. The
2569          * horizontal bias attribute of the child will then affect the positioning of the packed
2570          * elements</li>
2571          * </ul>
2572          */
2573         public int horizontalChainStyle = CHAIN_SPREAD;
2574 
2575         /**
2576          * If the child is the start of a vertical chain, this attribute will drive how
2577          * the elements of the chain will be positioned. The possible values are:
2578          * <ul>
2579          * <li>{@link #CHAIN_SPREAD} -- the elements will be spread out</li>
2580          * <li>{@link #CHAIN_SPREAD_INSIDE} -- similar, but the endpoints of the chain will not
2581          * be spread out</li>
2582          * <li>{@link #CHAIN_PACKED} -- the elements of the chain will be packed together. The
2583          * vertical bias attribute of the child will then affect the positioning of the packed
2584          * elements</li>
2585          * </ul>
2586          */
2587         public int verticalChainStyle = CHAIN_SPREAD;
2588 
2589         /**
2590          * Define how the widget horizontal dimension is handled when set to MATCH_CONSTRAINT
2591          * <ul>
2592          * <li>{@link #MATCH_CONSTRAINT_SPREAD} -- the default. The dimension will expand up to
2593          * the constraints, minus margins</li>
2594          * <li>{@link #MATCH_CONSTRAINT_WRAP} -- DEPRECATED -- use instead WRAP_CONTENT and
2595          * constrainedWidth=true<br>
2596          * The dimension will be the same as WRAP_CONTENT, unless the size ends
2597          * up too large for the constraints;
2598          * in that case the dimension will expand up to the constraints, minus margins
2599          * This attribute may not be applied if
2600          * the widget is part of a chain in that dimension.</li>
2601          * <li>{@link #MATCH_CONSTRAINT_PERCENT} -- The dimension will be a percent of another
2602          * widget (by default, the parent)</li>
2603          * </ul>
2604          */
2605         public int matchConstraintDefaultWidth = MATCH_CONSTRAINT_SPREAD;
2606 
2607         /**
2608          * Define how the widget vertical dimension is handled when set to MATCH_CONSTRAINT
2609          * <ul>
2610          * <li>{@link #MATCH_CONSTRAINT_SPREAD} -- the default. The dimension will expand up to
2611          * the constraints, minus margins</li>
2612          * <li>{@link #MATCH_CONSTRAINT_WRAP} -- DEPRECATED -- use instead WRAP_CONTENT and
2613          * constrainedWidth=true<br>
2614          * The dimension will be the same as WRAP_CONTENT, unless the size ends
2615          * up too large for the constraints;
2616          * in that case the dimension will expand up to the constraints, minus margins
2617          * This attribute may not be applied if the widget is
2618          * part of a chain in that dimension.</li>
2619          * <li>{@link #MATCH_CONSTRAINT_PERCENT} -- The dimension will be a percent of another
2620          * widget (by default, the parent)</li>
2621          * </ul>
2622          */
2623         public int matchConstraintDefaultHeight = MATCH_CONSTRAINT_SPREAD;
2624 
2625         /**
2626          * Specify a minimum width size for the widget.
2627          * It will only apply if the size of the widget
2628          * is set to MATCH_CONSTRAINT. Don't apply if the widget is part of a horizontal chain.
2629          */
2630         public int matchConstraintMinWidth = 0;
2631 
2632         /**
2633          * Specify a minimum height size for the widget.
2634          * It will only apply if the size of the widget
2635          * is set to MATCH_CONSTRAINT. Don't apply if the widget is part of a vertical chain.
2636          */
2637         public int matchConstraintMinHeight = 0;
2638 
2639         /**
2640          * Specify a maximum width size for the widget.
2641          * It will only apply if the size of the widget
2642          * is set to MATCH_CONSTRAINT. Don't apply if the widget is part of a horizontal chain.
2643          */
2644         public int matchConstraintMaxWidth = 0;
2645 
2646         /**
2647          * Specify a maximum height size for the widget.
2648          * It will only apply if the size of the widget
2649          * is set to MATCH_CONSTRAINT. Don't apply if the widget is part of a vertical chain.
2650          */
2651         public int matchConstraintMaxHeight = 0;
2652 
2653         /**
2654          * Specify the percentage when using the match constraint percent mode. From 0 to 1.
2655          */
2656         public float matchConstraintPercentWidth = 1;
2657 
2658         /**
2659          * Specify the percentage when using the match constraint percent mode. From 0 to 1.
2660          */
2661         public float matchConstraintPercentHeight = 1;
2662 
2663         /**
2664          * The design time location of the left side of the child.
2665          * Used at design time for a horizontally unconstrained child.
2666          */
2667         public int editorAbsoluteX = UNSET;
2668 
2669         /**
2670          * The design time location of the right side of the child.
2671          * Used at design time for a vertically unconstrained child.
2672          */
2673         public int editorAbsoluteY = UNSET;
2674 
2675         public int orientation = UNSET;
2676 
2677         /**
2678          * Specify if the horizontal dimension is constrained in
2679          * case both left & right constraints are set
2680          * and the widget dimension is not a fixed dimension. By default,
2681          * if a widget is set to WRAP_CONTENT,
2682          * we will treat that dimension as a fixed dimension,
2683          * meaning the dimension will not change regardless
2684          * of constraints. Setting this attribute to true allows the dimension to change
2685          * in order to respect constraints.
2686          */
2687         public boolean constrainedWidth = false;
2688 
2689         /**
2690          * Specify if the vertical dimension is constrained in case both
2691          * top & bottom constraints are set and the widget dimension is not a fixed dimension.
2692          * By default, if a widget is set to WRAP_CONTENT,
2693          * we will treat that dimension as a fixed dimension,
2694          * meaning the dimension will not change regardless
2695          * of constraints. Setting this attribute to true allows the
2696          * dimension to change in order to respect constraints.
2697          */
2698         public boolean constrainedHeight = false;
2699 
2700         /**
2701          * Define a category of view to be used by helpers and motionLayout
2702          */
2703         public String constraintTag = null;
2704 
2705         public static final int WRAP_BEHAVIOR_INCLUDED =
2706                 ConstraintWidget.WRAP_BEHAVIOR_INCLUDED;
2707         public static final int WRAP_BEHAVIOR_HORIZONTAL_ONLY =
2708                 ConstraintWidget.WRAP_BEHAVIOR_HORIZONTAL_ONLY;
2709         public static final int WRAP_BEHAVIOR_VERTICAL_ONLY =
2710                 ConstraintWidget.WRAP_BEHAVIOR_VERTICAL_ONLY;
2711         public static final int WRAP_BEHAVIOR_SKIPPED = ConstraintWidget.WRAP_BEHAVIOR_SKIPPED;
2712 
2713         /**
2714          * Specify how this view is taken in account during the parent's wrap computation
2715          *
2716          * Can be either of:
2717          * WRAP_BEHAVIOR_INCLUDED the widget is taken in account for the wrap (default)
2718          * WRAP_BEHAVIOR_HORIZONTAL_ONLY the widget will be included in the wrap only horizontally
2719          * WRAP_BEHAVIOR_VERTICAL_ONLY the widget will be included in the wrap only vertically
2720          * WRAP_BEHAVIOR_SKIPPED the widget is not part of the wrap computation
2721          */
2722         public int wrapBehaviorInParent = WRAP_BEHAVIOR_INCLUDED;
2723 
2724         // Internal use only
2725         boolean mHorizontalDimensionFixed = true;
2726         boolean mVerticalDimensionFixed = true;
2727 
2728         boolean mNeedsBaseline = false;
2729         boolean mIsGuideline = false;
2730         boolean mIsHelper = false;
2731         boolean mIsInPlaceholder = false;
2732         boolean mIsVirtualGroup = false;
2733 
2734         int mResolvedLeftToLeft = UNSET;
2735         int mResolvedLeftToRight = UNSET;
2736         int mResolvedRightToLeft = UNSET;
2737         int mResolvedRightToRight = UNSET;
2738         int mResolveGoneLeftMargin = GONE_UNSET;
2739         int mResolveGoneRightMargin = GONE_UNSET;
2740         float mResolvedHorizontalBias = 0.5f;
2741 
2742         int mResolvedGuideBegin;
2743         int mResolvedGuideEnd;
2744         float mResolvedGuidePercent;
2745 
2746         ConstraintWidget mWidget = new ConstraintWidget();
2747 
2748         /**
2749          *
2750          */
getConstraintWidget()2751         public ConstraintWidget getConstraintWidget() {
2752             return mWidget;
2753         }
2754 
2755         /**
2756          * @param text
2757          *
2758          */
setWidgetDebugName(String text)2759         public void setWidgetDebugName(String text) {
2760             mWidget.setDebugName(text);
2761         }
2762 
2763         /**
2764          * Reset the ConstraintWidget
2765          */
reset()2766         public void reset() {
2767             if (mWidget != null) {
2768                 mWidget.reset();
2769             }
2770         }
2771 
2772         public boolean helped = false;
2773 
2774         /**
2775          * Create a LayoutParams base on an existing layout Params
2776          *
2777          * @param params the Layout Params to be copied
2778          */
LayoutParams(ViewGroup.LayoutParams params)2779         public LayoutParams(ViewGroup.LayoutParams params) {
2780             super(params);
2781 
2782             // if the params is an instance of ViewGroup.MarginLayoutParams,
2783             // we should also copy margin relevant properties.
2784             if (params instanceof ViewGroup.MarginLayoutParams) {
2785                 MarginLayoutParams marginSource = (MarginLayoutParams) params;
2786                 this.leftMargin = marginSource.leftMargin;
2787                 this.rightMargin = marginSource.rightMargin;
2788                 this.topMargin = marginSource.topMargin;
2789                 this.bottomMargin = marginSource.bottomMargin;
2790                 this.setMarginStart(marginSource.getMarginStart());
2791                 this.setMarginEnd(marginSource.getMarginEnd());
2792             }
2793 
2794             if (!(params instanceof LayoutParams)) {
2795                 return;
2796             }
2797             LayoutParams source = (LayoutParams) params;
2798             ///////////////////////////////////////////////////////////////////////////////////////
2799             // Layout margins handling TODO: re-activate in 3.0
2800             ///////////////////////////////////////////////////////////////////////////////////////
2801             // this.layoutDirection = source.layoutDirection;
2802             // this.isRtl = source.isRtl;
2803             // this.originalLeftMargin = source.originalLeftMargin;
2804             // this.originalRightMargin = source.originalRightMargin;
2805             // this.startMargin = source.startMargin;
2806             // this.endMargin = source.endMargin;
2807             // this.leftMargin = source.leftMargin;
2808             // this.rightMargin = source.rightMargin;
2809             // this.topMargin = source.topMargin;
2810             // this.bottomMargin = source.bottomMargin;
2811             ///////////////////////////////////////////////////////////////////////////////////////
2812 
2813             this.guideBegin = source.guideBegin;
2814             this.guideEnd = source.guideEnd;
2815             this.guidePercent = source.guidePercent;
2816             this.guidelineUseRtl = source.guidelineUseRtl;
2817             this.leftToLeft = source.leftToLeft;
2818             this.leftToRight = source.leftToRight;
2819             this.rightToLeft = source.rightToLeft;
2820             this.rightToRight = source.rightToRight;
2821             this.topToTop = source.topToTop;
2822             this.topToBottom = source.topToBottom;
2823             this.bottomToTop = source.bottomToTop;
2824             this.bottomToBottom = source.bottomToBottom;
2825             this.baselineToBaseline = source.baselineToBaseline;
2826             this.baselineToTop = source.baselineToTop;
2827             this.baselineToBottom = source.baselineToBottom;
2828             this.circleConstraint = source.circleConstraint;
2829             this.circleRadius = source.circleRadius;
2830             this.circleAngle = source.circleAngle;
2831             this.startToEnd = source.startToEnd;
2832             this.startToStart = source.startToStart;
2833             this.endToStart = source.endToStart;
2834             this.endToEnd = source.endToEnd;
2835             this.goneLeftMargin = source.goneLeftMargin;
2836             this.goneTopMargin = source.goneTopMargin;
2837             this.goneRightMargin = source.goneRightMargin;
2838             this.goneBottomMargin = source.goneBottomMargin;
2839             this.goneStartMargin = source.goneStartMargin;
2840             this.goneEndMargin = source.goneEndMargin;
2841             this.goneBaselineMargin = source.goneBaselineMargin;
2842             this.baselineMargin = source.baselineMargin;
2843             this.horizontalBias = source.horizontalBias;
2844             this.verticalBias = source.verticalBias;
2845             this.dimensionRatio = source.dimensionRatio;
2846             this.mDimensionRatioValue = source.mDimensionRatioValue;
2847             this.mDimensionRatioSide = source.mDimensionRatioSide;
2848             this.horizontalWeight = source.horizontalWeight;
2849             this.verticalWeight = source.verticalWeight;
2850             this.horizontalChainStyle = source.horizontalChainStyle;
2851             this.verticalChainStyle = source.verticalChainStyle;
2852             this.constrainedWidth = source.constrainedWidth;
2853             this.constrainedHeight = source.constrainedHeight;
2854             this.matchConstraintDefaultWidth = source.matchConstraintDefaultWidth;
2855             this.matchConstraintDefaultHeight = source.matchConstraintDefaultHeight;
2856             this.matchConstraintMinWidth = source.matchConstraintMinWidth;
2857             this.matchConstraintMaxWidth = source.matchConstraintMaxWidth;
2858             this.matchConstraintMinHeight = source.matchConstraintMinHeight;
2859             this.matchConstraintMaxHeight = source.matchConstraintMaxHeight;
2860             this.matchConstraintPercentWidth = source.matchConstraintPercentWidth;
2861             this.matchConstraintPercentHeight = source.matchConstraintPercentHeight;
2862             this.editorAbsoluteX = source.editorAbsoluteX;
2863             this.editorAbsoluteY = source.editorAbsoluteY;
2864             this.orientation = source.orientation;
2865             this.mHorizontalDimensionFixed = source.mHorizontalDimensionFixed;
2866             this.mVerticalDimensionFixed = source.mVerticalDimensionFixed;
2867             this.mNeedsBaseline = source.mNeedsBaseline;
2868             this.mIsGuideline = source.mIsGuideline;
2869             this.mResolvedLeftToLeft = source.mResolvedLeftToLeft;
2870             this.mResolvedLeftToRight = source.mResolvedLeftToRight;
2871             this.mResolvedRightToLeft = source.mResolvedRightToLeft;
2872             this.mResolvedRightToRight = source.mResolvedRightToRight;
2873             this.mResolveGoneLeftMargin = source.mResolveGoneLeftMargin;
2874             this.mResolveGoneRightMargin = source.mResolveGoneRightMargin;
2875             this.mResolvedHorizontalBias = source.mResolvedHorizontalBias;
2876             this.constraintTag = source.constraintTag;
2877             this.wrapBehaviorInParent = source.wrapBehaviorInParent;
2878             this.mWidget = source.mWidget;
2879             this.mWidthSet = source.mWidthSet;
2880             this.mHeightSet = source.mHeightSet;
2881         }
2882 
2883         private static class Table {
2884             public static final int UNUSED = 0;
2885             public static final int ANDROID_ORIENTATION = 1;
2886             public static final int LAYOUT_CONSTRAINT_CIRCLE = 2;
2887             public static final int LAYOUT_CONSTRAINT_CIRCLE_RADIUS = 3;
2888             public static final int LAYOUT_CONSTRAINT_CIRCLE_ANGLE = 4;
2889             public static final int LAYOUT_CONSTRAINT_GUIDE_BEGIN = 5;
2890             public static final int LAYOUT_CONSTRAINT_GUIDE_END = 6;
2891             public static final int LAYOUT_CONSTRAINT_GUIDE_PERCENT = 7;
2892             public static final int LAYOUT_CONSTRAINT_LEFT_TO_LEFT_OF = 8;
2893             public static final int LAYOUT_CONSTRAINT_LEFT_TO_RIGHT_OF = 9;
2894             public static final int LAYOUT_CONSTRAINT_RIGHT_TO_LEFT_OF = 10;
2895             public static final int LAYOUT_CONSTRAINT_RIGHT_TO_RIGHT_OF = 11;
2896             public static final int LAYOUT_CONSTRAINT_TOP_TO_TOP_OF = 12;
2897             public static final int LAYOUT_CONSTRAINT_TOP_TO_BOTTOM_OF = 13;
2898             public static final int LAYOUT_CONSTRAINT_BOTTOM_TO_TOP_OF = 14;
2899             public static final int LAYOUT_CONSTRAINT_BOTTOM_TO_BOTTOM_OF = 15;
2900             public static final int LAYOUT_CONSTRAINT_BASELINE_TO_BASELINE_OF = 16;
2901             public static final int LAYOUT_CONSTRAINT_START_TO_END_OF = 17;
2902             public static final int LAYOUT_CONSTRAINT_START_TO_START_OF = 18;
2903             public static final int LAYOUT_CONSTRAINT_END_TO_START_OF = 19;
2904             public static final int LAYOUT_CONSTRAINT_END_TO_END_OF = 20;
2905             public static final int LAYOUT_GONE_MARGIN_LEFT = 21;
2906             public static final int LAYOUT_GONE_MARGIN_TOP = 22;
2907             public static final int LAYOUT_GONE_MARGIN_RIGHT = 23;
2908             public static final int LAYOUT_GONE_MARGIN_BOTTOM = 24;
2909             public static final int LAYOUT_GONE_MARGIN_START = 25;
2910             public static final int LAYOUT_GONE_MARGIN_END = 26;
2911             public static final int LAYOUT_CONSTRAINED_WIDTH = 27;
2912             public static final int LAYOUT_CONSTRAINED_HEIGHT = 28;
2913             public static final int LAYOUT_CONSTRAINT_HORIZONTAL_BIAS = 29;
2914             public static final int LAYOUT_CONSTRAINT_VERTICAL_BIAS = 30;
2915             public static final int LAYOUT_CONSTRAINT_WIDTH_DEFAULT = 31;
2916             public static final int LAYOUT_CONSTRAINT_HEIGHT_DEFAULT = 32;
2917             public static final int LAYOUT_CONSTRAINT_WIDTH_MIN = 33;
2918             public static final int LAYOUT_CONSTRAINT_WIDTH_MAX = 34;
2919             public static final int LAYOUT_CONSTRAINT_WIDTH_PERCENT = 35;
2920             public static final int LAYOUT_CONSTRAINT_HEIGHT_MIN = 36;
2921             public static final int LAYOUT_CONSTRAINT_HEIGHT_MAX = 37;
2922             public static final int LAYOUT_CONSTRAINT_HEIGHT_PERCENT = 38;
2923             public static final int LAYOUT_CONSTRAINT_LEFT_CREATOR = 39;
2924             public static final int LAYOUT_CONSTRAINT_TOP_CREATOR = 40;
2925             public static final int LAYOUT_CONSTRAINT_RIGHT_CREATOR = 41;
2926             public static final int LAYOUT_CONSTRAINT_BOTTOM_CREATOR = 42;
2927             public static final int LAYOUT_CONSTRAINT_BASELINE_CREATOR = 43;
2928             public static final int LAYOUT_CONSTRAINT_DIMENSION_RATIO = 44;
2929             public static final int LAYOUT_CONSTRAINT_HORIZONTAL_WEIGHT = 45;
2930             public static final int LAYOUT_CONSTRAINT_VERTICAL_WEIGHT = 46;
2931             public static final int LAYOUT_CONSTRAINT_HORIZONTAL_CHAINSTYLE = 47;
2932             public static final int LAYOUT_CONSTRAINT_VERTICAL_CHAINSTYLE = 48;
2933             public static final int LAYOUT_EDITOR_ABSOLUTEX = 49;
2934             public static final int LAYOUT_EDITOR_ABSOLUTEY = 50;
2935             public static final int LAYOUT_CONSTRAINT_TAG = 51;
2936             public static final int LAYOUT_CONSTRAINT_BASELINE_TO_TOP_OF = 52;
2937             public static final int LAYOUT_CONSTRAINT_BASELINE_TO_BOTTOM_OF = 53;
2938             public static final int LAYOUT_MARGIN_BASELINE = 54;
2939             public static final int LAYOUT_GONE_MARGIN_BASELINE = 55;
2940             ///////////////////////////////////////////////////////////////////////////////////////
2941             // Layout margins handling TODO: re-activate in 3.0
2942             ///////////////////////////////////////////////////////////////////////////////////////
2943             // public static final int LAYOUT_MARGIN_LEFT = 56;
2944             // public static final int LAYOUT_MARGIN_RIGHT = 57;
2945             // public static final int LAYOUT_MARGIN_TOP = 58;
2946             // public static final int LAYOUT_MARGIN_BOTTOM = 59;
2947             // public static final int LAYOUT_MARGIN_START = 60;
2948             // public static final int LAYOUT_MARGIN_END = 61;
2949             // public static final int LAYOUT_WIDTH = 62;
2950             // public static final int LAYOUT_HEIGHT = 63;
2951             ///////////////////////////////////////////////////////////////////////////////////////
2952             public static final int LAYOUT_CONSTRAINT_WIDTH = 64;
2953             public static final int LAYOUT_CONSTRAINT_HEIGHT = 65;
2954             public static final int LAYOUT_WRAP_BEHAVIOR_IN_PARENT = 66;
2955             public static final int GUIDELINE_USE_RTL = 67;
2956 
2957             public static final SparseIntArray sMap = new SparseIntArray();
2958 
2959             static {
2960                 ///////////////////////////////////////////////////////////////////////////////////
2961                 // Layout margins handling TODO: re-activate in 3.0
2962                 ///////////////////////////////////////////////////////////////////////////////////
2963                 // map.append(R.styleable.ConstraintLayout_Layout_android_layout_width,
2964                 // LAYOUT_WIDTH);
2965                 // map.append(R.styleable.ConstraintLayout_Layout_android_layout_height,
2966                 // LAYOUT_HEIGHT);
2967                 // map.append(R.styleable.ConstraintLayout_Layout_android_layout_marginLeft,
2968                 // LAYOUT_MARGIN_LEFT);
2969                 // map.append(R.styleable.ConstraintLayout_Layout_android_layout_marginRight,
2970                 // LAYOUT_MARGIN_RIGHT);
2971                 // map.append(R.styleable.ConstraintLayout_Layout_android_layout_marginTop,
2972                 // LAYOUT_MARGIN_TOP);
2973                 // map.append(R.styleable.ConstraintLayout_Layout_android_layout_marginBottom,
2974                 // LAYOUT_MARGIN_BOTTOM);
2975                 // map.append(R.styleable.ConstraintLayout_Layout_android_layout_marginStart,
2976                 // LAYOUT_MARGIN_START);
2977                 // map.append(R.styleable.ConstraintLayout_Layout_android_layout_marginEnd,
2978                 // LAYOUT_MARGIN_END);
2979                 //////////////////////////////////////////////////////////////////////////////////
sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintWidth, LAYOUT_CONSTRAINT_WIDTH)2980                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintWidth,
2981                         LAYOUT_CONSTRAINT_WIDTH);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintHeight, LAYOUT_CONSTRAINT_HEIGHT)2982                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintHeight,
2983                         LAYOUT_CONSTRAINT_HEIGHT);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintLeft_toLeftOf, LAYOUT_CONSTRAINT_LEFT_TO_LEFT_OF)2984                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintLeft_toLeftOf,
2985                         LAYOUT_CONSTRAINT_LEFT_TO_LEFT_OF);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintLeft_toRightOf, LAYOUT_CONSTRAINT_LEFT_TO_RIGHT_OF)2986                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintLeft_toRightOf,
2987                         LAYOUT_CONSTRAINT_LEFT_TO_RIGHT_OF);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintRight_toLeftOf, LAYOUT_CONSTRAINT_RIGHT_TO_LEFT_OF)2988                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintRight_toLeftOf,
2989                         LAYOUT_CONSTRAINT_RIGHT_TO_LEFT_OF);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintRight_toRightOf, LAYOUT_CONSTRAINT_RIGHT_TO_RIGHT_OF)2990                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintRight_toRightOf,
2991                         LAYOUT_CONSTRAINT_RIGHT_TO_RIGHT_OF);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintTop_toTopOf, LAYOUT_CONSTRAINT_TOP_TO_TOP_OF)2992                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintTop_toTopOf,
2993                         LAYOUT_CONSTRAINT_TOP_TO_TOP_OF);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintTop_toBottomOf, LAYOUT_CONSTRAINT_TOP_TO_BOTTOM_OF)2994                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintTop_toBottomOf,
2995                         LAYOUT_CONSTRAINT_TOP_TO_BOTTOM_OF);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintBottom_toTopOf, LAYOUT_CONSTRAINT_BOTTOM_TO_TOP_OF)2996                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintBottom_toTopOf,
2997                         LAYOUT_CONSTRAINT_BOTTOM_TO_TOP_OF);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintBottom_toBottomOf, LAYOUT_CONSTRAINT_BOTTOM_TO_BOTTOM_OF)2998                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintBottom_toBottomOf,
2999                         LAYOUT_CONSTRAINT_BOTTOM_TO_BOTTOM_OF);
sMap.append( R.styleable.ConstraintLayout_Layout_layout_constraintBaseline_toBaselineOf, LAYOUT_CONSTRAINT_BASELINE_TO_BASELINE_OF)3000                 sMap.append(
3001                         R.styleable.ConstraintLayout_Layout_layout_constraintBaseline_toBaselineOf,
3002                         LAYOUT_CONSTRAINT_BASELINE_TO_BASELINE_OF);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintBaseline_toTopOf, LAYOUT_CONSTRAINT_BASELINE_TO_TOP_OF)3003                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintBaseline_toTopOf,
3004                         LAYOUT_CONSTRAINT_BASELINE_TO_TOP_OF);
sMap.append(R.styleable .ConstraintLayout_Layout_layout_constraintBaseline_toBottomOf, LAYOUT_CONSTRAINT_BASELINE_TO_BOTTOM_OF)3005                 sMap.append(R.styleable
3006                                 .ConstraintLayout_Layout_layout_constraintBaseline_toBottomOf,
3007                         LAYOUT_CONSTRAINT_BASELINE_TO_BOTTOM_OF);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintCircle, LAYOUT_CONSTRAINT_CIRCLE)3008                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintCircle,
3009                         LAYOUT_CONSTRAINT_CIRCLE);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintCircleRadius, LAYOUT_CONSTRAINT_CIRCLE_RADIUS)3010                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintCircleRadius,
3011                         LAYOUT_CONSTRAINT_CIRCLE_RADIUS);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintCircleAngle, LAYOUT_CONSTRAINT_CIRCLE_ANGLE)3012                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintCircleAngle,
3013                         LAYOUT_CONSTRAINT_CIRCLE_ANGLE);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_editor_absoluteX, LAYOUT_EDITOR_ABSOLUTEX)3014                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_editor_absoluteX,
3015                         LAYOUT_EDITOR_ABSOLUTEX);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_editor_absoluteY, LAYOUT_EDITOR_ABSOLUTEY)3016                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_editor_absoluteY,
3017                         LAYOUT_EDITOR_ABSOLUTEY);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintGuide_begin, LAYOUT_CONSTRAINT_GUIDE_BEGIN)3018                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintGuide_begin,
3019                         LAYOUT_CONSTRAINT_GUIDE_BEGIN);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintGuide_end, LAYOUT_CONSTRAINT_GUIDE_END)3020                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintGuide_end,
3021                         LAYOUT_CONSTRAINT_GUIDE_END);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintGuide_percent, LAYOUT_CONSTRAINT_GUIDE_PERCENT)3022                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintGuide_percent,
3023                         LAYOUT_CONSTRAINT_GUIDE_PERCENT);
sMap.append(R.styleable.ConstraintLayout_Layout_guidelineUseRtl, GUIDELINE_USE_RTL)3024                 sMap.append(R.styleable.ConstraintLayout_Layout_guidelineUseRtl,
3025                         GUIDELINE_USE_RTL);
sMap.append(R.styleable.ConstraintLayout_Layout_android_orientation, ANDROID_ORIENTATION)3026                 sMap.append(R.styleable.ConstraintLayout_Layout_android_orientation,
3027                         ANDROID_ORIENTATION);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintStart_toEndOf, LAYOUT_CONSTRAINT_START_TO_END_OF)3028                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintStart_toEndOf,
3029                         LAYOUT_CONSTRAINT_START_TO_END_OF);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintStart_toStartOf, LAYOUT_CONSTRAINT_START_TO_START_OF)3030                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintStart_toStartOf,
3031                         LAYOUT_CONSTRAINT_START_TO_START_OF);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintEnd_toStartOf, LAYOUT_CONSTRAINT_END_TO_START_OF)3032                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintEnd_toStartOf,
3033                         LAYOUT_CONSTRAINT_END_TO_START_OF);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintEnd_toEndOf, LAYOUT_CONSTRAINT_END_TO_END_OF)3034                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintEnd_toEndOf,
3035                         LAYOUT_CONSTRAINT_END_TO_END_OF);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_goneMarginLeft, LAYOUT_GONE_MARGIN_LEFT)3036                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_goneMarginLeft,
3037                         LAYOUT_GONE_MARGIN_LEFT);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_goneMarginTop, LAYOUT_GONE_MARGIN_TOP)3038                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_goneMarginTop,
3039                         LAYOUT_GONE_MARGIN_TOP);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_goneMarginRight, LAYOUT_GONE_MARGIN_RIGHT)3040                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_goneMarginRight,
3041                         LAYOUT_GONE_MARGIN_RIGHT);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_goneMarginBottom, LAYOUT_GONE_MARGIN_BOTTOM)3042                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_goneMarginBottom,
3043                         LAYOUT_GONE_MARGIN_BOTTOM);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_goneMarginStart, LAYOUT_GONE_MARGIN_START)3044                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_goneMarginStart,
3045                         LAYOUT_GONE_MARGIN_START);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_goneMarginEnd, LAYOUT_GONE_MARGIN_END)3046                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_goneMarginEnd,
3047                         LAYOUT_GONE_MARGIN_END);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_goneMarginBaseline, LAYOUT_GONE_MARGIN_BASELINE)3048                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_goneMarginBaseline,
3049                         LAYOUT_GONE_MARGIN_BASELINE);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_marginBaseline, LAYOUT_MARGIN_BASELINE)3050                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_marginBaseline,
3051                         LAYOUT_MARGIN_BASELINE);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintHorizontal_bias, LAYOUT_CONSTRAINT_HORIZONTAL_BIAS)3052                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintHorizontal_bias,
3053                         LAYOUT_CONSTRAINT_HORIZONTAL_BIAS);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintVertical_bias, LAYOUT_CONSTRAINT_VERTICAL_BIAS)3054                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintVertical_bias,
3055                         LAYOUT_CONSTRAINT_VERTICAL_BIAS);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintDimensionRatio, LAYOUT_CONSTRAINT_DIMENSION_RATIO)3056                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintDimensionRatio,
3057                         LAYOUT_CONSTRAINT_DIMENSION_RATIO);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintHorizontal_weight, LAYOUT_CONSTRAINT_HORIZONTAL_WEIGHT)3058                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintHorizontal_weight,
3059                         LAYOUT_CONSTRAINT_HORIZONTAL_WEIGHT);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintVertical_weight, LAYOUT_CONSTRAINT_VERTICAL_WEIGHT)3060                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintVertical_weight,
3061                         LAYOUT_CONSTRAINT_VERTICAL_WEIGHT);
sMap.append( R.styleable.ConstraintLayout_Layout_layout_constraintHorizontal_chainStyle, LAYOUT_CONSTRAINT_HORIZONTAL_CHAINSTYLE)3062                 sMap.append(
3063                         R.styleable.ConstraintLayout_Layout_layout_constraintHorizontal_chainStyle,
3064                         LAYOUT_CONSTRAINT_HORIZONTAL_CHAINSTYLE);
sMap.append(R.styleable .ConstraintLayout_Layout_layout_constraintVertical_chainStyle, LAYOUT_CONSTRAINT_VERTICAL_CHAINSTYLE)3065                 sMap.append(R.styleable
3066                                 .ConstraintLayout_Layout_layout_constraintVertical_chainStyle,
3067                         LAYOUT_CONSTRAINT_VERTICAL_CHAINSTYLE);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_constrainedWidth, LAYOUT_CONSTRAINED_WIDTH)3068                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_constrainedWidth,
3069                         LAYOUT_CONSTRAINED_WIDTH);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_constrainedHeight, LAYOUT_CONSTRAINED_HEIGHT)3070                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_constrainedHeight,
3071                         LAYOUT_CONSTRAINED_HEIGHT);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintWidth_default, LAYOUT_CONSTRAINT_WIDTH_DEFAULT)3072                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintWidth_default,
3073                         LAYOUT_CONSTRAINT_WIDTH_DEFAULT);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintHeight_default, LAYOUT_CONSTRAINT_HEIGHT_DEFAULT)3074                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintHeight_default,
3075                         LAYOUT_CONSTRAINT_HEIGHT_DEFAULT);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintWidth_min, LAYOUT_CONSTRAINT_WIDTH_MIN)3076                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintWidth_min,
3077                         LAYOUT_CONSTRAINT_WIDTH_MIN);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintWidth_max, LAYOUT_CONSTRAINT_WIDTH_MAX)3078                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintWidth_max,
3079                         LAYOUT_CONSTRAINT_WIDTH_MAX);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintWidth_percent, LAYOUT_CONSTRAINT_WIDTH_PERCENT)3080                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintWidth_percent,
3081                         LAYOUT_CONSTRAINT_WIDTH_PERCENT);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintHeight_min, LAYOUT_CONSTRAINT_HEIGHT_MIN)3082                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintHeight_min,
3083                         LAYOUT_CONSTRAINT_HEIGHT_MIN);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintHeight_max, LAYOUT_CONSTRAINT_HEIGHT_MAX)3084                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintHeight_max,
3085                         LAYOUT_CONSTRAINT_HEIGHT_MAX);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintHeight_percent, LAYOUT_CONSTRAINT_HEIGHT_PERCENT)3086                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintHeight_percent,
3087                         LAYOUT_CONSTRAINT_HEIGHT_PERCENT);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintLeft_creator, LAYOUT_CONSTRAINT_LEFT_CREATOR)3088                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintLeft_creator,
3089                         LAYOUT_CONSTRAINT_LEFT_CREATOR);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintTop_creator, LAYOUT_CONSTRAINT_TOP_CREATOR)3090                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintTop_creator,
3091                         LAYOUT_CONSTRAINT_TOP_CREATOR);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintRight_creator, LAYOUT_CONSTRAINT_RIGHT_CREATOR)3092                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintRight_creator,
3093                         LAYOUT_CONSTRAINT_RIGHT_CREATOR);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintBottom_creator, LAYOUT_CONSTRAINT_BOTTOM_CREATOR)3094                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintBottom_creator,
3095                         LAYOUT_CONSTRAINT_BOTTOM_CREATOR);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintBaseline_creator, LAYOUT_CONSTRAINT_BASELINE_CREATOR)3096                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintBaseline_creator,
3097                         LAYOUT_CONSTRAINT_BASELINE_CREATOR);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintTag, LAYOUT_CONSTRAINT_TAG)3098                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_constraintTag,
3099                         LAYOUT_CONSTRAINT_TAG);
sMap.append(R.styleable.ConstraintLayout_Layout_layout_wrapBehaviorInParent, LAYOUT_WRAP_BEHAVIOR_IN_PARENT)3100                 sMap.append(R.styleable.ConstraintLayout_Layout_layout_wrapBehaviorInParent,
3101                         LAYOUT_WRAP_BEHAVIOR_IN_PARENT);
3102             }
3103         }
3104 
3105         ///////////////////////////////////////////////////////////////////////////////////////////
3106         // Layout margins handling TODO: re-activate in 3.0
3107         ///////////////////////////////////////////////////////////////////////////////////////////
3108         /*
3109         public void setMarginStart(int start) {
3110             startMargin = start;
3111         }
3112 
3113         public void setMarginEnd(int end) {
3114             endMargin = end;
3115         }
3116 
3117         public int getMarginStart() {
3118             return startMargin;
3119         }
3120 
3121         public int getMarginEnd() {
3122             return endMargin;
3123         }
3124 
3125         public int getLayoutDirection() {
3126             return layoutDirection;
3127         }
3128         */
3129         ///////////////////////////////////////////////////////////////////////////////////////
3130 
LayoutParams(Context c, AttributeSet attrs)3131         public LayoutParams(Context c, AttributeSet attrs) {
3132             super(c, attrs);
3133 
3134             TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ConstraintLayout_Layout);
3135             final int n = a.getIndexCount();
3136 
3137             ///////////////////////////////////////////////////////////////////////////////////////
3138             // Layout margins handling TODO: re-activate in 3.0
3139             ///////////////////////////////////////////////////////////////////////////////////////
3140             // super(WRAP_CONTENT, WRAP_CONTENT);
3141             /*
3142             if (n == 0) {
3143                // check if it's an include
3144                throw new IllegalArgumentException("Invalid LayoutParams supplied to " + this);
3145             }
3146 
3147             // let's first apply full margins if they are present.
3148             int margin = a.getDimensionPixelSize(R.styleable
3149             .ConstraintLayout_Layout_android_layout_margin, -1);
3150             int horizontalMargin = -1;
3151             int verticalMargin = -1;
3152             if (margin >= 0) {
3153                 originalLeftMargin = margin;
3154                 originalRightMargin = margin;
3155                 topMargin = margin;
3156                 bottomMargin = margin;
3157             } else {
3158                 horizontalMargin = a.getDimensionPixelSize(R.styleable
3159                 .ConstraintLayout_Layout_android_layout_marginHorizontal, -1);
3160                 verticalMargin = a.getDimensionPixelSize(R.styleable
3161                 .ConstraintLayout_Layout_android_layout_marginVertical, -1);
3162                 if (horizontalMargin >= 0) {
3163                     originalLeftMargin = horizontalMargin;
3164                     originalRightMargin = horizontalMargin;
3165                 }
3166                 if (verticalMargin >= 0) {
3167                     topMargin = verticalMargin;
3168                     bottomMargin = verticalMargin;
3169                 }
3170             }
3171             */
3172             //////////////////////////////////////////////////////////////////////////////////////
3173 
3174             for (int i = 0; i < n; i++) {
3175                 int attr = a.getIndex(i);
3176                 int look = Table.sMap.get(attr);
3177                 switch (look) {
3178                     case Table.UNUSED: {
3179                         // Skip
3180                         break;
3181                     }
3182                     case Table.LAYOUT_CONSTRAINT_WIDTH: {
3183                         ConstraintSet.parseDimensionConstraints(this, a, attr, HORIZONTAL);
3184                         mWidthSet = true;
3185                         break;
3186                     }
3187                     case Table.LAYOUT_CONSTRAINT_HEIGHT: {
3188                         ConstraintSet.parseDimensionConstraints(this, a, attr, VERTICAL);
3189                         mHeightSet = true;
3190                         break;
3191                     }
3192                     ///////////////////////////////////////////////////////////////////////////////
3193                     // Layout margins handling TODO: re-activate in 3.0
3194                     ///////////////////////////////////////////////////////////////////////////////
3195                     /*
3196                     case Table.LAYOUT_WIDTH: {
3197                         width = a.getLayoutDimension(R.styleable
3198                         .ConstraintLayout_Layout_android_layout_width, "layout_width");
3199                         widthSet = true;
3200                         break;
3201                     }
3202                     case Table.LAYOUT_HEIGHT: {
3203                         height = a.getLayoutDimension(R.styleable
3204                         .ConstraintLayout_Layout_android_layout_height, "layout_height");
3205                         heightSet = true;
3206                         break;
3207                     }
3208                     */
3209                     ///////////////////////////////////////////////////////////////////////////////
3210                     case Table.LAYOUT_WRAP_BEHAVIOR_IN_PARENT: {
3211                         wrapBehaviorInParent = a.getInt(attr, wrapBehaviorInParent);
3212                         break;
3213                     }
3214                     case Table.LAYOUT_CONSTRAINT_LEFT_TO_LEFT_OF: {
3215                         leftToLeft = a.getResourceId(attr, leftToLeft);
3216                         if (leftToLeft == UNSET) {
3217                             leftToLeft = a.getInt(attr, UNSET);
3218                         }
3219                         break;
3220                     }
3221                     case Table.LAYOUT_CONSTRAINT_LEFT_TO_RIGHT_OF: {
3222                         leftToRight = a.getResourceId(attr, leftToRight);
3223                         if (leftToRight == UNSET) {
3224                             leftToRight = a.getInt(attr, UNSET);
3225                         }
3226                         break;
3227                     }
3228                     case Table.LAYOUT_CONSTRAINT_RIGHT_TO_LEFT_OF: {
3229                         rightToLeft = a.getResourceId(attr, rightToLeft);
3230                         if (rightToLeft == UNSET) {
3231                             rightToLeft = a.getInt(attr, UNSET);
3232                         }
3233                         break;
3234                     }
3235                     case Table.LAYOUT_CONSTRAINT_RIGHT_TO_RIGHT_OF: {
3236                         rightToRight = a.getResourceId(attr, rightToRight);
3237                         if (rightToRight == UNSET) {
3238                             rightToRight = a.getInt(attr, UNSET);
3239                         }
3240                         break;
3241                     }
3242                     case Table.LAYOUT_CONSTRAINT_TOP_TO_TOP_OF: {
3243                         topToTop = a.getResourceId(attr, topToTop);
3244                         if (topToTop == UNSET) {
3245                             topToTop = a.getInt(attr, UNSET);
3246                         }
3247                         break;
3248                     }
3249                     case Table.LAYOUT_CONSTRAINT_TOP_TO_BOTTOM_OF: {
3250                         topToBottom = a.getResourceId(attr, topToBottom);
3251                         if (topToBottom == UNSET) {
3252                             topToBottom = a.getInt(attr, UNSET);
3253                         }
3254                         break;
3255                     }
3256                     case Table.LAYOUT_CONSTRAINT_BOTTOM_TO_TOP_OF: {
3257                         bottomToTop = a.getResourceId(attr, bottomToTop);
3258                         if (bottomToTop == UNSET) {
3259                             bottomToTop = a.getInt(attr, UNSET);
3260                         }
3261                         break;
3262                     }
3263                     case Table.LAYOUT_CONSTRAINT_BOTTOM_TO_BOTTOM_OF: {
3264                         bottomToBottom = a.getResourceId(attr, bottomToBottom);
3265                         if (bottomToBottom == UNSET) {
3266                             bottomToBottom = a.getInt(attr, UNSET);
3267                         }
3268                         break;
3269                     }
3270                     case Table.LAYOUT_CONSTRAINT_BASELINE_TO_BASELINE_OF: {
3271                         baselineToBaseline = a.getResourceId(attr, baselineToBaseline);
3272                         if (baselineToBaseline == UNSET) {
3273                             baselineToBaseline = a.getInt(attr, UNSET);
3274                         }
3275                         break;
3276                     }
3277                     case Table.LAYOUT_CONSTRAINT_BASELINE_TO_TOP_OF: {
3278                         baselineToTop = a.getResourceId(attr, baselineToTop);
3279                         if (baselineToTop == UNSET) {
3280                             baselineToTop = a.getInt(attr, UNSET);
3281                         }
3282                         break;
3283                     }
3284                     case Table.LAYOUT_CONSTRAINT_BASELINE_TO_BOTTOM_OF: {
3285                         baselineToBottom = a.getResourceId(attr, baselineToBottom);
3286                         if (baselineToBottom == UNSET) {
3287                             baselineToBottom = a.getInt(attr, UNSET);
3288                         }
3289                         break;
3290                     }
3291                     case Table.LAYOUT_CONSTRAINT_CIRCLE: {
3292                         circleConstraint = a.getResourceId(attr, circleConstraint);
3293                         if (circleConstraint == UNSET) {
3294                             circleConstraint = a.getInt(attr, UNSET);
3295                         }
3296                         break;
3297                     }
3298                     case Table.LAYOUT_CONSTRAINT_CIRCLE_RADIUS: {
3299                         circleRadius = a.getDimensionPixelSize(attr, circleRadius);
3300                         break;
3301                     }
3302                     case Table.LAYOUT_CONSTRAINT_CIRCLE_ANGLE: {
3303                         circleAngle = a.getFloat(attr, circleAngle) % 360;
3304                         if (circleAngle < 0) {
3305                             circleAngle = (360 - circleAngle) % 360;
3306                         }
3307                         break;
3308                     }
3309                     case Table.LAYOUT_EDITOR_ABSOLUTEX: {
3310                         editorAbsoluteX = a.getDimensionPixelOffset(attr, editorAbsoluteX);
3311                         break;
3312                     }
3313                     case Table.LAYOUT_EDITOR_ABSOLUTEY: {
3314                         editorAbsoluteY = a.getDimensionPixelOffset(attr, editorAbsoluteY);
3315                         break;
3316                     }
3317                     case Table.LAYOUT_CONSTRAINT_GUIDE_BEGIN: {
3318                         guideBegin = a.getDimensionPixelOffset(attr, guideBegin);
3319                         break;
3320                     }
3321 
3322                     case Table.LAYOUT_CONSTRAINT_GUIDE_END: {
3323                         guideEnd = a.getDimensionPixelOffset(attr, guideEnd);
3324                         break;
3325                     }
3326 
3327                     case Table.LAYOUT_CONSTRAINT_GUIDE_PERCENT: {
3328                         guidePercent = a.getFloat(attr, guidePercent);
3329                         break;
3330                     }
3331                     case Table.GUIDELINE_USE_RTL: {
3332                         guidelineUseRtl = a.getBoolean(attr, guidelineUseRtl);
3333                         break;
3334                     }
3335 
3336                     case Table.ANDROID_ORIENTATION: {
3337                         orientation = a.getInt(attr, orientation);
3338                         break;
3339                     }
3340 
3341                     case Table.LAYOUT_CONSTRAINT_START_TO_END_OF: {
3342                         startToEnd = a.getResourceId(attr, startToEnd);
3343                         if (startToEnd == UNSET) {
3344                             startToEnd = a.getInt(attr, UNSET);
3345                         }
3346                         break;
3347                     }
3348                     case Table.LAYOUT_CONSTRAINT_START_TO_START_OF: {
3349                         startToStart = a.getResourceId(attr, startToStart);
3350                         if (startToStart == UNSET) {
3351                             startToStart = a.getInt(attr, UNSET);
3352                         }
3353                         break;
3354                     }
3355                     case Table.LAYOUT_CONSTRAINT_END_TO_START_OF: {
3356                         endToStart = a.getResourceId(attr, endToStart);
3357                         if (endToStart == UNSET) {
3358                             endToStart = a.getInt(attr, UNSET);
3359                         }
3360                         break;
3361                     }
3362                     case Table.LAYOUT_CONSTRAINT_END_TO_END_OF: {
3363                         endToEnd = a.getResourceId(attr, endToEnd);
3364                         if (endToEnd == UNSET) {
3365                             endToEnd = a.getInt(attr, UNSET);
3366                         }
3367                         break;
3368                     }
3369                     case Table.LAYOUT_GONE_MARGIN_LEFT: {
3370                         goneLeftMargin = a.getDimensionPixelSize(attr, goneLeftMargin);
3371                         break;
3372                     }
3373                     case Table.LAYOUT_GONE_MARGIN_TOP: {
3374                         goneTopMargin = a.getDimensionPixelSize(attr, goneTopMargin);
3375                         break;
3376                     }
3377                     case Table.LAYOUT_GONE_MARGIN_RIGHT: {
3378                         goneRightMargin = a.getDimensionPixelSize(attr, goneRightMargin);
3379                         break;
3380                     }
3381                     case Table.LAYOUT_GONE_MARGIN_BOTTOM: {
3382                         goneBottomMargin = a.getDimensionPixelSize(attr, goneBottomMargin);
3383                         break;
3384                     }
3385                     case Table.LAYOUT_GONE_MARGIN_START: {
3386                         goneStartMargin = a.getDimensionPixelSize(attr, goneStartMargin);
3387                         break;
3388                     }
3389                     case Table.LAYOUT_GONE_MARGIN_END: {
3390                         goneEndMargin = a.getDimensionPixelSize(attr, goneEndMargin);
3391                         break;
3392                     }
3393                     case Table.LAYOUT_GONE_MARGIN_BASELINE: {
3394                         goneBaselineMargin = a.getDimensionPixelSize(attr, goneBaselineMargin);
3395                         break;
3396                     }
3397                     case Table.LAYOUT_MARGIN_BASELINE: {
3398                         baselineMargin = a.getDimensionPixelSize(attr, baselineMargin);
3399                         break;
3400                     }
3401                     //////////////////////////////////////////////////////////////////////////////
3402                     // Layout margins handling TODO: re-activate in 3.0
3403                     //////////////////////////////////////////////////////////////////////////////
3404                     /*
3405                     case Table.LAYOUT_MARGIN_LEFT: {
3406                         if (margin == -1 && horizontalMargin == -1) {
3407                             originalLeftMargin = a.getDimensionPixelSize(attr, originalLeftMargin);
3408                         }
3409                         break;
3410                     }
3411                     case Table.LAYOUT_MARGIN_RIGHT: {
3412                         if (margin == -1 && horizontalMargin == -1) {
3413                             originalRightMargin =
3414                                 a.getDimensionPixelSize(attr, originalRightMargin);
3415                         }
3416                         break;
3417                     }
3418                     case Table.LAYOUT_MARGIN_TOP: {
3419                         if (margin == -1 && verticalMargin == -1) {
3420                             topMargin = a.getDimensionPixelSize(attr, topMargin);
3421                         }
3422                         break;
3423                     }
3424                     case Table.LAYOUT_MARGIN_BOTTOM: {
3425                         if (margin == -1 && verticalMargin == -1) {
3426                             bottomMargin = a.getDimensionPixelSize(attr, bottomMargin);
3427                         }
3428                         break;
3429                     }
3430                     case Table.LAYOUT_MARGIN_START: {
3431                         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
3432                             if (margin == -1 && horizontalMargin == -1) {
3433                                 startMargin = a.getDimensionPixelSize(attr, startMargin);
3434                             }
3435                         }
3436                         break;
3437                     }
3438                     case Table.LAYOUT_MARGIN_END: {
3439                         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
3440                             if (margin == -1 && horizontalMargin == -1) {
3441                                 endMargin = a.getDimensionPixelSize(attr, endMargin);
3442                             }
3443                         }
3444                         break;
3445                     }
3446                     */
3447                     ///////////////////////////////////////////////////////////////////////////////
3448                     case Table.LAYOUT_CONSTRAINT_HORIZONTAL_BIAS: {
3449                         horizontalBias = a.getFloat(attr, horizontalBias);
3450                         break;
3451                     }
3452                     case Table.LAYOUT_CONSTRAINT_VERTICAL_BIAS: {
3453                         verticalBias = a.getFloat(attr, verticalBias);
3454                         break;
3455                     }
3456                     case Table.LAYOUT_CONSTRAINT_DIMENSION_RATIO: {
3457                         ConstraintSet.parseDimensionRatioString(this, a.getString(attr));
3458                         break;
3459                     }
3460                     case Table.LAYOUT_CONSTRAINT_HORIZONTAL_WEIGHT: {
3461                         horizontalWeight = a.getFloat(attr, horizontalWeight);
3462                         break;
3463                     }
3464                     case Table.LAYOUT_CONSTRAINT_VERTICAL_WEIGHT: {
3465                         verticalWeight = a.getFloat(attr, verticalWeight);
3466                         break;
3467                     }
3468                     case Table.LAYOUT_CONSTRAINT_HORIZONTAL_CHAINSTYLE: {
3469                         horizontalChainStyle = a.getInt(attr, CHAIN_SPREAD);
3470                         break;
3471                     }
3472                     case Table.LAYOUT_CONSTRAINT_VERTICAL_CHAINSTYLE: {
3473                         verticalChainStyle = a.getInt(attr, CHAIN_SPREAD);
3474                         break;
3475                     }
3476                     case Table.LAYOUT_CONSTRAINED_WIDTH: {
3477                         constrainedWidth = a.getBoolean(attr, constrainedWidth);
3478                         break;
3479                     }
3480                     case Table.LAYOUT_CONSTRAINED_HEIGHT: {
3481                         constrainedHeight = a.getBoolean(attr, constrainedHeight);
3482                         break;
3483                     }
3484                     case Table.LAYOUT_CONSTRAINT_WIDTH_DEFAULT: {
3485                         matchConstraintDefaultWidth = a.getInt(attr, MATCH_CONSTRAINT_SPREAD);
3486                         if (matchConstraintDefaultWidth == MATCH_CONSTRAINT_WRAP) {
3487                             Log.e(TAG, "layout_constraintWidth_default=\"wrap\" is deprecated."
3488                                     + "\nUse layout_width=\"WRAP_CONTENT\" and "
3489                                     + "layout_constrainedWidth=\"true\" instead.");
3490                         }
3491                         break;
3492                     }
3493                     case Table.LAYOUT_CONSTRAINT_HEIGHT_DEFAULT: {
3494                         matchConstraintDefaultHeight = a.getInt(attr, MATCH_CONSTRAINT_SPREAD);
3495                         if (matchConstraintDefaultHeight == MATCH_CONSTRAINT_WRAP) {
3496                             Log.e(TAG, "layout_constraintHeight_default=\"wrap\" is deprecated."
3497                                     + "\nUse layout_height=\"WRAP_CONTENT\" and "
3498                                     + "layout_constrainedHeight=\"true\" instead.");
3499                         }
3500                         break;
3501                     }
3502                     case Table.LAYOUT_CONSTRAINT_WIDTH_MIN: {
3503                         try {
3504                             matchConstraintMinWidth = a.getDimensionPixelSize(attr,
3505                                     matchConstraintMinWidth);
3506                         } catch (Exception e) {
3507                             int value = a.getInt(attr, matchConstraintMinWidth);
3508                             if (value == WRAP_CONTENT) {
3509                                 matchConstraintMinWidth = WRAP_CONTENT;
3510                             }
3511                         }
3512                         break;
3513                     }
3514                     case Table.LAYOUT_CONSTRAINT_WIDTH_MAX: {
3515                         try {
3516                             matchConstraintMaxWidth = a.getDimensionPixelSize(attr,
3517                                     matchConstraintMaxWidth);
3518                         } catch (Exception e) {
3519                             int value = a.getInt(attr, matchConstraintMaxWidth);
3520                             if (value == WRAP_CONTENT) {
3521                                 matchConstraintMaxWidth = WRAP_CONTENT;
3522                             }
3523                         }
3524                         break;
3525                     }
3526                     case Table.LAYOUT_CONSTRAINT_WIDTH_PERCENT: {
3527                         matchConstraintPercentWidth = Math.max(0, a.getFloat(attr,
3528                                 matchConstraintPercentWidth));
3529                         matchConstraintDefaultWidth = MATCH_CONSTRAINT_PERCENT;
3530                         break;
3531                     }
3532                     case Table.LAYOUT_CONSTRAINT_HEIGHT_MIN: {
3533                         try {
3534                             matchConstraintMinHeight = a.getDimensionPixelSize(attr,
3535                                     matchConstraintMinHeight);
3536                         } catch (Exception e) {
3537                             int value = a.getInt(attr, matchConstraintMinHeight);
3538                             if (value == WRAP_CONTENT) {
3539                                 matchConstraintMinHeight = WRAP_CONTENT;
3540                             }
3541                         }
3542                         break;
3543                     }
3544                     case Table.LAYOUT_CONSTRAINT_HEIGHT_MAX: {
3545                         try {
3546                             matchConstraintMaxHeight = a.getDimensionPixelSize(attr,
3547                                     matchConstraintMaxHeight);
3548                         } catch (Exception e) {
3549                             int value = a.getInt(attr, matchConstraintMaxHeight);
3550                             if (value == WRAP_CONTENT) {
3551                                 matchConstraintMaxHeight = WRAP_CONTENT;
3552                             }
3553                         }
3554                         break;
3555                     }
3556                     case Table.LAYOUT_CONSTRAINT_HEIGHT_PERCENT: {
3557                         matchConstraintPercentHeight = Math.max(0, a.getFloat(attr,
3558                                 matchConstraintPercentHeight));
3559                         matchConstraintDefaultHeight = MATCH_CONSTRAINT_PERCENT;
3560                         break;
3561                     }
3562                     case Table.LAYOUT_CONSTRAINT_TAG:
3563                         constraintTag = a.getString(attr);
3564                         break;
3565                     case Table.LAYOUT_CONSTRAINT_LEFT_CREATOR: {
3566                         // Skip
3567                         break;
3568                     }
3569                     case Table.LAYOUT_CONSTRAINT_TOP_CREATOR: {
3570                         // Skip
3571                         break;
3572                     }
3573                     case Table.LAYOUT_CONSTRAINT_RIGHT_CREATOR: {
3574                         // Skip
3575                         break;
3576                     }
3577                     case Table.LAYOUT_CONSTRAINT_BOTTOM_CREATOR: {
3578                         // Skip
3579                         break;
3580                     }
3581                     case Table.LAYOUT_CONSTRAINT_BASELINE_CREATOR: {
3582                         // Skip
3583                         break;
3584                     }
3585                 }
3586             }
3587 
3588             ///////////////////////////////////////////////////////////////////////////////////////
3589             // Layout margins handling TODO: re-activate in 3.0
3590             ///////////////////////////////////////////////////////////////////////////////////////
3591             /*
3592             if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
3593                 leftMargin = originalLeftMargin;
3594                 rightMargin = originalRightMargin;
3595             }
3596             */
3597             ///////////////////////////////////////////////////////////////////////////////////////
3598 
3599             a.recycle();
3600             validate();
3601         }
3602 
3603         /**
3604          * validate the layout
3605          */
validate()3606         public void validate() {
3607             mIsGuideline = false;
3608             mHorizontalDimensionFixed = true;
3609             mVerticalDimensionFixed = true;
3610             ///////////////////////////////////////////////////////////////////////////////////////
3611             // Layout margins handling TODO: re-activate in 3.0
3612             ///////////////////////////////////////////////////////////////////////////////////////
3613             /*
3614             if (dimensionRatio != null && !widthSet && !heightSet) {
3615                 width = MATCH_CONSTRAINT;
3616                 height = MATCH_CONSTRAINT;
3617             }
3618             */
3619             ///////////////////////////////////////////////////////////////////////////////////////
3620 
3621             if (width == WRAP_CONTENT && constrainedWidth) {
3622                 mHorizontalDimensionFixed = false;
3623                 if (matchConstraintDefaultWidth == MATCH_CONSTRAINT_SPREAD) {
3624                     matchConstraintDefaultWidth = MATCH_CONSTRAINT_WRAP;
3625                 }
3626             }
3627             if (height == WRAP_CONTENT && constrainedHeight) {
3628                 mVerticalDimensionFixed = false;
3629                 if (matchConstraintDefaultHeight == MATCH_CONSTRAINT_SPREAD) {
3630                     matchConstraintDefaultHeight = MATCH_CONSTRAINT_WRAP;
3631                 }
3632             }
3633             if (width == MATCH_CONSTRAINT || width == MATCH_PARENT) {
3634                 mHorizontalDimensionFixed = false;
3635                 // We have to reset LayoutParams width/height to WRAP_CONTENT here,
3636                 // as some widgets like TextView
3637                 // will use the layout params directly as a hint to know
3638                 // if they need to request a layout
3639                 // when their content change (e.g. during setTextView)
3640                 if (width == MATCH_CONSTRAINT
3641                         && matchConstraintDefaultWidth == MATCH_CONSTRAINT_WRAP) {
3642                     width = WRAP_CONTENT;
3643                     constrainedWidth = true;
3644                 }
3645             }
3646             if (height == MATCH_CONSTRAINT || height == MATCH_PARENT) {
3647                 mVerticalDimensionFixed = false;
3648                 // We have to reset LayoutParams width/height to WRAP_CONTENT here,
3649                 // as some widgets like TextView
3650                 // will use the layout params directly as a hint to know
3651                 // if they need to request a layout
3652                 // when their content change (e.g. during setTextView)
3653                 if (height == MATCH_CONSTRAINT
3654                         && matchConstraintDefaultHeight == MATCH_CONSTRAINT_WRAP) {
3655                     height = WRAP_CONTENT;
3656                     constrainedHeight = true;
3657                 }
3658             }
3659             if (guidePercent != UNSET || guideBegin != UNSET || guideEnd != UNSET) {
3660                 mIsGuideline = true;
3661                 mHorizontalDimensionFixed = true;
3662                 mVerticalDimensionFixed = true;
3663                 if (!(mWidget instanceof Guideline)) {
3664                     mWidget = new Guideline();
3665                 }
3666                 ((Guideline) mWidget).setOrientation(orientation);
3667             }
3668         }
3669 
LayoutParams(int width, int height)3670         public LayoutParams(int width, int height) {
3671             super(width, height);
3672         }
3673 
3674         /**
3675          * {@inheritDoc}
3676          */
3677         @Override
resolveLayoutDirection(int layoutDirection)3678         public void resolveLayoutDirection(int layoutDirection) {
3679             ///////////////////////////////////////////////////////////////////////////////////////
3680             // Layout margins handling TODO: re-activate in 3.0
3681             ///////////////////////////////////////////////////////////////////////////////////////
3682             /*
3683             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
3684                 this.layoutDirection = layoutDirection;
3685                 isRtl = (View.LAYOUT_DIRECTION_RTL == layoutDirection);
3686             }
3687 
3688             // First apply margins.
3689             leftMargin = originalLeftMargin;
3690             rightMargin = originalRightMargin;
3691 
3692             if (isRtl) {
3693                 leftMargin = originalRightMargin;
3694                 rightMargin = originalLeftMargin;
3695                 if (startMargin != UNSET) {
3696                     rightMargin = startMargin;
3697                 }
3698                 if (endMargin != UNSET) {
3699                     leftMargin = endMargin;
3700                 }
3701             } else {
3702                 if (startMargin != UNSET) {
3703                     leftMargin = startMargin;
3704                 }
3705                 if (endMargin != UNSET) {
3706                     rightMargin = endMargin;
3707                 }
3708             }
3709             */
3710             ///////////////////////////////////////////////////////////////////////////////////////
3711             int originalLeftMargin = leftMargin;
3712             int originalRightMargin = rightMargin;
3713 
3714             super.resolveLayoutDirection(layoutDirection);
3715             boolean isRtl = (View.LAYOUT_DIRECTION_RTL == getLayoutDirection());
3716             ///////////////////////////////////////////////////////////////////////////////////////
3717 
3718             mResolvedRightToLeft = UNSET;
3719             mResolvedRightToRight = UNSET;
3720             mResolvedLeftToLeft = UNSET;
3721             mResolvedLeftToRight = UNSET;
3722 
3723             mResolveGoneLeftMargin = UNSET;
3724             mResolveGoneRightMargin = UNSET;
3725             mResolveGoneLeftMargin = goneLeftMargin;
3726             mResolveGoneRightMargin = goneRightMargin;
3727             mResolvedHorizontalBias = horizontalBias;
3728 
3729             mResolvedGuideBegin = guideBegin;
3730             mResolvedGuideEnd = guideEnd;
3731             mResolvedGuidePercent = guidePercent;
3732 
3733             // Post JB MR1, if start/end are defined, they take precedence over left/right
3734             if (isRtl) {
3735                 boolean startEndDefined = false;
3736                 if (startToEnd != UNSET) {
3737                     mResolvedRightToLeft = startToEnd;
3738                     startEndDefined = true;
3739                 } else if (startToStart != UNSET) {
3740                     mResolvedRightToRight = startToStart;
3741                     startEndDefined = true;
3742                 }
3743                 if (endToStart != UNSET) {
3744                     mResolvedLeftToRight = endToStart;
3745                     startEndDefined = true;
3746                 }
3747                 if (endToEnd != UNSET) {
3748                     mResolvedLeftToLeft = endToEnd;
3749                     startEndDefined = true;
3750                 }
3751                 if (goneStartMargin != GONE_UNSET) {
3752                     mResolveGoneRightMargin = goneStartMargin;
3753                 }
3754                 if (goneEndMargin != GONE_UNSET) {
3755                     mResolveGoneLeftMargin = goneEndMargin;
3756                 }
3757                 if (startEndDefined) {
3758                     mResolvedHorizontalBias = 1 - horizontalBias;
3759                 }
3760 
3761                 // Only apply to vertical guidelines
3762                 if (mIsGuideline && orientation == Guideline.VERTICAL && guidelineUseRtl) {
3763                     if (guidePercent != UNSET) {
3764                         mResolvedGuidePercent = 1 - guidePercent;
3765                         mResolvedGuideBegin = UNSET;
3766                         mResolvedGuideEnd = UNSET;
3767                     } else if (guideBegin != UNSET) {
3768                         mResolvedGuideEnd = guideBegin;
3769                         mResolvedGuideBegin = UNSET;
3770                         mResolvedGuidePercent = UNSET;
3771                     } else if (guideEnd != UNSET) {
3772                         mResolvedGuideBegin = guideEnd;
3773                         mResolvedGuideEnd = UNSET;
3774                         mResolvedGuidePercent = UNSET;
3775                     }
3776                 }
3777             } else {
3778                 if (startToEnd != UNSET) {
3779                     mResolvedLeftToRight = startToEnd;
3780                 }
3781                 if (startToStart != UNSET) {
3782                     mResolvedLeftToLeft = startToStart;
3783                 }
3784                 if (endToStart != UNSET) {
3785                     mResolvedRightToLeft = endToStart;
3786                 }
3787                 if (endToEnd != UNSET) {
3788                     mResolvedRightToRight = endToEnd;
3789                 }
3790                 if (goneStartMargin != GONE_UNSET) {
3791                     mResolveGoneLeftMargin = goneStartMargin;
3792                 }
3793                 if (goneEndMargin != GONE_UNSET) {
3794                     mResolveGoneRightMargin = goneEndMargin;
3795                 }
3796             }
3797             // if no constraint is defined via RTL attributes, use left/right if present
3798             if (endToStart == UNSET && endToEnd == UNSET
3799                     && startToStart == UNSET && startToEnd == UNSET) {
3800                 if (rightToLeft != UNSET) {
3801                     mResolvedRightToLeft = rightToLeft;
3802                     if (rightMargin <= 0 && originalRightMargin > 0) {
3803                         rightMargin = originalRightMargin;
3804                     }
3805                 } else if (rightToRight != UNSET) {
3806                     mResolvedRightToRight = rightToRight;
3807                     if (rightMargin <= 0 && originalRightMargin > 0) {
3808                         rightMargin = originalRightMargin;
3809                     }
3810                 }
3811                 if (leftToLeft != UNSET) {
3812                     mResolvedLeftToLeft = leftToLeft;
3813                     if (leftMargin <= 0 && originalLeftMargin > 0) {
3814                         leftMargin = originalLeftMargin;
3815                     }
3816                 } else if (leftToRight != UNSET) {
3817                     mResolvedLeftToRight = leftToRight;
3818                     if (leftMargin <= 0 && originalLeftMargin > 0) {
3819                         leftMargin = originalLeftMargin;
3820                     }
3821                 }
3822             }
3823         }
3824 
3825         /**
3826          * Tag that can be used to identify a view as being a member of a group.
3827          * Which can be used for Helpers or in MotionLayout
3828          *
3829          * @return tag string or null if not defined
3830          */
getConstraintTag()3831         public String getConstraintTag() {
3832             return constraintTag;
3833         }
3834     }
3835 
3836     /**
3837      * {@inheritDoc}
3838      */
3839     @Override
requestLayout()3840     public void requestLayout() {
3841         markHierarchyDirty();
3842         super.requestLayout();
3843     }
3844 
3845     @Override
forceLayout()3846     public void forceLayout() {
3847         markHierarchyDirty();
3848         super.forceLayout();
3849     }
3850 
markHierarchyDirty()3851     private void markHierarchyDirty() {
3852         mDirtyHierarchy = true;
3853         // reset measured cache
3854         mLastMeasureWidth = -1;
3855         mLastMeasureHeight = -1;
3856         mLastMeasureWidthSize = -1;
3857         mLastMeasureHeightSize = -1;
3858         mLastMeasureWidthMode = MeasureSpec.UNSPECIFIED;
3859         mLastMeasureHeightMode = MeasureSpec.UNSPECIFIED;
3860     }
3861 
3862     /**
3863      *
3864      *
3865      * @return
3866      */
3867     @Override
shouldDelayChildPressedState()3868     public boolean shouldDelayChildPressedState() {
3869         return false;
3870     }
3871 
3872     /**
3873      * Returns a JSON5 string useful for debugging the constraints actually applied.
3874      * In situations where a complex set of code dynamically constructs constraints
3875      * it is useful to be able to query the layout for what are the constraints.
3876      * @return json5 string representing the constraint in effect now.
3877      */
getSceneString()3878     public String getSceneString() {
3879         StringBuilder ret = new StringBuilder();
3880 
3881         if (mLayoutWidget.stringId == null) {
3882             int id = this.getId();
3883             if (id != -1) {
3884                 String str = getContext().getResources().getResourceEntryName(id);
3885                 mLayoutWidget.stringId = str;
3886             } else {
3887                 mLayoutWidget.stringId = "parent";
3888             }
3889         }
3890         if (mLayoutWidget.getDebugName() == null) {
3891             mLayoutWidget.setDebugName(mLayoutWidget.stringId);
3892             Log.v(TAG, " setDebugName " + mLayoutWidget.getDebugName());
3893         }
3894 
3895         ArrayList<ConstraintWidget> children = mLayoutWidget.getChildren();
3896         for (ConstraintWidget child : children) {
3897             View v = (View) child.getCompanionWidget();
3898             if (v != null) {
3899                 if (child.stringId == null) {
3900                     int id = v.getId();
3901                     if (id != -1) {
3902                         String str = getContext().getResources().getResourceEntryName(id);
3903                         child.stringId = str;
3904                     }
3905                 }
3906                 if (child.getDebugName() == null) {
3907                     child.setDebugName(child.stringId);
3908                     Log.v(TAG, " setDebugName " + child.getDebugName());
3909                 }
3910 
3911             }
3912         }
3913         mLayoutWidget.getSceneString(ret);
3914         return ret.toString();
3915     }
3916 
3917     /**
3918      * This is the interface to a valued modifier.
3919      * implement this and add it using addValueModifier
3920      */
3921     public interface ValueModifier {
3922         /**
3923          *  if needed in the implementation modify params and return true
3924          * @param width of the ConstraintLayout in pixels
3925          * @param height of the ConstraintLayout in pixels
3926          * @param id The id of the view which
3927          * @param view The View
3928          * @param params The layout params of the view
3929          * @return true if you modified the layout params
3930          */
update(int width, int height, int id, View view, LayoutParams params)3931         boolean update(int width, int height, int id, View view, LayoutParams params);
3932     }
3933 
3934     private ArrayList<ValueModifier> mModifiers;
3935 
3936     /**
3937      * a ValueModify to the ConstraintLayout.
3938      * This can be useful to add custom behavour to the ConstraintLayout or
3939      * address limitation of the capabilities of Constraint Layout
3940      * @param modifier
3941      */
addValueModifier(ValueModifier modifier)3942     public void addValueModifier(ValueModifier modifier) {
3943         if (mModifiers == null) {
3944             mModifiers = new ArrayList<>();
3945         }
3946         mModifiers.add(modifier);
3947     }
3948 
3949     /**
3950      * Remove a value modifier this can be useful if the modifier is used during in one state of the
3951      * system.
3952      * @param modifier The modifier to remove
3953      */
removeValueModifier(ValueModifier modifier)3954     void removeValueModifier(ValueModifier modifier) {
3955         if (modifier == null) {
3956             return;
3957         }
3958         mModifiers.remove(modifier);
3959     }
3960 
3961     /**
3962      * This can be overridden to change the way Modifiers are used.
3963      * @param widthMeasureSpec
3964      * @param heightMeasureSpec
3965      * @return
3966      */
dynamicUpdateConstraints(int widthMeasureSpec, int heightMeasureSpec)3967     protected boolean dynamicUpdateConstraints(int widthMeasureSpec, int heightMeasureSpec) {
3968         if (mModifiers == null) {
3969             return false;
3970         }
3971         boolean dirty = false;
3972         int width = MeasureSpec.getSize(widthMeasureSpec);
3973         int height = MeasureSpec.getSize(heightMeasureSpec);
3974         for (ValueModifier m : mModifiers) {
3975             for (ConstraintWidget widget : mLayoutWidget.getChildren()) {
3976                 View view = (View) widget.getCompanionWidget();
3977                 int id = view.getId();
3978                 LayoutParams layoutParams = (LayoutParams) view.getLayoutParams();
3979                 dirty |= m.update(width, height, id, view, layoutParams);
3980             }
3981         }
3982         return dirty;
3983     }
3984 }
3985