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