ConstraintLayout源码

/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package android.support.constraint;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.os.Build;
import android.support.constraint.solver.widgets.ConstraintAnchor;
import android.support.constraint.solver.widgets.ConstraintWidget;
import android.support.constraint.solver.widgets.ConstraintWidgetContainer;
import android.support.constraint.solver.widgets.Guideline;
import android.util.AttributeSet;
import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.HashMap;
import static android.support.constraint.ConstraintLayout.LayoutParams.*;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
/**
 * A {@code ConstraintLayout} is a {@link android.view.ViewGroup} which allows you
 * to position and size widgets in a flexible way.
 *<p>
 *     <b>Note:</b> {@code ConstraintLayout} is available as a support library that you can use
 *     on Android systems starting with API level 9 (Gingerbread).
 *     As such, we are planning in enriching its API and capabilities over time.
 *     This documentation will reflects the changes.
 *</p>
 * <p>
 * There are currently various types of constraints that you can use:
 * <ul>
 *     <li>
 *         <a href="#RelativePositioning">Relative positioning</a>
 *     </li>
 *     <li>
 *         <a href="#Margins">Margins</a>
 *     </li>
 *     <li>
 *         <a href="#CenteringPositioning">Centering positioning</a>
 *     </li>
 *     <li>
 *         <a href="#VisibilityBehavior">Visibility behavior</a>
 *     </li>
 *     <li>
 *         <a href="#DimensionConstraints">Dimension constraints</a>
 *     </li>
 *     <li>
 *         <a href="#Chains">Chains</a>
 *     </li>
 *     <li>
 *         <a href="#VirtualHelpers">Virtual Helpers objects</a>
 *     </li>
 * </ul>
 * </p>
 *
 * <p>
 * Note that you cannot have a circular dependency in constraints.
 * </p>
 * <p>
 * Also see {@link ConstraintLayout.LayoutParams
 * ConstraintLayout.LayoutParams} for layout attributes
 * </p>
 *
 * <div class="special reference">
 * <h3>Developer Guide</h3>
 *
 * <h4 id="RelativePositioning"> Relative positioning </h4>
 * <p>
 *     Relative positioning is one of the basic building block of creating layouts in ConstraintLayout.
 *     Those constraints allow you to position a given widget relative to another one. You can constrain
 *     a widget on the horizontal and vertical axis:
 *     <ul>
 *         <li>Horizontal Axis: Left, Right, Start and End sides</li>
 *         <li>Vertical Axis: top, bottom sides and text baseline</li>
 *     </ul>
 *     <p>
 *     The general concept is to constrain a given side of a widget to another side of any other widget.
 *     <p>
 *     For example, in order to position button B to the right of button A (Fig. 1):
 *     <br><div align="center">
 *       <img width="300px" src="resources/images/relative-positioning.png">
 *           <br><b><i>Fig. 1 - Relative Positioning Example</i></b>
 *     </div>
 *     </p>
 *     <p>
 *         you would need to do:
 *     </p>
 *     <pre>{@code
 *         <Button android:id="@+id/buttonA" ... />
 *         <Button android:id="@+id/buttonB" ...
 *                 app:layout_constraintLeft_toRightOf="@+id/buttonA" />
 *         }
 *     </pre>
 *     This tells the system that we want the left side of button B to be constrained to the right side of button A.
 *     Such a position constraint means that the system will try to have both sides share the same location.
 *     <br><div align="center" >
 *       <img width="350px" src="resources/images/relative-positioning-constraints.png">
 *           <br><b><i>Fig. 2 - Relative Positioning Constraints</i></b>
 *     </div>
 *
 *     <p>Here is the list of available constraints (Fig. 2):</p>
 *     <ul>
 *         <li>{@code layout_constraintLeft_toLeftOf}</li>
 *         <li>{@code layout_constraintLeft_toRightOf}</li>
 *         <li>{@code layout_constraintRight_toLeftOf}</li>
 *         <li>{@code layout_constraintRight_toRightOf}</li>
 *         <li>{@code layout_constraintTop_toTopOf}</li>
 *         <li>{@code layout_constraintTop_toBottomOf}</li>
 *         <li>{@code layout_constraintBottom_toTopOf}</li>
 *         <li>{@code layout_constraintBottom_toBottomOf}</li>
 *         <li>{@code layout_constraintBaseline_toBaselineOf}</li>
 *         <li>{@code layout_constraintStart_toEndOf}</li>
 *         <li>{@code layout_constraintStart_toStartOf}</li>
 *         <li>{@code layout_constraintEnd_toStartOf}</li>
 *         <li>{@code layout_constraintEnd_toEndOf}</li>
 *     </ul>
 *     <p>
 *     They all takes a reference {@code id} to another widget, or the {@code parent} (which will reference the parent container, i.e. the ConstraintLayout):
 *     <pre>{@code
 *         <Button android:id="@+id/buttonB" ...
 *                 app:layout_constraintLeft_toLeftOf="parent" />
 *         }
 *     </pre>
 *
 *     </p>
 *
 * <h4 id="Margins"> Margins </h4>
 * <p>
 *     <div align="center" >
 *       <img width="325px" src="resources/images/relative-positioning-margin.png">
 *           <br><b><i>Fig. 3 - Relative Positioning Margins</i></b>
 *     </div>
 *      <p>If side margins are set, they will be applied to the corresponding constraints (if they exist) (Fig. 3), enforcing
 *      the margin as a space between the target and the source side. The usual layout margins attributes can be used to this effect:
 *      <ul>
 *          <li>{@code android:layout_marginStart}</li>
 *          <li>{@code android:layout_marginEnd}</li>
 *          <li>{@code android:layout_marginLeft}</li>
 *          <li>{@code android:layout_marginTop}</li>
 *          <li>{@code android:layout_marginRight}</li>
 *          <li>{@code android:layout_marginBottom}</li>
 *      </ul>
 *      <p>Note that a margin can only be positive or equals to zero, and takes a {@code Dimension}.</p>
 * <h4 id="GoneMargin"> Margins when connected to a GONE widget</h4>
 *      <p>When a position constraint target's visibility is {@code View.GONE}, you can also indicates a different
 *      margin value to be used using the following attributes:</p>
 *      <ul>
 *          <li>{@code layout_goneMarginStart}</li>
 *          <li>{@code layout_goneMarginEnd}</li>
 *          <li>{@code layout_goneMarginLeft}</li>
 *          <li>{@code layout_goneMarginTop}</li>
 *          <li>{@code layout_goneMarginRight}</li>
 *          <li>{@code layout_goneMarginBottom}</li>
 *      </ul>
 * </p>
 * </p>
 * <h4 id="CenteringPositioning"> Centering positioning and bias</h4>
 * <p>
 *     A useful aspect of {@code ConstraintLayout} is in how it deals with "impossible" constrains. For example, if
 *     we have something like:
 *     <pre>{@code
 *         <android.support.constraint.ConstraintLayout ...>
 *             <Button android:id="@+id/button" ...
 *                 app:layout_constraintLeft_toLeftOf="parent"
 *                 app:layout_constraintRight_toRightOf="parent/>
 *         </>
 *         }
 *     </pre>
 * </p>
 * <p>
 *     Unless the {@code ConstraintLayout} happens to have the exact same size as the {@code Button}, both constraints
 *     cannot be satisfied at the same time (both sides cannot be where we want them to be).
 *     <p><div align="center" >
 *       <img width="325px" src="resources/images/centering-positioning.png">
 *           <br><b><i>Fig. 4 - Centering Positioning</i></b>
 *     </div>
 *     <p>
 *     What happens in this case is that the constraints act like opposite forces
 *     pulling the widget apart equally (Fig. 4); such that the widget will end up being centered in the parent container.
 *     This will apply similarly for vertical constraints.
 * </p>
 * <h5 id="Bias">Bias</h5>
 *     <p>
 *        The default when encountering such opposite constraints is to center the widget; but you can tweak
 *        the positioning to favor one side over another using the bias attributes:
 *        <ul>
 *            <li>{@code layout_constraintHorizontal_bias}</li>
 *            <li>{@code layout_constraintVertical_bias}</li>
 *        </ul>
 *     <p><div align="center" >
 *       <img width="325px" src="resources/images/centering-positioning-bias.png">
 *           <br><b><i>Fig. 5 - Centering Positioning with Bias</i></b>
 *     </div>
 *     <p>
 *        For example the following will make the left side with a 30% bias instead of the default 50%, such that the left side will be
 *        shorter, with the widget leaning more toward the left side (Fig. 5):
 *        </p>
 *     <pre>{@code
 *         <android.support.constraint.ConstraintLayout ...>
 *             <Button android:id="@+id/button" ...
 *                 app:layout_constraintHorizontal_bias="0.3"
 *                 app:layout_constraintLeft_toLeftOf="parent"
 *                 app:layout_constraintRight_toRightOf="parent/>
 *         </>
 *         }
 *     </pre>
 *     Using bias, you can craft User Interfaces that will better adapt to screen sizes changes.
 *     </p>
 * </p>
 *
 * <h4 id="VisibilityBehavior"> Visibility behavior </h4>
 * <p>
 *     {@code ConstraintLayout} has a specific handling of widgets being marked as {@code View.GONE}.
 *     <p>{@code GONE} widgets, as usual, are not going to be displayed and are not part of the layout itself (i.e. their actual dimensions
 *      will not be changed if marked as {@code GONE}).
 *
 *     <p>But in terms of the layout computations, {@code GONE} widgets are still part of it, with an important distinction:
 *     <ul>
 *         <li> For the layout pass, their dimension will be considered as if zero (basically, they will be resolved to a point)</li>
 *         <li> If they have constraints to other widgets they will still be respected, but any margins will be as if equals to zero</li>
 *     </ul>
 *
 *     <p><div align="center" >
 *       <img width="350px" src="resources/images/visibility-behavior.png">
 *           <br><b><i>Fig. 6 - Visibility Behavior</i></b>
 *     </div>
 *     <p>This specific behavior allows to build layouts where you can temporarily mark widgets as being {@code GONE},
 *     without breaking the layout (Fig. 6), which can be particularly useful when doing simple layout animations.
 *     <p><b>Note: </b>The margin used will be the margin that B had defined when connecting to A (see Fig. 6 for an example).
 *     In some cases, this might not be the margin you want (e.g. A had a 100dp margin to the side of its container,
 *     B only a 16dp to A, marking
 *     A as gone, B will have a margin of 16dp to the container).
 *     For this reason, you can specify an alternate
 *     margin value to be used when the connection is to a widget being marked as gone (see <a href="#GoneMargin">the section above about the gone margin attributes</a>).
 * </p>
 *
 * <h4 id="DimensionConstraints"> Dimensions constraints </h4>
 * <h5>Minimum dimensions on ConstraintLayout</h5>
 * <p>
 *     You can define minimum sizes for the {@code ConstraintLayout} itself:
 *     <ul>
 *         <li>{@code android:minWidth} set the minimum width for the layout</li>
 *         <li>{@code android:minHeight} set the minimum height for the layout</li>
 *     </ul>
 *     Those minimum dimensions will be used by {@code ConstraintLayout} when its dimensions are set to {@code WRAP_CONTENT}.
 * </p>
 * <h5>Widgets dimension constraints</h5>
 * <p>
 *     The dimension of the widgets can be specified by setting the {@code android:layout_width} and
 *     {@code android:layout_height} attributes in 3 different ways:
 *     <ul>
 *         <li>Using a specific dimension (either a literal value such as {@code 123dp} or a {@code Dimension} reference)</li>
 *         <li>Using {@code WRAP_CONTENT}, which will ask the widget to compute its own size</li>
 *         <li>Using {@code 0dp}, which is the equivalent of "{@code MATCH_CONSTRAINT}"</li>
 *     </ul>
 *     <p><div align="center" >
 *       <img width="325px" src="resources/images/dimension-match-constraints.png">
 *           <br><b><i>Fig. 7 - Dimension Constraints</i></b>
 *     </div>
 *     The first two works in a similar fashion as other layouts. The last one will resize the widget in such a way as
 *     matching the constraints that are set (see Fig. 7, (a) is wrap_content, (b) is 0dp). If margins are set, they will be taken in account
 *     in the computation (Fig. 7, (c) with 0dp).
 *     <p>
 *         <b>Important: </b> {@code MATCH_PARENT} is not recommended for widgets contained in a {@code ConstraintLayout}. Similar behavior can
 *         be defined by using {@code MATCH_CONSTRAINT} with the corresponding left/right or top/bottom constraints being set to {@code "parent"}.
 *     </p>
 * </p>
 * <h5>Ratio</h5>
 * <p>
 *     You can also define one dimension of a widget as a ratio of the other one. In order to do that, you
 *     need to have at least one constrained dimension be set to {@code 0dp} (i.e., {@code MATCH_CONSTRAINT}), and set the
 *     attribute {@code layout_constraintDimentionRatio} to a given ratio.
 *     For example:
 *     <pre>
 *         {@code
 *           <Button android:layout_width="wrap_content"
 *                   android:layout_height="0dp"
 *                   app:layout_constraintDimensionRatio="1:1" />
 *         }
 *     </pre>
 *     will set the height of the button to be the same as its width.
 * </p>
 * <p> The ratio can be expressed either as:
 * <ul>
 *     <li>a float value, representing a ratio between width and height</li>
 *     <li>a ratio in the form "width:height"</li>
 * </ul>
 * </p>
 * <p>
 *     You can also use ratio if both dimensions are set to {@code MATCH_CONSTRAINT} (0dp). In this case the system sets the
 *     largest dimensions the satisfies all constraints and maintains the aspect ratio specified. To constrain one specific side
 *     based on the dimensions of another. You can pre append {@code W,}" or {@code H,} to constrain the width or height
 *     respectively.
 *     For example,
 *     If one dimension is constrained by two targets (e.g. width is 0dp and centered on parent) you can indicate which
 *     side should be constrained, by adding the letter {@code W} (for constraining the width) or {@code H}
 *     (for constraining the height) in front of the ratio, separated
 *     by a comma:
 *     <pre>
 *         {@code
 *           <Button android:layout_width="0dp"
 *                   android:layout_height="0dp"
 *                   app:layout_constraintDimensionRatio="H,16:9"
 *                   app:layout_constraintBottom_toBottomOf="parent"
 *                   app:layout_constraintTop_toTopOf="parent"/>
 *         }
 *     </pre>
 *     will set the height of the button following a 16:9 ratio, while the width of the button will match the constraints
 *     to parent.
 *
 * </p>
 *
 * <h4 id="Chains">Chains</h4>
 * <p>Chains provide group-like behavior in a single axis (horizontally or vertically). The other axis can be constrained independently.</p>
 * <h5>Creating a chain</h5>
 * <p>
 *     A set of widgets are considered a chain if they a linked together via a bi-directional connection (see Fig. 8, showing a minimal chain, with two widgets).
 * </p>
 *     <p><div align="center" >
 *       <img width="325px" src="resources/images/chains.png">
 *           <br><b><i>Fig. 8 - Chain</i></b>
 *     </div>
 * <p>
 * <h5>Chain heads</h5>
 * <p>
 *     Chains are controlled by attributes set on the first element of the chain (the "head" of the chain):
 * </p>
 *     <p><div align="center" >
 *       <img width="400px" src="resources/images/chains-head.png">
 *           <br><b><i>Fig. 9 - Chain Head</i></b>
 *     </div>
 *     <p>The head is the left-most widget for horizontal chains, and the top-most widget for vertical chains.</p>
 * <h5>Margins in chains</h5>
 * <p>If margins are specified on connections, they will be taken in account. In the case of spread chains, margins will be deducted from the allocated space.</p>
 * <h5>Chain Style</h5>
 * <p>When setting the attribute {@code layout_constraintHorizontal_chainStyle} or {@code layout_constraintVertical_chainStyle} on the first element of a chain,
 * the behavior of the chain will change according to the specified style (default is {@code CHAIN_SPREAD}).
 * <ul>
 *     <li>{@code CHAIN_SPREAD} -- the elements will be spread out (default style)</li>
 *     <li>Weighted chain -- in {@code CHAIN_SPREAD} mode, if some widgets are set to {@code MATCH_CONSTRAINT}, they will split the available space</li>
 *     <li>{@code CHAIN_SPREAD_INSIDE} -- similar, but the endpoints of the chain will not be spread out</li>
 *     <li>{@code CHAIN_PACKED} -- the elements of the chain will be packed together. The horizontal or vertical
 *          bias attribute of the child will then affect the positioning of the packed elements</li>
 * </ul>
 *     <p><div align="center" >
 *       <img width="600px" src="resources/images/chains-styles.png">
 *           <br><b><i>Fig. 10 - Chains Styles</i></b>
 *     </div>
 * </p>
 * <h5>Weighted chains</h5>
 * <p>The default behavior of a chain is to spread the elements equally in the available space. If one or more elements are using {@code MATCH_CONSTRAINT}, they
 * will use the available empty space (equally divided among themselves). The attribute {@code layout_constraintHorizontal_weight} and {@code layout_constraintVertical_weight}
 * will control how the space will be distributed among the elements using {@code MATCH_CONSTRAINT}. For exemple, on a chain containing two elements using {@code MATCH_CONSTRAINT},
 * with the first element using a weight of 2 and the second a weight of 1, the space occupied by the first element will be twice that of the second element.</p>
 *
 * <h4 id="VirtualHelpers"> Virtual Helper objects </h4>
 * <p>In addition to the intrinsic capabilities detailed previously, you can also use special helper objects
 * in {@code ConstraintLayout} to help you with your layout. Currently, the {@code Guideline}{@see Guideline} object allows you to create
 * Horizontal and Vertical guidelines which are positioned relative to the {@code ConstraintLayout} container. Widgets can
 * then be positioned by constraining them to such guidelines.</p>
 * </div>
 */
public class ConstraintLayout extends ViewGroup {
    // For now, disallow embedded (single-layer resolution) situations.
    // While it works, the constraints of the layout have the same importance as any other
    // constraint of the overall layout, which can cause issues. Let's revisit this
    // after implementing priorities/hierarchy of constraints.
    static final boolean ALLOWS_EMBEDDED = false;
    /** @hide */
    public static final String VERSION="ConstraintLayout-1.1.0-beta2";
    private static final String TAG = "ConstraintLayout";
    private static final boolean SIMPLE_LAYOUT = true;
    private static final boolean USE_CONSTRAINTS_HELPER = true;
    SparseArray<View> mChildrenByIds = new SparseArray<>();
    // This array keep a list of helpers objects if they are present
    private ArrayList<ConstraintHelper> mConstraintHelpers = new ArrayList<>(4);
    // This array will keep a list of the widget with one or two dimensions that are
    // set to MATCH_CONSTRAINT (i.e. they depend on the solver result, not from
    // WRAP_CONTENT or a fixed dimension)
    private final ArrayList<ConstraintWidget> mVariableDimensionsWidgets = new ArrayList<>(100);
    ConstraintWidgetContainer mLayoutWidget = new ConstraintWidgetContainer();
    private int mMinWidth = 0;
    private int mMinHeight = 0;
    private int mMaxWidth = Integer.MAX_VALUE;
    private int mMaxHeight = Integer.MAX_VALUE;
    private boolean mDirtyHierarchy = true;
    private int mOptimizationLevel = 2; // all
    private ConstraintSet mConstraintSet = null;
    private String mTitle;
    private int mConstraintSetId = -1;
    private HashMap<String, Integer> mDesignIds = new HashMap<>();
    /**
     * @hide
     */
    public final static int DESIGN_INFO_ID = 0;
    /**
     * @hide
     */
    public void setDesignInformation(int type, Object value1, Object value2) {
        if (type == DESIGN_INFO_ID && value1 instanceof String && value2 instanceof Integer) {
            if (mDesignIds == null) {
                mDesignIds = new HashMap<>();
            }
            String name = (String) value1;
            int index = name.indexOf("/");
            if (index != -1) {
                name = name.substring(index + 1);
            }
            int id = (Integer) value2;
            mDesignIds.put(name, id);
        }
    }
    /**
     * @hide
     */
    public Object getDesignInformation(int type, Object value) {
        if (type == DESIGN_INFO_ID && value instanceof String) {
            String name = (String) value;
            if (mDesignIds != null && mDesignIds.containsKey(name)) {
                return mDesignIds.get(name);
            }
        }
        return null;
    }
    public ConstraintLayout(Context context) {
        super(context);
        init(null);
    }
    public ConstraintLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(attrs);
    }
    public ConstraintLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(attrs);
    }
    @Override
    public void setId(int id) {
        mChildrenByIds.remove(getId());
        super.setId(id);
        mChildrenByIds.put(getId(), this);
    }
    public void setTitle(String title) {
        mTitle = title;
    }
    public String getTitle() {
        return mTitle;
    }
    private void init(AttributeSet attrs) {
        mLayoutWidget.setCompanionWidget(this);
        mChildrenByIds.put(getId(), this);
        mConstraintSet = null;
        if (attrs != null) {
            TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.ConstraintLayout_Layout);
            final int N = a.getIndexCount();
            for (int i = 0; i < N; i++) {
                int attr = a.getIndex(i);
                if (attr == R.styleable.ConstraintLayout_Layout_android_minWidth) {
                    mMinWidth = a.getDimensionPixelOffset(attr, mMinWidth);
                } else if (attr == R.styleable.ConstraintLayout_Layout_android_minHeight) {
                    mMinHeight = a.getDimensionPixelOffset(attr, mMinHeight);
                } else if (attr == R.styleable.ConstraintLayout_Layout_android_maxWidth) {
                    mMaxWidth = a.getDimensionPixelOffset(attr, mMaxWidth);
                } else if (attr == R.styleable.ConstraintLayout_Layout_android_maxHeight) {
                    mMaxHeight = a.getDimensionPixelOffset(attr, mMaxHeight);
                } else if (attr == R.styleable.ConstraintLayout_Layout_layout_optimizationLevel) {
                    mOptimizationLevel = a.getInt(attr, mOptimizationLevel);
                } else if (attr == R.styleable.ConstraintLayout_Layout_title) {
                    mTitle = a.getString(attr);
                } else if (attr == R.styleable.ConstraintLayout_Layout_constraintSet) {
                    int id = a.getResourceId(attr, 0);
                    try {
                        mConstraintSet = new ConstraintSet();
                        mConstraintSet.load(getContext(), id);
                    } catch (Resources.NotFoundException e) {
                        mConstraintSet = null;
                    }
                    mConstraintSetId = id;
                }
            }
            a.recycle();
        }
        mLayoutWidget.setOptimizationLevel(mOptimizationLevel);
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void addView(View child, int index, ViewGroup.LayoutParams params) {
        super.addView(child, index, params);
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
            onViewAdded(child);
        }
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void removeView(View view) {
        super.removeView(view);
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
            onViewRemoved(view);
        }
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void onViewAdded(View view) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
            super.onViewAdded(view);
        }
        ConstraintWidget widget = getViewWidget(view);
        if (view instanceof android.support.constraint.Guideline) {
            if (!(widget instanceof Guideline)) {
                LayoutParams layoutParams = (LayoutParams) view.getLayoutParams();
                layoutParams.widget = new Guideline();
                layoutParams.isGuideline = true;
                ((Guideline) layoutParams.widget).setOrientation(layoutParams.orientation);
            }
        }
        if (view instanceof ConstraintHelper) {
            ConstraintHelper helper = (ConstraintHelper) view;
            helper.validateParams();
            LayoutParams layoutParams = (LayoutParams) view.getLayoutParams();
            layoutParams.isHelper = true;
            if (!mConstraintHelpers.contains(helper)) {
                mConstraintHelpers.add(helper);
            }
        }
        mChildrenByIds.put(view.getId(), view);
        mDirtyHierarchy = true;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void onViewRemoved(View view) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
            super.onViewRemoved(view);
        }
        mChildrenByIds.remove(view.getId());
        mLayoutWidget.remove(getViewWidget(view));
        mConstraintHelpers.remove(view);
        mDirtyHierarchy = true;
    }
    /**
     * Set the min width for this view
     *
     * @param value
     */
    public void setMinWidth(int value) {
        if (value == mMinWidth) {
            return;
        }
        mMinWidth = value;
        requestLayout();
    }
    /**
     * Set the min height for this view
     *
     * @param value
     */
    public void setMinHeight(int value) {
        if (value == mMinHeight) {
            return;
        }
        mMinHeight = value;
        requestLayout();
    }
    /*
     * The minimum width of this view.
     *
     * @return The minimum width of this view
     *
     * @see #setMinWidth(int)
     */
    public int getMinWidth() {
        return mMinWidth;
    }
    /**
     * The minimum height of this view.
     *
     * @return The minimum height of this view
     *
     * @see #setMinHeight(int)
     */
    public int getMinHeight() {
        return mMinHeight;
    }
    /**
     * Set the max width for this view
     *
     * @param value
     */
    public void setMaxWidth(int value) {
        if (value == mMaxWidth) {
            return;
        }
        mMaxWidth = value;
        requestLayout();
    }
    /**
     * Set the max height for this view
     *
     * @param value
     */
    public void setMaxHeight(int value) {
        if (value == mMaxHeight) {
            return;
        }
        mMaxHeight = value;
        requestLayout();
    }
    /*
     * The maximum width of this view.
     *
     * @return The maximum width of this view
     *
     * @see #setMaxWidth(int)
     */
    public int getMaxWidth() {
        return mMaxWidth;
    }
    /**
     * The maximum height of this view.
     *
     * @return The maximum height of this view
     *
     * @see #setMaxHeight(int)
     */
    public int getMaxHeight() {
        return mMaxHeight;
    }
    private void updateHierarchy() {
        final int count = getChildCount();
        boolean recompute = false;
        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (child.isLayoutRequested()) {
                recompute = true;
                break;
            }
        }
        if (recompute) {
            mVariableDimensionsWidgets.clear();
            setChildrenConstraints();
        }
    }
    private void setChildrenConstraints() {
        final boolean isInEditMode = isInEditMode();
        final int count = getChildCount();
        if (isInEditMode) {
            // In design mode, let's make sure we keep track of the ids; in Studio, a build step
            // might not have been done yet, so asking the system for ids can break. So to be safe,
            // we save the current ids, which helpers can ask for.
            for (int i = 0; i < count; i++) {
                final View view = getChildAt(i);
                try {
                    String IdAsString = getResources().getResourceName(view.getId());
                    setDesignInformation(DESIGN_INFO_ID, IdAsString, view.getId());
                } catch (Resources.NotFoundException e) {
                    // nothing
                }
            }
        }
        if (USE_CONSTRAINTS_HELPER && mConstraintSetId != -1) {
            for (int i = 0; i < count; i++) {
                final View child = getChildAt(i);
                if (child.getId() == mConstraintSetId && child instanceof Constraints) {
                    mConstraintSet = ((Constraints) child).getConstraintSet();
                }
            }
        }
        if (mConstraintSet != null) {
            mConstraintSet.applyToInternal(this);
        }
        mLayoutWidget.removeAllChildren();
        final int helperCount = mConstraintHelpers.size();
        if (helperCount > 0) {
            for (int i = 0; i < helperCount; i++) {
                ConstraintHelper helper = mConstraintHelpers.get(i);
                helper.updatePreLayout(this);
            }
        }
        // TODO refactor into an updatePreLayout interface
        for (int i = 0; i < count; i++) {
            View child = getChildAt(i);
            if (child instanceof Placeholder) {
                ((Placeholder) child).updatePreLayout(this);
            }
        }
        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            ConstraintWidget widget = getViewWidget(child);
            if (widget == null) {
                continue;
            }
            final LayoutParams layoutParams = (LayoutParams) child.getLayoutParams();
            layoutParams.validate();
            if (layoutParams.helped) {
                layoutParams.helped = false;
            } else {
                widget.reset();
            }
            widget.setVisibility(child.getVisibility());
            if (layoutParams.isInPlaceholder) {
                widget.setVisibility(View.GONE);
            }
            widget.setCompanionWidget(child);
            mLayoutWidget.add(widget);
            if (!layoutParams.verticalDimensionFixed || !layoutParams.horizontalDimensionFixed) {
                mVariableDimensionsWidgets.add(widget);
            }
            if (layoutParams.isGuideline) {
                Guideline guideline = (Guideline) widget;
                if (layoutParams.guideBegin != -1) {
                    guideline.setGuideBegin(layoutParams.guideBegin);
                }
                if (layoutParams.guideEnd != -1) {
                    guideline.setGuideEnd(layoutParams.guideEnd);
                }
                if (layoutParams.guidePercent != -1) {
                    guideline.setGuidePercent(layoutParams.guidePercent);
                }
            } else if ((layoutParams.resolvedLeftToLeft != UNSET)
                    || (layoutParams.resolvedLeftToRight != UNSET)
                    || (layoutParams.resolvedRightToLeft != UNSET)
                    || (layoutParams.resolvedRightToRight != UNSET)
                    || (layoutParams.topToTop != UNSET)
                    || (layoutParams.topToBottom != UNSET)
                    || (layoutParams.bottomToTop != UNSET)
                    || (layoutParams.bottomToBottom != UNSET)
                    || (layoutParams.baselineToBaseline != UNSET)
                    || (layoutParams.editorAbsoluteX != UNSET)
                    || (layoutParams.editorAbsoluteY != UNSET)
                    || (layoutParams.width == MATCH_PARENT)
                    || (layoutParams.height == MATCH_PARENT)) {
                // Get the left/right constraints resolved for RTL
                int resolvedLeftToLeft = layoutParams.resolvedLeftToLeft;
                int resolvedLeftToRight = layoutParams.resolvedLeftToRight;
                int resolvedRightToLeft = layoutParams.resolvedRightToLeft;
                int resolvedRightToRight = layoutParams.resolvedRightToRight;
                int resolveGoneLeftMargin = layoutParams.resolveGoneLeftMargin;
                int resolveGoneRightMargin = layoutParams.resolveGoneRightMargin;
                float resolvedHorizontalBias = layoutParams.resolvedHorizontalBias;
                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
                    // Pre JB MR1, left/right should take precedence, unless they are
                    // not defined and somehow a corresponding start/end constraint exists
                    resolvedLeftToLeft = layoutParams.leftToLeft;
                    resolvedLeftToRight = layoutParams.leftToRight;
                    resolvedRightToLeft = layoutParams.rightToLeft;
                    resolvedRightToRight = layoutParams.rightToRight;
                    resolveGoneLeftMargin = layoutParams.goneLeftMargin;
                    resolveGoneRightMargin = layoutParams.goneRightMargin;
                    resolvedHorizontalBias = layoutParams.horizontalBias;
                    if (resolvedLeftToLeft == UNSET && resolvedLeftToRight == UNSET) {
                        if (layoutParams.startToStart != UNSET) {
                            resolvedLeftToLeft = layoutParams.startToStart;
                        } else if (layoutParams.startToEnd != UNSET) {
                            resolvedLeftToRight = layoutParams.startToEnd;
                        }
                    }
                    if (resolvedRightToLeft == UNSET && resolvedRightToRight == UNSET) {
                        if (layoutParams.endToStart != UNSET) {
                            resolvedRightToLeft = layoutParams.endToStart;
                        } else if (layoutParams.endToEnd != UNSET) {
                            resolvedRightToRight = layoutParams.endToEnd;
                        }
                    }
                }
                // Left constraint
                if (resolvedLeftToLeft != UNSET) {
                    ConstraintWidget target = getTargetWidget(resolvedLeftToLeft);
                    if (target != null) {
                        widget.immediateConnect(ConstraintAnchor.Type.LEFT, target,
                                ConstraintAnchor.Type.LEFT, layoutParams.leftMargin,
                                resolveGoneLeftMargin);
                    }
                } else if (resolvedLeftToRight != UNSET) {
                    ConstraintWidget target = getTargetWidget(resolvedLeftToRight);
                    if (target != null) {
                        widget.immediateConnect(ConstraintAnchor.Type.LEFT, target,
                                ConstraintAnchor.Type.RIGHT, layoutParams.leftMargin,
                                resolveGoneLeftMargin);
                    }
                }
                // Right constraint
                if (resolvedRightToLeft != UNSET) {
                    ConstraintWidget target = getTargetWidget(resolvedRightToLeft);
                    if (target != null) {
                        widget.immediateConnect(ConstraintAnchor.Type.RIGHT, target,
                                ConstraintAnchor.Type.LEFT, layoutParams.rightMargin,
                                resolveGoneRightMargin);
                    }
                } else if (resolvedRightToRight != UNSET) {
                    ConstraintWidget target = getTargetWidget(resolvedRightToRight);
                    if (target != null) {
                        widget.immediateConnect(ConstraintAnchor.Type.RIGHT, target,
                                ConstraintAnchor.Type.RIGHT, layoutParams.rightMargin,
                                resolveGoneRightMargin);
                    }
                }
                // Top constraint
                if (layoutParams.topToTop != UNSET) {
                    ConstraintWidget target = getTargetWidget(layoutParams.topToTop);
                    if (target != null) {
                        widget.immediateConnect(ConstraintAnchor.Type.TOP, target,
                                ConstraintAnchor.Type.TOP, layoutParams.topMargin,
                                layoutParams.goneTopMargin);
                    }
                } else if (layoutParams.topToBottom != UNSET) {
                    ConstraintWidget target = getTargetWidget(layoutParams.topToBottom);
                    if (target != null) {
                        widget.immediateConnect(ConstraintAnchor.Type.TOP, target,
                                ConstraintAnchor.Type.BOTTOM, layoutParams.topMargin,
                                layoutParams.goneTopMargin);
                    }
                }
                // Bottom constraint
                if (layoutParams.bottomToTop != UNSET) {
                    ConstraintWidget target = getTargetWidget(layoutParams.bottomToTop);
                    if (target != null) {
                        widget.immediateConnect(ConstraintAnchor.Type.BOTTOM, target,
                                ConstraintAnchor.Type.TOP, layoutParams.bottomMargin,
                                layoutParams.goneBottomMargin);
                    }
                } else if (layoutParams.bottomToBottom != UNSET) {
                    ConstraintWidget target = getTargetWidget(layoutParams.bottomToBottom);
                    if (target != null) {
                        widget.immediateConnect(ConstraintAnchor.Type.BOTTOM, target,
                                ConstraintAnchor.Type.BOTTOM, layoutParams.bottomMargin,
                                layoutParams.goneBottomMargin);
                    }
                }
                // Baseline constraint
                if (layoutParams.baselineToBaseline != UNSET) {
                    View view = mChildrenByIds.get(layoutParams.baselineToBaseline);
                    ConstraintWidget target = getTargetWidget(layoutParams.baselineToBaseline);
                    if (target != null && view != null && view.getLayoutParams() instanceof LayoutParams) {
                        LayoutParams targetParams = (LayoutParams) view.getLayoutParams();
                        layoutParams.needsBaseline = true;
                        targetParams.needsBaseline = true;
                        ConstraintAnchor baseline = widget.getAnchor(ConstraintAnchor.Type.BASELINE);
                        ConstraintAnchor targetBaseline =
                                target.getAnchor(ConstraintAnchor.Type.BASELINE);
                        baseline.connect(targetBaseline, 0, -1, ConstraintAnchor.Strength.STRONG,
                                ConstraintAnchor.USER_CREATOR, true);
                        widget.getAnchor(ConstraintAnchor.Type.TOP).reset();
                        widget.getAnchor(ConstraintAnchor.Type.BOTTOM).reset();
                    }
                }
                if (resolvedHorizontalBias >= 0 && resolvedHorizontalBias != 0.5f) {
                    widget.setHorizontalBiasPercent(resolvedHorizontalBias);
                }
                if (layoutParams.verticalBias >= 0 && layoutParams.verticalBias != 0.5f) {
                    widget.setVerticalBiasPercent(layoutParams.verticalBias);
                }
                if (isInEditMode && ((layoutParams.editorAbsoluteX != UNSET)
                        || (layoutParams.editorAbsoluteY != UNSET))) {
                    widget.setOrigin(layoutParams.editorAbsoluteX, layoutParams.editorAbsoluteY);
                }
                // FIXME: need to agree on the correct magic value for this rather than simply using zero.
                if (!layoutParams.horizontalDimensionFixed) {
                    if (layoutParams.width == LayoutParams.MATCH_PARENT) {
                        widget.setHorizontalDimensionBehaviour(ConstraintWidget.DimensionBehaviour.MATCH_PARENT);
                        widget.getAnchor(ConstraintAnchor.Type.LEFT).mMargin = layoutParams.leftMargin;
                        widget.getAnchor(ConstraintAnchor.Type.RIGHT).mMargin = layoutParams.rightMargin;
                    } else {
                        widget.setHorizontalDimensionBehaviour(ConstraintWidget.DimensionBehaviour.MATCH_CONSTRAINT);
                        widget.setWidth(0);
                    }
                } else {
                    widget.setHorizontalDimensionBehaviour(ConstraintWidget.DimensionBehaviour.FIXED);
                    widget.setWidth(layoutParams.width);
                }
                if (!layoutParams.verticalDimensionFixed) {
                    if (layoutParams.height == LayoutParams.MATCH_PARENT) {
                        widget.setVerticalDimensionBehaviour(ConstraintWidget.DimensionBehaviour.MATCH_PARENT);
                        widget.getAnchor(ConstraintAnchor.Type.TOP).mMargin = layoutParams.topMargin;
                        widget.getAnchor(ConstraintAnchor.Type.BOTTOM).mMargin = layoutParams.bottomMargin;
                    } else {
                        widget.setVerticalDimensionBehaviour(ConstraintWidget.DimensionBehaviour.MATCH_CONSTRAINT);
                        widget.setHeight(0);
                    }
                } else {
                    widget.setVerticalDimensionBehaviour(ConstraintWidget.DimensionBehaviour.FIXED);
                    widget.setHeight(layoutParams.height);
                }
                if (layoutParams.dimensionRatio != null) {
                    widget.setDimensionRatio(layoutParams.dimensionRatio);
                }
                widget.setHorizontalWeight(layoutParams.horizontalWeight);
                widget.setVerticalWeight(layoutParams.verticalWeight);
                widget.setHorizontalChainStyle(layoutParams.horizontalChainStyle);
                widget.setVerticalChainStyle(layoutParams.verticalChainStyle);
                widget.setHorizontalMatchStyle(layoutParams.matchConstraintDefaultWidth,
                        layoutParams.matchConstraintMinWidth, layoutParams.matchConstraintMaxWidth,
                        layoutParams.matchConstraintPercentWidth);
                widget.setVerticalMatchStyle(layoutParams.matchConstraintDefaultHeight,
                        layoutParams.matchConstraintMinHeight, layoutParams.matchConstraintMaxHeight,
                        layoutParams.matchConstraintPercentHeight);
            }
        }
    }
    private final ConstraintWidget getTargetWidget(int id) {
        if (id == LayoutParams.PARENT_ID) {
            return mLayoutWidget;
        } else {
            View view = mChildrenByIds.get(id);
            if (view == this) {
                return mLayoutWidget;
            }
            return view == null ? null : ((LayoutParams) view.getLayoutParams()).widget;
        }
    }
    public final ConstraintWidget getViewWidget(View view) {
        if (view == this) {
            return mLayoutWidget;
        }
        return view == null ? null : ((LayoutParams) view.getLayoutParams()).widget;
    }
    private void internalMeasureChildren(int parentWidthSpec, int parentHeightSpec) {
        int heightPadding = getPaddingTop() + getPaddingBottom();
        int widthPadding = getPaddingLeft() + getPaddingRight();
        final int widgetsCount = getChildCount();
        for (int i = 0; i < widgetsCount; i++) {
            final View child = getChildAt(i);
            if (child.getVisibility() == GONE) {
                continue;
            }
            LayoutParams params = (LayoutParams) child.getLayoutParams();
            ConstraintWidget widget = params.widget;
            if (params.isGuideline || params.isHelper) {
                continue;
            }
            int width = params.width;
            int height = params.height;
            // Don't need to measure widgets that are MATCH_CONSTRAINT on both dimensions,
            // unless they are marked as MATCH_CONSTRAINT_WRAP
            boolean doMeasure =
                    (params.horizontalDimensionFixed
                    || params.verticalDimensionFixed)
                    || (!params.horizontalDimensionFixed
                            && (params.matchConstraintDefaultWidth == MATCH_CONSTRAINT_WRAP)
                                || params.width == MATCH_PARENT)
                    || (!params.verticalDimensionFixed
                            && (params.matchConstraintDefaultHeight == MATCH_CONSTRAINT_WRAP
                                || params.height == MATCH_PARENT));
            boolean didWrapMeasureWidth = false;
            boolean didWrapMeasureHeight = false;
            if (doMeasure) {
                final int childWidthMeasureSpec;
                final int childHeightMeasureSpec;
                if (width == MATCH_CONSTRAINT || width == MATCH_PARENT) {
                    childWidthMeasureSpec = getChildMeasureSpec(parentWidthSpec,
                            widthPadding, LayoutParams.WRAP_CONTENT);
                    didWrapMeasureWidth = true;
                } else {
                    childWidthMeasureSpec = getChildMeasureSpec(parentWidthSpec,
                            widthPadding, width);
                }
                if (height == MATCH_CONSTRAINT || height == MATCH_PARENT) {
                    childHeightMeasureSpec = getChildMeasureSpec(parentHeightSpec,
                            heightPadding, LayoutParams.WRAP_CONTENT);
                    didWrapMeasureHeight = true;
                } else {
                    childHeightMeasureSpec = getChildMeasureSpec(parentHeightSpec,
                            heightPadding, height);
                }
                child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
                widget.setWidthWrapContent(width == WRAP_CONTENT);
                widget.setHeightWrapContent(height == WRAP_CONTENT);
                width = child.getMeasuredWidth();
                height = child.getMeasuredHeight();
            }
            widget.setWidth(width);
            widget.setHeight(height);
            if (didWrapMeasureWidth) {
                widget.setWrapWidth(width);
            }
            if (didWrapMeasureHeight) {
                widget.setWrapHeight(height);
            }
            if (params.needsBaseline) {
                int baseline = child.getBaseline();
                if (baseline != -1) {
                    widget.setBaselineDistance(baseline);
                }
            }
        }
        for (int i = 0; i < widgetsCount; i++) {
            final View child = getChildAt(i);
            if (child instanceof Placeholder) {
                ((Placeholder) child).updatePostMeasure(this);
            }
        }
        // TODO refactor into an updatePostMeasure interface
        final int helperCount = mConstraintHelpers.size();
        if (helperCount > 0) {
            for (int i = 0; i < helperCount; i++) {
                ConstraintHelper helper = mConstraintHelpers.get(i);
                helper.updatePostMeasure(this);
            }
        }
    }
    /**
     * {@inheritDoc}
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int paddingLeft = getPaddingLeft();
        int paddingTop = getPaddingTop();
        mLayoutWidget.setX(paddingLeft);
        mLayoutWidget.setY(paddingTop);
        setSelfDimensionBehaviour(widthMeasureSpec, heightMeasureSpec);
        int startingWidth = mLayoutWidget.getWidth();
        int startingHeight = mLayoutWidget.getHeight();
        if (mDirtyHierarchy) {
            mDirtyHierarchy = false;
            updateHierarchy();
        }
        internalMeasureChildren(widthMeasureSpec, heightMeasureSpec);
        //noinspection PointlessBooleanExpression
        if (ALLOWS_EMBEDDED && mLayoutWidget.getParent() != null) {
            setVisibility(INVISIBLE);
            return;
        }
        // let's solve the linear system.
        if (getChildCount() > 0) {
            solveLinearSystem(); // first pass
        }
        int childState = 0;
        // let's update the size dependent widgets if any...
        final int sizeDependentWidgetsCount = mVariableDimensionsWidgets.size();
        int heightPadding = paddingTop + getPaddingBottom();
        int widthPadding = paddingLeft + getPaddingRight();
        if (sizeDependentWidgetsCount > 0) {
            boolean needSolverPass = false;
            boolean containerWrapWidth = mLayoutWidget.getHorizontalDimensionBehaviour()
                    == ConstraintWidget.DimensionBehaviour.WRAP_CONTENT;
            boolean containerWrapHeight = mLayoutWidget.getVerticalDimensionBehaviour()
                    == ConstraintWidget.DimensionBehaviour.WRAP_CONTENT;
            int minWidth = Math.max(mLayoutWidget.getWidth(), mMinWidth);
            int minHeight = Math.max(mLayoutWidget.getHeight(), mMinHeight);
            for (int i = 0; i < sizeDependentWidgetsCount; i++) {
                ConstraintWidget widget = mVariableDimensionsWidgets.get(i);
                View child = (View) widget.getCompanionWidget();
                if (child == null) {
                    continue;
                }
                ConstraintLayout.LayoutParams params = (LayoutParams) child.getLayoutParams();
                if (params.isHelper || params.isGuideline) {
                    continue;
                }
                if (child.getVisibility() == View.GONE) {
                    continue;
                }
                int widthSpec = 0;
                int heightSpec = 0;
                if (params.width == WRAP_CONTENT) {
                    widthSpec = getChildMeasureSpec(widthMeasureSpec, widthPadding, params.width);
                } else {
                    widthSpec = MeasureSpec.makeMeasureSpec(widget.getWidth(), MeasureSpec.EXACTLY);
                }
                if (params.height == WRAP_CONTENT) {
                    heightSpec = getChildMeasureSpec(heightMeasureSpec, heightPadding, params.height);
                } else {
                    heightSpec = MeasureSpec.makeMeasureSpec(widget.getHeight(), MeasureSpec.EXACTLY);
                }
                // we need to re-measure the child...
                child.measure(widthSpec, heightSpec);
                int measuredWidth = child.getMeasuredWidth();
                int measuredHeight = child.getMeasuredHeight();
                if (measuredWidth != widget.getWidth()) {
                    widget.setWidth(measuredWidth);
                    if (containerWrapWidth && widget.getRight() > minWidth) {
                        int w = widget.getRight()
                                + widget.getAnchor(ConstraintAnchor.Type.RIGHT).getMargin();
                        minWidth = Math.max(minWidth, w);
                    }
                    needSolverPass = true;
                }
                if (measuredHeight != widget.getHeight()) {
                    widget.setHeight(measuredHeight);
                    if (containerWrapHeight && widget.getBottom() > minHeight) {
                        int h = widget.getBottom()
                                + widget.getAnchor(ConstraintAnchor.Type.BOTTOM).getMargin();
                        minHeight = Math.max(minHeight, h);
                    }
                    needSolverPass = true;
                }
                if (params.needsBaseline) {
                    int baseline = child.getBaseline();
                    if (baseline != -1 && baseline != widget.getBaselineDistance()) {
                        widget.setBaselineDistance(baseline);
                        needSolverPass = true;
                    }
                }
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
                    childState = combineMeasuredStates(childState, child.getMeasuredState());
                }
            }
            if (needSolverPass) {
                mLayoutWidget.setWidth(startingWidth);
                mLayoutWidget.setHeight(startingHeight);
                solveLinearSystem();
                needSolverPass = false;
                if (mLayoutWidget.getWidth() < minWidth) {
                    mLayoutWidget.setWidth(minWidth);
                    needSolverPass = true;
                }
                if (mLayoutWidget.getHeight() < minHeight) {
                    mLayoutWidget.setHeight(minHeight);
                    needSolverPass = true;
                }
                if (needSolverPass) {
                    solveLinearSystem();
                }
            }
            for (int i = 0; i < sizeDependentWidgetsCount; i++) {
                ConstraintWidget widget = mVariableDimensionsWidgets.get(i);
                View child = (View) widget.getCompanionWidget();
                if (child == null) {
                    continue;
                }
                if (child.getWidth() != widget.getWidth() || child.getHeight() != widget.getHeight()) {
                    int widthSpec = MeasureSpec.makeMeasureSpec(widget.getWidth(), MeasureSpec.EXACTLY);
                    int heightSpec = MeasureSpec.makeMeasureSpec(widget.getHeight(), MeasureSpec.EXACTLY);
                    child.measure(widthSpec, heightSpec);
                }
            }
        }
        int androidLayoutWidth = mLayoutWidget.getWidth() + widthPadding;
        int androidLayoutHeight = mLayoutWidget.getHeight() + heightPadding;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            int resolvedWidthSize = resolveSizeAndState(androidLayoutWidth, widthMeasureSpec, childState);
            int resolvedHeightSize = resolveSizeAndState(androidLayoutHeight, heightMeasureSpec,
                    childState << MEASURED_HEIGHT_STATE_SHIFT);
            resolvedWidthSize = Math.min(mMaxWidth, resolvedWidthSize);
            resolvedHeightSize = Math.min(mMaxHeight, resolvedHeightSize);
            resolvedWidthSize &= MEASURED_SIZE_MASK;
            resolvedHeightSize &= MEASURED_SIZE_MASK;
            if (mLayoutWidget.isWidthMeasuredTooSmall()) {
                resolvedWidthSize |= MEASURED_STATE_TOO_SMALL;
            }
            if (mLayoutWidget.isHeightMeasuredTooSmall()) {
                resolvedHeightSize |= MEASURED_STATE_TOO_SMALL;
            }
            setMeasuredDimension(resolvedWidthSize, resolvedHeightSize);
        } else {
            setMeasuredDimension(androidLayoutWidth, androidLayoutHeight);
        }
    }
    private void setSelfDimensionBehaviour(int widthMeasureSpec, int heightMeasureSpec) {
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int heightPadding = getPaddingTop() + getPaddingBottom();
        int widthPadding = getPaddingLeft() + getPaddingRight();
        ConstraintWidget.DimensionBehaviour widthBehaviour = ConstraintWidget.DimensionBehaviour.FIXED;
        ConstraintWidget.DimensionBehaviour heightBehaviour = ConstraintWidget.DimensionBehaviour.FIXED;
        int desiredWidth = 0;
        int desiredHeight = 0;
        ViewGroup.LayoutParams params = getLayoutParams();
        switch (widthMode) {
            case MeasureSpec.AT_MOST: {
                widthBehaviour = ConstraintWidget.DimensionBehaviour.WRAP_CONTENT;
                desiredWidth = widthSize;
            }
            break;
            case MeasureSpec.UNSPECIFIED: {
                widthBehaviour = ConstraintWidget.DimensionBehaviour.WRAP_CONTENT;
            }
            break;
            case MeasureSpec.EXACTLY: {
                desiredWidth = Math.min(mMaxWidth, widthSize) - widthPadding;
            }
        }
        switch (heightMode) {
            case MeasureSpec.AT_MOST: {
                heightBehaviour = ConstraintWidget.DimensionBehaviour.WRAP_CONTENT;
                desiredHeight = heightSize;
            }
            break;
            case MeasureSpec.UNSPECIFIED: {
                heightBehaviour = ConstraintWidget.DimensionBehaviour.WRAP_CONTENT;
            }
            break;
            case MeasureSpec.EXACTLY: {
                desiredHeight = Math.min(mMaxHeight, heightSize) - heightPadding;
            }
        }
        mLayoutWidget.setMinWidth(0);
        mLayoutWidget.setMinHeight(0);
        mLayoutWidget.setHorizontalDimensionBehaviour(widthBehaviour);
        mLayoutWidget.setWidth(desiredWidth);
        mLayoutWidget.setVerticalDimensionBehaviour(heightBehaviour);
        mLayoutWidget.setHeight(desiredHeight);
        mLayoutWidget.setMinWidth(mMinWidth - getPaddingLeft() - getPaddingRight());
        mLayoutWidget.setMinHeight(mMinHeight - getPaddingTop() - getPaddingBottom());
    }
    /**
     * @hide
     *
     * Solve the linear system
     */
    protected void solveLinearSystem() {
        if (SIMPLE_LAYOUT) {
            mLayoutWidget.layout();
        } else {
            int groups = mLayoutWidget.layoutFindGroupsSimple();
            mLayoutWidget.layoutWithGroup(groups);
        }
    }
    /**
     * {@inheritDoc}
     */
    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        final int widgetsCount = getChildCount();
        final boolean isInEditMode = isInEditMode();
        final int helperCount = mConstraintHelpers.size();
        if (helperCount > 0) {
            for (int i = 0; i < helperCount; i++) {
                ConstraintHelper helper = mConstraintHelpers.get(i);
                helper.updatePostLayout(this);
            }
        }
        for (int i = 0; i < widgetsCount; i++) {
            final View child = getChildAt(i);
            LayoutParams params = (LayoutParams) child.getLayoutParams();
            ConstraintWidget widget = params.widget;
            if (child.getVisibility() == GONE && !params.isGuideline && !params.isHelper && !isInEditMode) {
                // If we are in edit mode, let's layout the widget so that they are at "the right place"
                // visually in the editor (as we get our positions from layoutlib)
                continue;
            }
            if (params.isInPlaceholder) {
                continue;
            }
            int l = widget.getDrawX();
            int t = widget.getDrawY();
            int r = l + widget.getWidth();
            int b = t + widget.getHeight();
            if (ALLOWS_EMBEDDED) {
                if (getParent() instanceof ConstraintLayout) {
                    int dx = 0;
                    int dy = 0;
                    ConstraintWidget item = mLayoutWidget; // start with ourselves
                    while (item != null) {
                        dx += item.getDrawX();
                        dy += item.getDrawY();
                        item = item.getParent();
                    }
                    l -= dx;
                    t -= dy;
                    r -= dx;
                    b -= dy;
                }
            }
            child.layout(l, t, r, b);
            if (child instanceof Placeholder) {
                Placeholder holder = (Placeholder) child;
                View content = holder.getContent();
                if (content != null) {
                    content.setVisibility(VISIBLE);
                    content.layout(l, t, r, b);
                }
            }
        }
    }
    /**
     * @hide
     *
     * Set the optimization level for the layout resolution
     * The level can be one of:
     * <ul>
     * <li>ConstraintWidgetContainer.OPTIMIZATION_NONE</li>
     * <li>ConstraintWidgetContainer.OPTIMIZATION_ALL</li>
     * <li>ConstraintWidgetContainer.OPTIMIZATION_BASIC</li>
     * <li>ConstraintWidgetContainer.OPTIMIZATION_CHAIN  </li>
     * </ul>
     * @param level optimization level
     */
    public void setOptimizationLevel(int level) {
        mLayoutWidget.setOptimizationLevel(level);
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new LayoutParams(getContext(), attrs);
    }
    /**
     * {@inheritDoc}
     */
    @Override
    protected LayoutParams generateDefaultLayoutParams() {
        return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
    }
    /**
     * {@inheritDoc}
     */
    @Override
    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
        return new LayoutParams(p);
    }
    /**
     * {@inheritDoc}
     */
    @Override
    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
        return p instanceof LayoutParams;
    }
    /**
     * Sets a ConstraintSet object to manage constraints. The ConstraintSet overrides LayoutParams of child views.
     * @param set Layout children using ConstraintSet
     */
    public void setConstraintSet(ConstraintSet set) {
        mConstraintSet = set;
    }
    /**
     * Return a direct child view by its id if it exists
     *
     * @param id the view id
     * @return the child view, can return null
     */
    public View getViewById(int id) {
        return mChildrenByIds.get(id);
    }
    /**
     *  This class contains the different attributes specifying how a view want to be laid out inside
     *  a {@link ConstraintLayout}. For building up constraints at run time, using {@link ConstraintSet} is recommended.
     */
    public static class LayoutParams extends ViewGroup.MarginLayoutParams {
        /**
         * Dimension will be controlled by constraints.
         */
        public static final int MATCH_CONSTRAINT = 0;
        /**
         * References the id of the parent.
         */
        public static final int PARENT_ID = 0;
        /**
         * Defines an id that is not set.
         */
        public static final int UNSET = -1;
        /**
         * The horizontal orientation.
         */
        public static final int HORIZONTAL = ConstraintWidget.HORIZONTAL;
        /**
         * The vertical orientation.
         */
        public static final int VERTICAL = ConstraintWidget.VERTICAL;
        /**
         * The left side of a view.
         */
        public static final int LEFT = 1;
        /**
         * The right side of a view.
         */
        public static final int RIGHT = 2;
        /**
         * The top of a view.
         */
        public static final int TOP = 3;
        /**
         * The bottom side of a view.
         */
        public static final int BOTTOM = 4;
        /**
         * The baseline of the text in a view.
         */
        public static final int BASELINE = 5;
        /**
         * The left side of a view in left to right languages.
         * In right to left languages it corresponds to the right side of the view
         */
        public static final int START = 6;
        /**
         * The right side of a view in right to left languages.
         * In right to left languages it corresponds to the left side of the view
         */
        public static final int END =  7;
        /**
         * Set matchConstraintDefault* default to the wrap content size.
         * Use to set the matchConstraintDefaultWidth and matchConstraintDefaultHeight
         */
        public static final int MATCH_CONSTRAINT_WRAP = ConstraintWidget.MATCH_CONSTRAINT_WRAP;
        /**
         * Set matchConstraintDefault* spread as much as possible within its constraints.
         * Use to set the matchConstraintDefaultWidth and matchConstraintDefaultHeight
         */
        public static final int MATCH_CONSTRAINT_SPREAD = ConstraintWidget.MATCH_CONSTRAINT_SPREAD;
        /**
         * Set matchConstraintDefault* percent to be based on a percent of another dimension (by default, the parent)
         * Use to set the matchConstraintDefaultWidth and matchConstraintDefaultHeight
         */
        public static final int MATCH_CONSTRAINT_PERCENT = ConstraintWidget.MATCH_CONSTRAINT_PERCENT;
        /**
         * Chain spread style
         */
        public static final int CHAIN_SPREAD = ConstraintWidget.CHAIN_SPREAD;
        /**
         * Chain spread inside style
         */
        public static final int CHAIN_SPREAD_INSIDE = ConstraintWidget.CHAIN_SPREAD_INSIDE;
        /**
         * Chain packed style
         */
        public static final int CHAIN_PACKED = ConstraintWidget.CHAIN_PACKED;
        /**
         * The distance of child (guideline) to the top or left edge of its parent.
         */
        public int guideBegin = UNSET;
        /**
         * The distance of child (guideline) to the top or left edge of its parent.
         */
        public int guideEnd = UNSET;
        /**
         * The ratio of the distance to the parent's sides
         */
        public float guidePercent = UNSET;
        /**
         * Constrains the left side of a child to the left side of a target child (contains the target child id).
         */
        public int leftToLeft = UNSET;
        /**
         * Constrains the left side of a child to the right side of a target child (contains the target child id).
         */
        public int leftToRight = UNSET;
        /**
         * Constrains the right side of a child to the left side of a target child (contains the target child id).
         */
        public int rightToLeft = UNSET;
        /**
         * Constrains the right side of a child to the right side of a target child (contains the target child id).
         */
        public int rightToRight = UNSET;
        /**
         * Constrains the top side of a child to the top side of a target child (contains the target child id).
         */
        public int topToTop = UNSET;
        /**
         * Constrains the top side of a child to the bottom side of a target child (contains the target child id).
         */
        public int topToBottom = UNSET;
        /**
         * Constrains the bottom side of a child to the top side of a target child (contains the target child id).
         */
        public int bottomToTop = UNSET;
        /**
         * Constrains the bottom side of a child to the bottom side of a target child (contains the target child id).
         */
        public int bottomToBottom = UNSET;
        /**
         * Constrains the baseline of a child to the baseline of a target child (contains the target child id).
         */
        public int baselineToBaseline = UNSET;
        /**
         * Constrains the start side of a child to the end side of a target child (contains the target child id).
         */
        public int startToEnd = UNSET;
        /**
         * Constrains the start side of a child to the start side of a target child (contains the target child id).
         */
        public int startToStart = UNSET;
        /**
         * Constrains the end side of a child to the start side of a target child (contains the target child id).
         */
        public int endToStart = UNSET;
        /**
         * Constrains the end side of a child to the end side of a target child (contains the target child id).
         */
        public int endToEnd = UNSET;
        /**
         * The left margin to use when the target is gone.
         */
        public int goneLeftMargin = UNSET;
        /**
         * The top margin to use when the target is gone.
         */
        public int goneTopMargin = UNSET;
        /**
         * The right margin to use when the target is gone
         */
        public int goneRightMargin = UNSET;
        /**
         * The bottom margin to use when the target is gone.
         */
        public int goneBottomMargin = UNSET;
        /**
         * The start margin to use when the target is gone.
         */
        public int goneStartMargin = UNSET;
        /**
         * The end margin to use when the target is gone.
         */
        public int goneEndMargin = UNSET;
        /**
         * The ratio between two connections when the left and right (or start and end) sides are constrained.
         */
        public float horizontalBias = 0.5f;
        /**
         * The ratio between two connections when the top and bottom sides are constrained.
         */
        public float verticalBias = 0.5f;
        /**
         * The ratio information.
         */
        public String dimensionRatio = null;
        /**
         * The ratio between the width and height of the child.
         */
        float dimensionRatioValue = 0;
        /**
         * The child's side to constrain using dimensRatio.
         */
        int dimensionRatioSide = VERTICAL;
        /**
         * The child's weight that we can use to distribute the available horizontal space
         * in a chain, if the dimension behaviour is set to MATCH_CONSTRAINT
         */
        public float horizontalWeight = 0;
        /**
         * The child's weight that we can use to distribute the available vertical space
         * in a chain, if the dimension behaviour is set to MATCH_CONSTRAINT
         */
        public float verticalWeight = 0;
        /**
         * If the child is the start of a horizontal chain, this attribute will drive how
         * the elements of the chain will be positioned. The possible values are:
         * <ul>
         *     <li>{@see CHAIN_SPREAD} -- the elements will be spread out</li>
         *     <li>{@see CHAIN_SPREAD_INSIDE} -- similar, but the endpoints of the chain will not be spread out</li>
         *     <li>{@see CHAIN_PACKED} -- the elements of the chain will be packed together. The horizontal
         *          bias attribute of the child will then affect the positioning of the packed elements</li>
         * </ul>
         */
        public int horizontalChainStyle = CHAIN_SPREAD;
        /**
         * If the child is the start of a vertical chain, this attribute will drive how
         * the elements of the chain will be positioned. The possible values are:
         * <ul>
         *     <li>{@see CHAIN_SPREAD} -- the elements will be spread out</li>
         *     <li>{@see CHAIN_SPREAD_INSIDE} -- similar, but the endpoints of the chain will not be spread out</li>
         *     <li>{@see CHAIN_PACKED} -- the elements of the chain will be packed together. The vertical
         *          bias attribute of the child will then affect the positioning of the packed elements</li>
         * </ul>
         */
        public int verticalChainStyle = CHAIN_SPREAD;
        /**
         * Define how the widget horizontal dimension is handled when set to MATCH_CONSTRAINT
         * <ul>
         *     <li>{@see MATCH_CONSTRAINT_SPREAD} -- the default. The dimension will expand up to the constraints, minus margins</li>
         *     <li>{@see MATCH_CONSTRAINT_WRAP} -- The dimension will be the same as WRAP_CONTENT, unless the size ends
         *     up too large for the constraints; in that case the dimension will expand up to the constraints, minus margins</li>
         *     This attribute may not be applied if the widget is part of a chain in that dimension.
         *     <li>{@see MATCH_CONSTRAINT_PERCENT} -- The dimension will be a percent of another widget (by default, the parent)</li>
         * </ul>
         */
        public int matchConstraintDefaultWidth = MATCH_CONSTRAINT_SPREAD;
        /**
         * Define how the widget vertical dimension is handled when set to MATCH_CONSTRAINT
         * <ul>
         *     <li>{@see MATCH_CONSTRAINT_SPREAD} -- the default. The dimension will expand up to the constraints, minus margins</li>
         *     <li>{@see MATCH_CONSTRAINT_WRAP} -- The dimension will be the same as WRAP_CONTENT, unless the size ends
         *     up too large for the constraints; in that case the dimension will expand up to the constraints, minus margins</li>
         *     This attribute may not be applied if the widget is part of a chain in that dimension.
         *     <li>{@see MATCH_CONSTRAINT_PERCENT} -- The dimension will be a percent of another widget (by default, the parent)</li>
         * </ul>
         */
        public int matchConstraintDefaultHeight = MATCH_CONSTRAINT_SPREAD;
        /**
         * Specify a minimum width size for the widget. It will only apply if the size of the widget
         * is set to MATCH_CONSTRAINT. Don't apply if the widget is part of an horizontal chain.
         */
        public int matchConstraintMinWidth = 0;
        /**
         * Specify a minimum height size for the widget. It will only apply if the size of the widget
         * is set to MATCH_CONSTRAINT. Don't apply if the widget is part of an vertical chain.
         */
        public int matchConstraintMinHeight = 0;
        /**
         * Specify a maximum width size for the widget. It will only apply if the size of the widget
         * is set to MATCH_CONSTRAINT. Don't apply if the widget is part of an horizontal chain.
         */
        public int matchConstraintMaxWidth = 0;
        /**
         * Specify a maximum height size for the widget. It will only apply if the size of the widget
         * is set to MATCH_CONSTRAINT. Don't apply if the widget is part of an vertical chain.
         */
        public int matchConstraintMaxHeight = 0;
        /**
         * Specify the percentage when using the match constraint percent mode. From 0 to 1.
         */
        public float matchConstraintPercentWidth = 1;
        /**
         * Specify the percentage when using the match constraint percent mode. From 0 to 1.
         */
        public float matchConstraintPercentHeight = 1;
        /**
         * The design time location of the left side of the child.
         * Used at design time for a horizontally unconstrained child.
         */
        public int editorAbsoluteX = UNSET;
        /**
         * The design time location of the right side of the child.
         *  Used at design time for a vertically unconstrained child.
         */
        public int editorAbsoluteY = UNSET;
        public int orientation = UNSET;
        // Internal use only
        boolean horizontalDimensionFixed = true;
        boolean verticalDimensionFixed = true;
        boolean needsBaseline = false;
        boolean isGuideline = false;
        boolean isHelper = false;
        boolean isInPlaceholder = false;
        int resolvedLeftToLeft = UNSET;
        int resolvedLeftToRight = UNSET;
        int resolvedRightToLeft = UNSET;
        int resolvedRightToRight = UNSET;
        int resolveGoneLeftMargin = UNSET;
        int resolveGoneRightMargin = UNSET;
        float resolvedHorizontalBias = 0.5f;
        ConstraintWidget widget = new ConstraintWidget();
        public boolean helped = false;
        /**
         * Create a LayoutParams base on an existing layout Params
         *
         * @param source the Layout Params to be copied
         */
        public LayoutParams(LayoutParams source) {
            super(source);
            this.guideBegin = source.guideBegin;
            this.guideEnd = source.guideEnd;
            this.guidePercent = source.guidePercent;
            this.leftToLeft = source.leftToLeft;
            this.leftToRight = source.leftToRight;
            this.rightToLeft = source.rightToLeft;
            this.rightToRight = source.rightToRight;
            this.topToTop = source.topToTop;
            this.topToBottom = source.topToBottom;
            this.bottomToTop = source.bottomToTop;
            this.bottomToBottom = source.bottomToBottom;
            this.baselineToBaseline = source.baselineToBaseline;
            this.startToEnd = source.startToEnd;
            this.startToStart = source.startToStart;
            this.endToStart = source.endToStart;
            this.endToEnd = source.endToEnd;
            this.goneLeftMargin = source.goneLeftMargin;
            this.goneTopMargin = source.goneTopMargin;
            this.goneRightMargin = source.goneRightMargin;
            this.goneBottomMargin = source.goneBottomMargin;
            this.goneStartMargin = source.goneStartMargin;
            this.goneEndMargin = source.goneEndMargin;
            this.horizontalBias = source.horizontalBias;
            this.verticalBias = source.verticalBias;
            this.dimensionRatio = source.dimensionRatio;
            this.dimensionRatioValue = source.dimensionRatioValue;
            this.dimensionRatioSide = source.dimensionRatioSide;
            this.horizontalWeight = source.horizontalWeight;
            this.verticalWeight = source.verticalWeight;
            this.horizontalChainStyle = source.horizontalChainStyle;
            this.verticalChainStyle = source.verticalChainStyle;
            this.matchConstraintDefaultWidth = source.matchConstraintDefaultWidth;
            this.matchConstraintDefaultHeight = source.matchConstraintDefaultHeight;
            this.matchConstraintMinWidth = source.matchConstraintMinWidth;
            this.matchConstraintMaxWidth = source.matchConstraintMaxWidth;
            this.matchConstraintMinHeight = source.matchConstraintMinHeight;
            this.matchConstraintMaxHeight = source.matchConstraintMaxHeight;
            this.editorAbsoluteX = source.editorAbsoluteX;
            this.editorAbsoluteY = source.editorAbsoluteY;
            this.orientation = source.orientation;
            this.horizontalDimensionFixed = source.horizontalDimensionFixed;
            this.verticalDimensionFixed = source.verticalDimensionFixed;
            this.needsBaseline = source.needsBaseline;
            this.isGuideline = source.isGuideline;
            this.resolvedLeftToLeft = source.resolvedLeftToLeft;
            this.resolvedLeftToRight = source.resolvedLeftToRight;
            this.resolvedRightToLeft = source.resolvedRightToLeft;
            this.resolvedRightToRight = source.resolvedRightToRight;
            this.resolveGoneLeftMargin = source.resolveGoneLeftMargin;
            this.resolveGoneRightMargin = source.resolveGoneRightMargin;
            this.resolvedHorizontalBias = source.resolvedHorizontalBias;
            this.widget = source.widget;
        }
        public LayoutParams(Context c, AttributeSet attrs) {
            super(c, attrs);
            TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ConstraintLayout_Layout);
            final int N = a.getIndexCount();
            for (int i = 0; i < N; i++) {
                int attr = a.getIndex(i);
                if (attr == R.styleable.ConstraintLayout_Layout_layout_constraintLeft_toLeftOf) {
                    leftToLeft = a.getResourceId(attr, leftToLeft);
                    if (leftToLeft == UNSET) {
                        leftToLeft = a.getInt(attr, UNSET);
                    }
                } else if (attr == R.styleable.ConstraintLayout_Layout_layout_constraintLeft_toRightOf) {
                    leftToRight = a.getResourceId(attr, leftToRight);
                    if (leftToRight == UNSET) {
                        leftToRight = a.getInt(attr, UNSET);
                    }
                } else if (attr == R.styleable.ConstraintLayout_Layout_layout_constraintRight_toLeftOf) {
                    rightToLeft = a.getResourceId(attr, rightToLeft);
                    if (rightToLeft == UNSET) {
                        rightToLeft = a.getInt(attr, UNSET);
                    }
                } else if (attr == R.styleable.ConstraintLayout_Layout_layout_constraintRight_toRightOf) {
                    rightToRight = a.getResourceId(attr, rightToRight);
                    if (rightToRight == UNSET) {
                        rightToRight = a.getInt(attr, UNSET);
                    }
                } else if (attr == R.styleable.ConstraintLayout_Layout_layout_constraintTop_toTopOf) {
                    topToTop = a.getResourceId(attr, topToTop);
                    if (topToTop == UNSET) {
                        topToTop = a.getInt(attr, UNSET);
                    }
                } else if (attr == R.styleable.ConstraintLayout_Layout_layout_constraintTop_toBottomOf) {
                    topToBottom = a.getResourceId(attr, topToBottom);
                    if (topToBottom == UNSET) {
                        topToBottom = a.getInt(attr, UNSET);
                    }
                } else if (attr == R.styleable.ConstraintLayout_Layout_layout_constraintBottom_toTopOf) {
                    bottomToTop = a.getResourceId(attr, bottomToTop);
                    if (bottomToTop == UNSET) {
                        bottomToTop = a.getInt(attr, UNSET);
                    }
                } else if (attr == R.styleable.ConstraintLayout_Layout_layout_constraintBottom_toBottomOf) {
                    bottomToBottom = a.getResourceId(attr, bottomToBottom);
                    if (bottomToBottom == UNSET) {
                        bottomToBottom = a.getInt(attr, UNSET);
                    }
                } else if (attr == R.styleable.ConstraintLayout_Layout_layout_constraintBaseline_toBaselineOf) {
                    baselineToBaseline = a.getResourceId(attr, baselineToBaseline);
                    if (baselineToBaseline == UNSET) {
                        baselineToBaseline = a.getInt(attr, UNSET);
                    }
                } else if (attr == R.styleable.ConstraintLayout_Layout_layout_editor_absoluteX) {
                    editorAbsoluteX = a.getDimensionPixelOffset(attr, editorAbsoluteX);
                } else if (attr == R.styleable.ConstraintLayout_Layout_layout_editor_absoluteY) {
                    editorAbsoluteY = a.getDimensionPixelOffset(attr, editorAbsoluteY);
                } else if (attr == R.styleable.ConstraintLayout_Layout_layout_constraintGuide_begin) {
                    guideBegin = a.getDimensionPixelOffset(attr, guideBegin);
                } else if (attr == R.styleable.ConstraintLayout_Layout_layout_constraintGuide_end) {
                    guideEnd = a.getDimensionPixelOffset(attr, guideEnd);
                } else if (attr == R.styleable.ConstraintLayout_Layout_layout_constraintGuide_percent) {
                    guidePercent = a.getFloat(attr, guidePercent);
                } else if (attr == R.styleable.ConstraintLayout_Layout_android_orientation) {
                    orientation = a.getInt(attr, orientation);
                } else if (attr == R.styleable.ConstraintLayout_Layout_layout_constraintStart_toEndOf) {
                    startToEnd = a.getResourceId(attr, startToEnd);
                    if (startToEnd == UNSET) {
                        startToEnd = a.getInt(attr, UNSET);
                    }
                } else if (attr == R.styleable.ConstraintLayout_Layout_layout_constraintStart_toStartOf) {
                    startToStart = a.getResourceId(attr, startToStart);
                    if (startToStart == UNSET) {
                        startToStart = a.getInt(attr, UNSET);
                    }
                } else if (attr == R.styleable.ConstraintLayout_Layout_layout_constraintEnd_toStartOf) {
                    endToStart = a.getResourceId(attr, endToStart);
                    if (endToStart == UNSET) {
                        endToStart = a.getInt(attr, UNSET);
                    }
                } else if (attr == R.styleable.ConstraintLayout_Layout_layout_constraintEnd_toEndOf) {
                    endToEnd = a.getResourceId(attr, endToEnd);
                    if (endToEnd == UNSET) {
                        endToEnd = a.getInt(attr, UNSET);
                    }
                } else if (attr == R.styleable.ConstraintLayout_Layout_layout_goneMarginLeft) {
                    goneLeftMargin = a.getDimensionPixelSize(attr, goneLeftMargin);
                } else if (attr == R.styleable.ConstraintLayout_Layout_layout_goneMarginTop) {
                    goneTopMargin = a.getDimensionPixelSize(attr, goneTopMargin);
                } else if (attr == R.styleable.ConstraintLayout_Layout_layout_goneMarginRight) {
                    goneRightMargin = a.getDimensionPixelSize(attr, goneRightMargin);
                } else if (attr == R.styleable.ConstraintLayout_Layout_layout_goneMarginBottom) {
                    goneBottomMargin = a.getDimensionPixelSize(attr, goneBottomMargin);
                } else if (attr == R.styleable.ConstraintLayout_Layout_layout_goneMarginStart) {
                    goneStartMargin = a.getDimensionPixelSize(attr, goneStartMargin);
                } else if (attr == R.styleable.ConstraintLayout_Layout_layout_goneMarginEnd) {
                    goneEndMargin = a.getDimensionPixelSize(attr, goneEndMargin);
                } else if (attr == R.styleable.ConstraintLayout_Layout_layout_constraintHorizontal_bias) {
                    horizontalBias = a.getFloat(attr, horizontalBias);
                } else if (attr == R.styleable.ConstraintLayout_Layout_layout_constraintVertical_bias) {
                    verticalBias = a.getFloat(attr, verticalBias);
                } else if (attr == R.styleable.ConstraintLayout_Layout_layout_constraintDimensionRatio) {
                    dimensionRatio = a.getString(attr);
                    dimensionRatioValue = Float.NaN;
                    dimensionRatioSide = UNSET;
                    if (dimensionRatio != null) {
                        int len = dimensionRatio.length();
                        int commaIndex = dimensionRatio.indexOf(',');
                        if (commaIndex > 0 && commaIndex < len - 1) {
                            String dimension = dimensionRatio.substring(0, commaIndex);
                            if (dimension.equalsIgnoreCase("W")) {
                                dimensionRatioSide = HORIZONTAL;
                            } else if (dimension.equalsIgnoreCase("H")) {
                                dimensionRatioSide = VERTICAL;
                            }
                            commaIndex++;
                        } else {
                            commaIndex = 0;
                        }
                        int colonIndex = dimensionRatio.indexOf(':');
                        if (colonIndex >= 0 && colonIndex < len - 1) {
                            String nominator = dimensionRatio.substring(commaIndex, colonIndex);
                            String denominator = dimensionRatio.substring(colonIndex + 1);
                            if (nominator.length() > 0 && denominator.length() > 0) {
                                try {
                                    float nominatorValue = Float.parseFloat(nominator);
                                    float denominatorValue = Float.parseFloat(denominator);
                                    if (nominatorValue > 0 && denominatorValue > 0) {
                                        if (dimensionRatioSide == VERTICAL) {
                                            dimensionRatioValue = Math.abs(denominatorValue / nominatorValue);
                                        } else {
                                            dimensionRatioValue = Math.abs(nominatorValue / denominatorValue);
                                        }
                                    }
                                } catch (NumberFormatException e) {
                                    // Ignore
                                }
                            }
                        } else {
                            String r = dimensionRatio.substring(commaIndex);
                            if (r.length() > 0) {
                                try {
                                    dimensionRatioValue = Float.parseFloat(r);
                                } catch (NumberFormatException e) {
                                    // Ignore
                                }
                            }
                        }
                    }
                } else if (attr == R.styleable.ConstraintLayout_Layout_layout_constraintHorizontal_weight) {
                    horizontalWeight = a.getFloat(attr, 0);
                } else if (attr == R.styleable.ConstraintLayout_Layout_layout_constraintVertical_weight) {
                    verticalWeight = a.getFloat(attr, 0);
                } else if (attr == R.styleable.ConstraintLayout_Layout_layout_constraintHorizontal_chainStyle) {
                    horizontalChainStyle = a.getInt(attr, CHAIN_SPREAD);
                } else if (attr == R.styleable.ConstraintLayout_Layout_layout_constraintVertical_chainStyle) {
                    verticalChainStyle = a.getInt(attr, CHAIN_SPREAD);
                } else if (attr == R.styleable.ConstraintLayout_Layout_layout_constraintWidth_default) {
                    matchConstraintDefaultWidth = a.getInt(attr, MATCH_CONSTRAINT_SPREAD);
                } else if (attr == R.styleable.ConstraintLayout_Layout_layout_constraintHeight_default) {
                    matchConstraintDefaultHeight = a.getInt(attr, MATCH_CONSTRAINT_SPREAD);
                } else if (attr == R.styleable.ConstraintLayout_Layout_layout_constraintWidth_min) {
                    try {
                        matchConstraintMinWidth = a.getDimensionPixelSize(attr, matchConstraintMinWidth);
                    } catch (android.view.InflateException e) {
                        int value = a.getInt(attr, matchConstraintMinWidth);
                        if (value == WRAP_CONTENT) {
                            matchConstraintMinWidth = WRAP_CONTENT;
                        }
                    }
                } else if (attr == R.styleable.ConstraintLayout_Layout_layout_constraintWidth_max) {
                    try {
                        matchConstraintMaxWidth = a.getDimensionPixelSize(attr, matchConstraintMaxWidth);
                    } catch (android.view.InflateException e) {
                        int value = a.getInt(attr, matchConstraintMaxWidth);
                        if (value == WRAP_CONTENT) {
                            matchConstraintMaxWidth = WRAP_CONTENT;
                        }
                    }
                } else if (attr == R.styleable.ConstraintLayout_Layout_layout_constraintWidth_percent) {
                    matchConstraintPercentWidth = a.getFloat(attr, matchConstraintPercentWidth);
                } else if (attr == R.styleable.ConstraintLayout_Layout_layout_constraintHeight_min) {
                    try {
                        matchConstraintMinHeight = a.getDimensionPixelSize(attr, matchConstraintMinHeight);
                    } catch (android.view.InflateException e) {
                        int value = a.getInt(attr, matchConstraintMinHeight);
                        if (value == WRAP_CONTENT) {
                            matchConstraintMinHeight = WRAP_CONTENT;
                        }
                    }
                } else if (attr == R.styleable.ConstraintLayout_Layout_layout_constraintHeight_max) {
                    try {
                        matchConstraintMaxHeight = a.getDimensionPixelSize(attr, matchConstraintMaxHeight);
                    } catch (android.view.InflateException e) {
                        int value = a.getInt(attr, matchConstraintMaxHeight);
                        if (value == WRAP_CONTENT) {
                            matchConstraintMaxHeight = WRAP_CONTENT;
                        }
                    }
                } else if (attr == R.styleable.ConstraintLayout_Layout_layout_constraintHeight_percent) {
                    matchConstraintPercentHeight = a.getFloat(attr, matchConstraintPercentHeight);
                } else if (attr == R.styleable.ConstraintLayout_Layout_layout_constraintLeft_creator) {
                    // Skip
                } else if (attr == R.styleable.ConstraintLayout_Layout_layout_constraintTop_creator) {
                    // Skip
                } else if (attr == R.styleable.ConstraintLayout_Layout_layout_constraintRight_creator) {
                    // Skip
                } else if (attr == R.styleable.ConstraintLayout_Layout_layout_constraintBottom_creator) {
                    // Skip
                } else if (attr == R.styleable.ConstraintLayout_Layout_layout_constraintBaseline_creator) {
                    // Skip
                } else {
                    // Skip
                }
            }
            a.recycle();
            validate();
        }
        public void validate() {
            isGuideline = false;
            horizontalDimensionFixed = true;
            verticalDimensionFixed = true;
            if (width == MATCH_CONSTRAINT || width == MATCH_PARENT) {
                horizontalDimensionFixed = false;
            }
            if (height == MATCH_CONSTRAINT || height == MATCH_PARENT) {
                verticalDimensionFixed = false;
            }
            if (guidePercent != UNSET || guideBegin != UNSET || guideEnd != UNSET) {
                isGuideline = true;
                horizontalDimensionFixed = true;
                verticalDimensionFixed = true;
                if (!(widget instanceof Guideline)) {
                    widget = new Guideline();
                }
                ((Guideline) widget).setOrientation(orientation);
            }
        }
        public LayoutParams(int width, int height) {
            super(width, height);
        }
        public LayoutParams(ViewGroup.LayoutParams source) {
            super(source);
        }
        /**
         * {@inheritDoc}
         */
        @Override
        @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
        public void resolveLayoutDirection(int layoutDirection) {
            super.resolveLayoutDirection(layoutDirection);
            resolvedRightToLeft = UNSET;
            resolvedRightToRight = UNSET;
            resolvedLeftToLeft = UNSET;
            resolvedLeftToRight = UNSET;
            resolveGoneLeftMargin = UNSET;
            resolveGoneRightMargin = UNSET;
            resolveGoneLeftMargin = goneLeftMargin;
            resolveGoneRightMargin = goneRightMargin;
            resolvedHorizontalBias = horizontalBias;
            boolean isRtl = (View.LAYOUT_DIRECTION_RTL == getLayoutDirection());
            // Post JB MR1, if start/end are defined, they take precedence over left/right
            if (isRtl) {
                boolean startEndDefined = false;
                if (startToEnd != UNSET) {
                    resolvedRightToLeft = startToEnd;
                    startEndDefined = true;
                } else if (startToStart != UNSET) {
                    resolvedRightToRight = startToStart;
                    startEndDefined = true;
                }
                if (endToStart != UNSET) {
                    resolvedLeftToRight = endToStart;
                    startEndDefined = true;
                }
                if (endToEnd != UNSET) {
                    resolvedLeftToLeft = endToEnd;
                    startEndDefined = true;
                }
                if (goneStartMargin != UNSET) {
                    resolveGoneRightMargin = goneStartMargin;
                }
                if (goneEndMargin != UNSET) {
                    resolveGoneLeftMargin = goneEndMargin;
                }
                if (startEndDefined) {
                    resolvedHorizontalBias = 1 - horizontalBias;
                }
            } else {
                if (startToEnd != UNSET) {
                    resolvedLeftToRight = startToEnd;
                }
                if (startToStart != UNSET) {
                    resolvedLeftToLeft = startToStart;
                }
                if (endToStart != UNSET) {
                    resolvedRightToLeft = endToStart;
                }
                if (endToEnd != UNSET) {
                    resolvedRightToRight = endToEnd;
                }
                if (goneStartMargin != UNSET) {
                    resolveGoneLeftMargin = goneStartMargin;
                }
                if (goneEndMargin != UNSET) {
                    resolveGoneRightMargin = goneEndMargin;
                }
            }
            // if no constraint is defined via RTL attributes, use left/right if present
            if (endToStart == UNSET && endToEnd == UNSET) {
                if (rightToLeft != UNSET) {
                    resolvedRightToLeft = rightToLeft;
                } else if (rightToRight != UNSET) {
                    resolvedRightToRight = rightToRight;
                }
            }
            if (startToStart == UNSET && startToEnd == UNSET) {
                if (leftToLeft != UNSET) {
                    resolvedLeftToLeft = leftToLeft;
                } else if (leftToRight != UNSET) {
                    resolvedLeftToRight = leftToRight;
                }
            }
        }
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void requestLayout() {
        super.requestLayout();
        mDirtyHierarchy = true;
    }
    @Override
    public boolean shouldDelayChildPressedState() {
        return false;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值