blob: 9222fd6d7bc0cd0d3a86f1681667920321ca5f0e [file] [log] [blame]
/*
* 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.TypedArray;
import android.os.Build;
import android.support.constraint.solver.widgets.Animator;
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.Log;
import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
/**
* A Layout where the positions of the children is described as constraints in relation to each
* other or to the parent.
* <p>
* <p>
* Note that you cannot have a circular dependency in constraints
* </p>
* Also see {@link ConstraintLayout.LayoutParams
* ConstraintLayout.LayoutParams} for layout attributes
* </p>
*/
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;
private static final String TAG = "ConstraintLayout";
private static final boolean SIMPLE_LAYOUT = true;
SparseArray<View> mChildrenByIds = new SparseArray<>();
private final ArrayList<ConstraintWidget> mSizeDependentsWidgets = new ArrayList<>(100);
ConstraintWidgetContainer mLayoutWidget = new ConstraintWidgetContainer();
private boolean mDirtyHierarchy = true;
public ConstraintLayout(Context context) {
super(context);
init();
}
public ConstraintLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public ConstraintLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mLayoutWidget.setCompanionWidget(this);
mChildrenByIds.put(getId(), this);
}
@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);
}
}
@Override
public void removeView(View view) {
super.removeView(view);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
onViewRemoved(view);
}
}
@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;
widget = layoutParams.widget;
}
}
ConstraintWidgetContainer container = mLayoutWidget;
widget.setCompanionWidget(view);
mChildrenByIds.put(view.getId(), view);
container.add(widget);
widget.setParent(container);
updateHierarchy();
}
@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));
updateHierarchy();
}
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) {
mSizeDependentsWidgets.clear();
setChildrenConstraints();
}
}
void setChildrenConstraints() {
final int count = getChildCount();
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();
widget.reset();
widget.setParent(mLayoutWidget);
widget.setVisibility(child.getVisibility());
widget.setCompanionWidget(child);
if (!layoutParams.verticalLock || !layoutParams.horizontalLock) {
mSizeDependentsWidgets.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);
}
if (layoutParams.orientation == LayoutParams.VERTICAL) {
guideline.setOrientation(Guideline.VERTICAL);
} else {
guideline.setOrientation(Guideline.HORIZONTAL);
}
} else if ((layoutParams.leftToLeft != LayoutParams.UNSET)
|| (layoutParams.leftToRight != LayoutParams.UNSET)
|| (layoutParams.rightToLeft != LayoutParams.UNSET)
|| (layoutParams.rightToRight != LayoutParams.UNSET)
|| (layoutParams.topToTop != LayoutParams.UNSET)
|| (layoutParams.topToBottom != LayoutParams.UNSET)
|| (layoutParams.bottomToTop != LayoutParams.UNSET)
|| (layoutParams.bottomToBottom != LayoutParams.UNSET)
|| (layoutParams.baselineToBaseline != LayoutParams.UNSET)
|| (layoutParams.editorAbsoluteX != LayoutParams.UNSET)
|| (layoutParams.editorAbsoluteY != LayoutParams.UNSET)) {
// Process match_Parent converting it to 0dp
if (layoutParams.width == LayoutParams.MATCH_PARENT) {
layoutParams.horizontalLock = false;
}
// Process match_Parent converting it to 0dp
if (layoutParams.height == LayoutParams.MATCH_PARENT) {
layoutParams.verticalLock = false;
}
// Left constraint
if (layoutParams.leftToLeft != LayoutParams.UNSET) {
ConstraintWidget target = getTargetWidget(layoutParams.leftToLeft);
if (target != null) {
widget.immediateConnect(ConstraintAnchor.Type.LEFT, target,
ConstraintAnchor.Type.LEFT, layoutParams.leftMargin);
}
} else if (layoutParams.leftToRight != LayoutParams.UNSET) {
ConstraintWidget target = getTargetWidget(layoutParams.leftToRight);
if (target != null) {
widget.immediateConnect(ConstraintAnchor.Type.LEFT, target,
ConstraintAnchor.Type.RIGHT, layoutParams.leftMargin);
}
}
// Right constraint
if (layoutParams.rightToLeft != LayoutParams.UNSET) {
ConstraintWidget target = getTargetWidget(layoutParams.rightToLeft);
if (target != null) {
widget.immediateConnect(ConstraintAnchor.Type.RIGHT, target,
ConstraintAnchor.Type.LEFT, layoutParams.rightMargin);
}
} else if (layoutParams.rightToRight != LayoutParams.UNSET) {
ConstraintWidget target = getTargetWidget(layoutParams.rightToRight);
if (target != null) {
widget.immediateConnect(ConstraintAnchor.Type.RIGHT, target,
ConstraintAnchor.Type.RIGHT, layoutParams.rightMargin);
}
}
// Top constraint
if (layoutParams.topToTop != LayoutParams.UNSET) {
ConstraintWidget target = getTargetWidget(layoutParams.topToTop);
if (target != null) {
widget.immediateConnect(ConstraintAnchor.Type.TOP, target,
ConstraintAnchor.Type.TOP, layoutParams.topMargin);
}
} else if (layoutParams.topToBottom != LayoutParams.UNSET) {
ConstraintWidget target = getTargetWidget(layoutParams.topToBottom);
if (target != null) {
widget.immediateConnect(ConstraintAnchor.Type.TOP, target,
ConstraintAnchor.Type.BOTTOM, layoutParams.topMargin);
}
}
// Bottom constraint
if (layoutParams.bottomToTop != LayoutParams.UNSET) {
ConstraintWidget target = getTargetWidget(layoutParams.bottomToTop);
if (target != null) {
widget.immediateConnect(ConstraintAnchor.Type.BOTTOM, target,
ConstraintAnchor.Type.TOP, layoutParams.bottomMargin);
}
} else if (layoutParams.bottomToBottom != LayoutParams.UNSET) {
ConstraintWidget target = getTargetWidget(layoutParams.bottomToBottom);
if (target != null) {
widget.immediateConnect(ConstraintAnchor.Type.BOTTOM, target,
ConstraintAnchor.Type.BOTTOM, layoutParams.bottomMargin);
}
}
// Baseline constraint
if (layoutParams.baselineToBaseline != LayoutParams.UNSET) {
View view = mChildrenByIds.get(layoutParams.baselineToBaseline);
ConstraintWidget target = getTargetWidget(layoutParams.baselineToBaseline);
if (target != null) {
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, ConstraintAnchor.Strength.STRONG,
ConstraintAnchor.USER_CREATOR, true);
widget.getAnchor(ConstraintAnchor.Type.TOP).reset();
widget.getAnchor(ConstraintAnchor.Type.BOTTOM).reset();
}
}
if (layoutParams.horizontalBias >= 0 && layoutParams.horizontalBias != 0.5f) {
widget.setHorizontalBiasPercent(layoutParams.horizontalBias);
}
if (layoutParams.verticalBias >= 0 && layoutParams.verticalBias != 0.5f) {
widget.setVerticalBiasPercent(layoutParams.verticalBias);
}
// FIXME: need to agree on the correct magic value for this rather than simply using zero.
if (!layoutParams.horizontalLock) {
widget.setHorizontalDimensionBehaviour(ConstraintWidget.DimensionBehaviour.ANY);
widget.setWidth(0);
if (layoutParams.width == LayoutParams.MATCH_PARENT) {
widget.setWidth(mLayoutWidget.getWidth());
}
} else {
widget.setHorizontalDimensionBehaviour(ConstraintWidget.DimensionBehaviour.FIXED);
widget.setWidth(layoutParams.width);
}
if (!layoutParams.verticalLock) {
widget.setVerticalDimensionBehaviour(ConstraintWidget.DimensionBehaviour.ANY);
widget.setHeight(0);
if (layoutParams.height == LayoutParams.MATCH_PARENT) {
widget.setWidth(mLayoutWidget.getHeight());
}
} else {
widget.setVerticalDimensionBehaviour(ConstraintWidget.DimensionBehaviour.FIXED);
widget.setHeight(layoutParams.height);
}
if (isInEditMode() && ((layoutParams.editorAbsoluteX != LayoutParams.UNSET)
|| (layoutParams.editorAbsoluteY != LayoutParams.UNSET))) {
widget.setOrigin(layoutParams.editorAbsoluteX, layoutParams.editorAbsoluteY);
}
if (layoutParams.dimensionRatio > 0) {
widget.setDimensionRatio(layoutParams.dimensionRatio);
}
}
}
}
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;
}
}
private final ConstraintWidget getViewWidget(View view) {
if (view == this) {
return mLayoutWidget;
}
return view == null ? null : ((LayoutParams) view.getLayoutParams()).widget;
}
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) {
continue;
}
int width = params.width;
int height = params.height;
final int childWidthMeasureSpec;
final int childHeightMeasureSpec;
if (width == 0) {
childWidthMeasureSpec = getChildMeasureSpec(parentWidthSpec,
widthPadding, LayoutParams.WRAP_CONTENT);
} else {
childWidthMeasureSpec = getChildMeasureSpec(parentWidthSpec,
widthPadding, width);
}
if (height == 0) {
childHeightMeasureSpec = getChildMeasureSpec(parentHeightSpec,
heightPadding, LayoutParams.WRAP_CONTENT);
} else {
childHeightMeasureSpec = getChildMeasureSpec(parentHeightSpec,
heightPadding, height);
}
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
width = child.getMeasuredWidth();
height = child.getMeasuredHeight();
widget.setWidth(width);
widget.setHeight(height);
if (params.needsBaseline) {
int baseline = child.getBaseline();
if (baseline != -1) {
widget.setBaselineDistance(baseline);
}
}
}
}
int previousPaddingLeft = -1;
int previousPaddingTop = -1;
int previousWidthMeasureSpec = -1;
int previousHeightMeasureSpec = -1;
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mDirtyHierarchy) {
mDirtyHierarchy = false;
updateHierarchy();
}
int paddingLeft = getPaddingLeft();
int paddingTop = getPaddingTop();
if (previousPaddingLeft == -1
|| previousPaddingTop == -1
|| previousHeightMeasureSpec == -1
|| previousWidthMeasureSpec == -1
|| previousPaddingLeft != paddingLeft
|| previousPaddingTop != paddingTop
|| previousWidthMeasureSpec != widthMeasureSpec
|| previousHeightMeasureSpec != heightMeasureSpec) {
mLayoutWidget.setX(paddingLeft);
mLayoutWidget.setY(paddingTop);
setSelfDimensionBehaviour(widthMeasureSpec, heightMeasureSpec);
}
previousPaddingLeft = paddingLeft;
previousPaddingTop = paddingTop;
previousWidthMeasureSpec = widthMeasureSpec;
previousHeightMeasureSpec = heightMeasureSpec;
internalMeasureChildren(widthMeasureSpec, heightMeasureSpec);
//noinspection PointlessBooleanExpression
if (ALLOWS_EMBEDDED && mLayoutWidget.getParent() != null) {
setVisibility(INVISIBLE);
return;
}
// let's solve the linear system.
solveLinearSystem(); // first pass
int childState = 0;
// let's update the size dependent widgets if any...
final int sizeDependentWidgetsCount = mSizeDependentsWidgets.size();
int heightPadding = paddingTop + getPaddingBottom();
int widthPadding = paddingLeft + getPaddingRight();
if (sizeDependentWidgetsCount > 0) {
for (int i = 0; i < sizeDependentWidgetsCount; i++) {
ConstraintWidget widget = mSizeDependentsWidgets.get(i);
if (widget instanceof Guideline) {
continue;
}
View child = (View) widget.getCompanionWidget();
if (child == null) {
continue;
}
int widthSpec = MeasureSpec.makeMeasureSpec(widget.getWidth(), MeasureSpec.EXACTLY);
int heightSpec = MeasureSpec.makeMeasureSpec(widget.getHeight(), MeasureSpec.EXACTLY);
final ViewGroup.LayoutParams lp = child.getLayoutParams();
if (lp.width == LayoutParams.WRAP_CONTENT) {
widthSpec = getChildMeasureSpec(widthMeasureSpec, widthPadding, lp.width);
}
if (lp.height == LayoutParams.WRAP_CONTENT) {
heightSpec = getChildMeasureSpec(heightMeasureSpec, heightPadding, lp.height);
}
// we need to re-measure the child...
child.measure(widthSpec, heightSpec);
int width = child.getMeasuredWidth();
int height = child.getMeasuredHeight();
widget.setWidth(width);
widget.setHeight(height);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
childState = combineMeasuredStates(childState, child.getMeasuredState());
}
}
solveLinearSystem(); // second pass
}
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);
setMeasuredDimension(resolvedWidthSize & MEASURED_SIZE_MASK, resolvedHeightSize & MEASURED_SIZE_MASK);
} else {
setMeasuredDimension(androidLayoutWidth, androidLayoutHeight);
}
}
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;
// TODO: investigate measure too small (check MeasureSpec)
ViewGroup.LayoutParams params = getLayoutParams();
switch (widthMode) {
case MeasureSpec.AT_MOST: {
widthBehaviour = ConstraintWidget.DimensionBehaviour.WRAP_CONTENT;
}
break;
case MeasureSpec.UNSPECIFIED: {
if (params.width > 0) {
desiredWidth = params.width;
} else {
widthBehaviour = ConstraintWidget.DimensionBehaviour.WRAP_CONTENT;
}
}
break;
case MeasureSpec.EXACTLY: {
desiredWidth = widthSize - widthPadding;
}
}
switch (heightMode) {
case MeasureSpec.AT_MOST: {
heightBehaviour = ConstraintWidget.DimensionBehaviour.WRAP_CONTENT;
}
break;
case MeasureSpec.UNSPECIFIED: {
if (params.height > 0) {
desiredHeight = params.height;
} else {
heightBehaviour = ConstraintWidget.DimensionBehaviour.WRAP_CONTENT;
}
}
break;
case MeasureSpec.EXACTLY: {
desiredHeight = heightSize - heightPadding;
}
}
mLayoutWidget.setHorizontalDimensionBehaviour(widthBehaviour);
mLayoutWidget.setWidth(desiredWidth);
mLayoutWidget.setVerticalDimensionBehaviour(heightBehaviour);
mLayoutWidget.setHeight(desiredHeight);
}
/**
* Solve the linear system
*/
private void solveLinearSystem() {
Animator.setAnimationEnabled(false);
if (SIMPLE_LAYOUT) {
mLayoutWidget.layout();
} else {
int groups = mLayoutWidget.layoutFindGroupsSimple();
mLayoutWidget.layoutWithGroup(groups);
}
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
final int widgetsCount = getChildCount();
for (int i = 0; i < widgetsCount; i++) {
final View child = getChildAt(i);
LayoutParams params = (LayoutParams) child.getLayoutParams();
ConstraintWidget widget = params.widget;
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);
}
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(getContext(), attrs);
}
@Override
protected LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
}
@Override
protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return new LayoutParams(p);
}
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof LayoutParams;
}
public static class LayoutParams extends ViewGroup.MarginLayoutParams {
public static final int PARENT_ID = 0;
public static int UNSET = -1;
public static int HORIZONTAL = 0;
public static int VERTICAL = 1;
public boolean needsBaseline = false;
public boolean isGuideline = false;
public int guideBegin = UNSET;
public int guideEnd = UNSET;
public float guidePercent = UNSET;
public int leftToLeft = UNSET;
public int leftToRight = UNSET;
public int rightToLeft = UNSET;
public int rightToRight = UNSET;
public int topToTop = UNSET;
public int topToBottom = UNSET;
public int bottomToTop = UNSET;
public int bottomToBottom = UNSET;
public int baselineToBaseline = UNSET;
public int startToEnd = UNSET;
public int startToStart = UNSET;
public int endToStart = UNSET;
public int endToEnd = UNSET;
public float horizontalBias = 0.5f;
public float verticalBias = 0.5f;
public float dimensionRatio = 0f;
public int editorAbsoluteX = UNSET;
public int editorAbsoluteY = UNSET;
public int orientation = UNSET;
// Internal use only
boolean horizontalLock = true;
boolean verticalLock = true;
int originalLeftToLeft = UNSET;
int originalLeftToRight = UNSET;
int originalRightToLeft = UNSET;
int originalRightToRight = UNSET;
ConstraintWidget widget = new ConstraintWidget();
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);
}
originalLeftToLeft = leftToLeft;
} else if (attr == R.styleable.ConstraintLayout_Layout_layout_constraintLeft_toRightOf) {
leftToRight = a.getResourceId(attr, leftToRight);
if (leftToRight == UNSET) {
leftToRight = a.getInt(attr, UNSET);
}
originalLeftToRight = leftToRight;
} else if (attr == R.styleable.ConstraintLayout_Layout_layout_constraintRight_toLeftOf) {
rightToLeft = a.getResourceId(attr, rightToLeft);
if (rightToLeft == UNSET) {
rightToLeft = a.getInt(attr, UNSET);
}
originalRightToLeft = rightToLeft;
} else if (attr == R.styleable.ConstraintLayout_Layout_layout_constraintRight_toRightOf) {
rightToRight = a.getResourceId(attr, rightToRight);
if (rightToRight == UNSET) {
rightToRight = a.getInt(attr, UNSET);
}
originalRightToRight = rightToRight;
} 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_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) {
String ratio = a.getString(attr);
if (ratio != null) {
int colonIndex = ratio.indexOf(':');
if (colonIndex >= 0 && colonIndex < ratio.length() - 1) {
String nominator = ratio.substring(0, colonIndex);
String denominator = ratio.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) {
dimensionRatio = Math.abs(nominatorValue / denominatorValue);
}
} catch (NumberFormatException e) {
// Ignore
}
}
}
}
} 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 {
Log.w(TAG, "Unknown attribute 0x" + Integer.toHexString(attr));
}
}
if (width == 0) {
horizontalLock = false;
}
if (height == 0) {
verticalLock = false;
}
if (guidePercent != UNSET || guideBegin != UNSET || guideEnd != UNSET) {
isGuideline = true;
horizontalLock = true;
verticalLock = true;
if (!(widget instanceof Guideline)) {
widget = new Guideline();
}
}
}
public LayoutParams(int width, int height) {
super(width, height);
}
public LayoutParams(ViewGroup.LayoutParams source) {
super(source);
}
@Override
protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
try {
width = a.getLayoutDimension(widthAttr, "layout_width");
height = a.getLayoutDimension(heightAttr, "layout_height");
} catch (RuntimeException e) {
// we ignore the runtime exception for now if layout_width and layout_height aren't there.
}
}
@Override
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
public void resolveLayoutDirection(int layoutDirection) {
super.resolveLayoutDirection(layoutDirection);
rightToLeft = originalRightToLeft;
rightToRight = originalRightToRight;
leftToRight = originalLeftToRight;
leftToLeft = originalLeftToLeft;
boolean isRtl = (View.LAYOUT_DIRECTION_RTL == getLayoutDirection());
if (isRtl) {
if (startToEnd != UNSET) {
rightToLeft = startToEnd;
}
if (startToStart != UNSET) {
rightToRight = startToStart;
}
if (endToStart != UNSET) {
leftToRight = endToStart;
}
if (endToEnd != UNSET) {
leftToLeft = endToEnd;
}
} else {
if (startToEnd != UNSET) {
leftToRight = startToEnd;
}
if (startToStart != UNSET) {
leftToLeft = startToStart;
}
if (endToStart != UNSET) {
rightToLeft = endToStart;
}
if (endToEnd != UNSET) {
rightToRight = endToEnd;
}
}
}
}
@Override
public void requestLayout() {
super.requestLayout();
mDirtyHierarchy = true;
}
}