Skip to content
This repository was archived by the owner on Dec 27, 2024. It is now read-only.

Commit bf49113

Browse files
authored
Add MotionPlaceholder (#398)
* Fix path for tools library * Add MotionPlaceholder Add a MotionPlaceholder VirtualLayout, providing placeholder widgets that work in MotionLayout. The first referenced widget will be centered where the placeholder is positioned. If placeholder is wrap_content itself, the size of the widget will be used.
1 parent d619654 commit bf49113

File tree

12 files changed

+613
-14
lines changed

12 files changed

+613
-14
lines changed
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* Copyright (C) 2021 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package androidx.constraintlayout.helper.widget;
17+
18+
import android.annotation.SuppressLint;
19+
import android.content.Context;
20+
import android.util.AttributeSet;
21+
import android.util.SparseArray;
22+
import android.view.ViewParent;
23+
24+
import androidx.constraintlayout.core.widgets.ConstraintWidget;
25+
import androidx.constraintlayout.core.widgets.ConstraintWidgetContainer;
26+
import androidx.constraintlayout.core.widgets.Helper;
27+
import androidx.constraintlayout.core.widgets.Placeholder;
28+
import androidx.constraintlayout.widget.VirtualLayout;
29+
30+
public class MotionPlaceholder extends VirtualLayout {
31+
private static final String TAG = "MotionPlaceholder";
32+
Placeholder mPlaceholder;
33+
34+
public MotionPlaceholder(Context context) {
35+
super(context);
36+
}
37+
38+
public MotionPlaceholder(Context context, AttributeSet attrs) {
39+
super(context, attrs);
40+
}
41+
42+
public MotionPlaceholder(Context context, AttributeSet attrs, int defStyleAttr) {
43+
super(context, attrs, defStyleAttr);
44+
}
45+
46+
public MotionPlaceholder(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
47+
super(context, attrs, defStyleAttr);
48+
}
49+
50+
@SuppressLint("WrongCall")
51+
@Override
52+
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
53+
onMeasure(mPlaceholder, widthMeasureSpec, heightMeasureSpec);
54+
}
55+
56+
@Override
57+
public void onMeasure(androidx.constraintlayout.core.widgets.VirtualLayout layout, int widthMeasureSpec, int heightMeasureSpec) {
58+
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
59+
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
60+
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
61+
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
62+
if (layout != null) {
63+
layout.measure(widthMode, widthSize, heightMode, heightSize);
64+
setMeasuredDimension(layout.getMeasuredWidth(), layout.getMeasuredHeight());
65+
} else {
66+
setMeasuredDimension(0,0);
67+
}
68+
}
69+
70+
@Override
71+
public void updatePreLayout(ConstraintWidgetContainer container,
72+
Helper helper,
73+
SparseArray<ConstraintWidget> map) {
74+
// override to block the ids being replaced
75+
}
76+
77+
@Override
78+
protected void init(AttributeSet attrs) {
79+
super.init(attrs);
80+
mHelperWidget = new Placeholder();
81+
validateParams();
82+
}
83+
}

constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/motion/widget/MotionLayout.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
import androidx.constraintlayout.core.widgets.ConstraintWidgetContainer;
5151
import androidx.constraintlayout.core.widgets.Flow;
5252
import androidx.constraintlayout.core.widgets.Helper;
53+
import androidx.constraintlayout.core.widgets.Placeholder;
5354
import androidx.constraintlayout.motion.utils.StopLogic;
5455
import androidx.constraintlayout.motion.utils.ViewState;
5556
import androidx.constraintlayout.widget.Barrier;
@@ -2512,6 +2513,8 @@ void copy(ConstraintWidgetContainer src, ConstraintWidgetContainer dest) {
25122513
child_d = new androidx.constraintlayout.core.widgets.Guideline();
25132514
} else if (child_s instanceof Flow) {
25142515
child_d = new Flow();
2516+
} else if (child_s instanceof Placeholder) {
2517+
child_d = new Placeholder();
25152518
} else if (child_s instanceof androidx.constraintlayout.core.widgets.Helper) {
25162519
child_d = new androidx.constraintlayout.core.widgets.HelperWidget();
25172520
} else {

constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/widget/ConstraintHelper.java

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -562,18 +562,23 @@ public void loadParameters(ConstraintSet.Constraint constraint, HelperWidget chi
562562
// as this makes changing referenced views tricky at runtime
563563
if (constraint.layout.mReferenceIds != null) {
564564
setReferencedIds(constraint.layout.mReferenceIds);
565-
} else if (constraint.layout.mReferenceIdString != null
566-
&& constraint.layout.mReferenceIdString.length() > 0) {
567-
constraint.layout.mReferenceIds = convertReferenceString(this,
568-
constraint.layout.mReferenceIdString);
565+
} else if (constraint.layout.mReferenceIdString != null) {
566+
if (constraint.layout.mReferenceIdString.length() > 0) {
567+
constraint.layout.mReferenceIds = convertReferenceString(this,
568+
constraint.layout.mReferenceIdString);
569+
} else {
570+
constraint.layout.mReferenceIds = null;
571+
}
569572
}
570-
child.removeAllIds();
571-
if (constraint.layout.mReferenceIds != null) {
572-
for (int i = 0; i < constraint.layout.mReferenceIds.length; i++) {
573-
int id = constraint.layout.mReferenceIds[i];
574-
ConstraintWidget widget = mapIdToWidget.get(id);
575-
if (widget != null) {
576-
child.add(widget);
573+
if (child != null) {
574+
child.removeAllIds();
575+
if (constraint.layout.mReferenceIds != null) {
576+
for (int i = 0; i < constraint.layout.mReferenceIds.length; i++) {
577+
int id = constraint.layout.mReferenceIds[i];
578+
ConstraintWidget widget = mapIdToWidget.get(id);
579+
if (widget != null) {
580+
child.add(widget);
581+
}
577582
}
578583
}
579584
}

constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/widget/ConstraintSet.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1101,7 +1101,7 @@ public void copyFrom(Layout src) {
11011101
mHelperType = src.mHelperType;
11021102
mConstraintTag = src.mConstraintTag;
11031103

1104-
if (src.mReferenceIds != null) {
1104+
if (src.mReferenceIds != null && src.mReferenceIdString == null) {
11051105
mReferenceIds = Arrays.copyOf(src.mReferenceIds, src.mReferenceIds.length);
11061106
} else {
11071107
mReferenceIds = null;
@@ -4798,6 +4798,8 @@ private static void setDeltaValue(Constraint c, int type, String value) {
47984798
break;
47994799
case CONSTRAINT_REFERENCED_IDS:
48004800
c.layout.mReferenceIdString = value;
4801+
// If a string is defined, clear up the reference ids array
4802+
c.layout.mReferenceIds = null;
48014803
break;
48024804
case CONSTRAINT_TAG:
48034805
c.layout.mConstraintTag = value;
@@ -5218,6 +5220,10 @@ private void populateConstraint(Context ctx, Constraint c, TypedArray a, boolean
52185220
"Unknown attribute 0x" + Integer.toHexString(attr) + " " + mapToConstant.get(attr));
52195221
}
52205222
}
5223+
if (c.layout.mReferenceIdString != null) {
5224+
// in case the strings are set, make sure to clear up the cached ids
5225+
c.layout.mReferenceIds = null;
5226+
};
52215227
}
52225228

52235229
private int[] convertReferenceString(View view, String referenceIdString) {

constraintlayout/core/src/main/java/androidx/constraintlayout/core/widgets/ConstraintWidget.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2049,7 +2049,8 @@ public void connect(ConstraintAnchor.Type constraintFrom,
20492049
/**
20502050
* Reset all the constraints set on this widget
20512051
*/
2052-
public void resetAllConstraints() { resetAnchors();
2052+
public void resetAllConstraints() {
2053+
resetAnchors();
20532054
setVerticalBiasPercent(DEFAULT_BIAS);
20542055
setHorizontalBiasPercent(DEFAULT_BIAS);
20552056
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* Copyright (C) 2021 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package androidx.constraintlayout.core.widgets;
18+
19+
import static androidx.constraintlayout.core.widgets.analyzer.BasicMeasure.AT_MOST;
20+
import static androidx.constraintlayout.core.widgets.analyzer.BasicMeasure.EXACTLY;
21+
import static androidx.constraintlayout.core.widgets.analyzer.BasicMeasure.UNSPECIFIED;
22+
23+
import androidx.constraintlayout.core.LinearSystem;
24+
25+
/**
26+
* Simple VirtualLayout that center the first referenced widget onto itself.
27+
*/
28+
public class Placeholder extends VirtualLayout {
29+
30+
public void measure(int widthMode, int widthSize, int heightMode, int heightSize) {
31+
int width = 0;
32+
int height = 0;
33+
int paddingLeft = getPaddingLeft();
34+
int paddingRight = getPaddingRight();
35+
int paddingTop = getPaddingTop();
36+
int paddingBottom = getPaddingBottom();
37+
38+
width += paddingLeft + paddingRight;
39+
height += paddingTop + paddingBottom;
40+
41+
if (mWidgetsCount > 0) {
42+
// grab the first referenced widget size in case we are ourselves in wrap_content
43+
width += mWidgets[0].getWidth();
44+
height += mWidgets[0].getHeight();
45+
}
46+
width = Math.max(getMinWidth(), width);
47+
height = Math.max(getMinHeight(), height);
48+
49+
int measuredWidth = 0;
50+
int measuredHeight = 0;
51+
52+
if (widthMode == EXACTLY) {
53+
measuredWidth = widthSize;
54+
} else if (widthMode == AT_MOST) {
55+
measuredWidth = Math.min(width, widthSize);
56+
} else if (widthMode == UNSPECIFIED) {
57+
measuredWidth = width;
58+
}
59+
60+
if (heightMode == EXACTLY) {
61+
measuredHeight = heightSize;
62+
} else if (heightMode == AT_MOST) {
63+
measuredHeight = Math.min(height, heightSize);
64+
} else if (heightMode == UNSPECIFIED) {
65+
measuredHeight = height;
66+
}
67+
68+
setMeasure(measuredWidth, measuredHeight);
69+
setWidth(measuredWidth);
70+
setHeight(measuredHeight);
71+
needsCallbackFromSolver(mWidgetsCount > 0);
72+
}
73+
74+
@Override
75+
public void addToSolver(LinearSystem system, boolean optimize) {
76+
super.addToSolver(system, optimize);
77+
78+
if (mWidgetsCount > 0) {
79+
ConstraintWidget widget = mWidgets[0];
80+
widget.resetAllConstraints();
81+
widget.connect(ConstraintAnchor.Type.LEFT, this, ConstraintAnchor.Type.LEFT);
82+
widget.connect(ConstraintAnchor.Type.RIGHT, this, ConstraintAnchor.Type.RIGHT);
83+
widget.connect(ConstraintAnchor.Type.TOP, this, ConstraintAnchor.Type.TOP);
84+
widget.connect(ConstraintAnchor.Type.BOTTOM, this, ConstraintAnchor.Type.BOTTOM);
85+
}
86+
}
87+
}

projects/MotionLayoutVerification/app/src/main/java/android/support/constraint/app/VerificationActivity.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ public class VerificationActivity extends AppCompatActivity implements View.OnCl
107107
private static boolean REVERSE = false;
108108

109109

110-
private static final String RUN_FIRST = (true) ? "verification_360" : "bug_005";
110+
private static final String RUN_FIRST = (true) ? "verification_131" : "bug_005";
111111
private final String LAYOUTS_MATCHES = "v.*_.*";
112112

113113
private static String SHOW_FIRST = "";
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<androidx.constraintlayout.motion.widget.MotionLayout xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:app="http://schemas.android.com/apk/res-auto"
4+
xmlns:tools="http://schemas.android.com/tools"
5+
android:id="@+id/root"
6+
android:background="#CFC"
7+
android:layout_width="match_parent"
8+
android:layout_height="match_parent"
9+
app:motionDebug="SHOW_ALL"
10+
app:layoutDescription="@xml/verification_scene_130"
11+
>
12+
13+
<androidx.constraintlayout.helper.widget.MotionPlaceholder
14+
android:id="@+id/p0"
15+
android:layout_width="64dp"
16+
android:layout_height="64dp"
17+
app:layout_constraintTop_toTopOf="parent"
18+
app:layout_constraintBottom_toBottomOf="parent"
19+
app:layout_constraintEnd_toStartOf="parent"/>
20+
21+
<androidx.constraintlayout.helper.widget.MotionPlaceholder
22+
android:id="@+id/p1"
23+
android:layout_width="128dp"
24+
android:layout_height="128dp"
25+
android:background="#777"
26+
android:visibility="invisible"
27+
app:constraint_referenced_ids="view_a"
28+
app:layout_constraintTop_toTopOf="parent"
29+
app:layout_constraintBottom_toBottomOf="parent"
30+
app:layout_constraintStart_toStartOf="parent"
31+
/>
32+
33+
<androidx.constraintlayout.helper.widget.MotionPlaceholder
34+
android:id="@+id/p2"
35+
android:layout_width="128dp"
36+
android:layout_height="256dp"
37+
android:background="#777"
38+
android:visibility="invisible"
39+
app:constraint_referenced_ids="view_b"
40+
app:layout_constraintTop_toTopOf="parent"
41+
app:layout_constraintBottom_toBottomOf="parent"
42+
app:layout_constraintStart_toEndOf="@id/p1"
43+
app:layout_constraintEnd_toStartOf="@id/p3"
44+
/>
45+
46+
47+
<androidx.constraintlayout.helper.widget.MotionPlaceholder
48+
android:id="@+id/p3"
49+
android:layout_width="128dp"
50+
android:layout_height="128dp"
51+
android:background="#777"
52+
android:visibility="invisible"
53+
app:constraint_referenced_ids="view_c"
54+
55+
app:layout_constraintTop_toTopOf="parent"
56+
app:layout_constraintBottom_toBottomOf="parent"
57+
app:layout_constraintEnd_toEndOf="parent" />
58+
59+
<androidx.constraintlayout.helper.widget.MotionPlaceholder
60+
android:id="@+id/p4"
61+
android:layout_width="64dp"
62+
android:layout_height="64dp"
63+
app:layout_constraintTop_toTopOf="parent"
64+
app:layout_constraintBottom_toBottomOf="parent"
65+
app:layout_constraintStart_toEndOf="parent"/>
66+
67+
<TextView
68+
android:id="@+id/info"
69+
android:layout_width="wrap_content"
70+
android:layout_height="wrap_content"
71+
android:text="placeholder test 5"
72+
app:layout_constraintBottom_toBottomOf="parent"
73+
app:layout_constraintEnd_toEndOf="parent"
74+
app:layout_constraintStart_toStartOf="parent"
75+
/>
76+
77+
<TextView
78+
android:id="@+id/view_a"
79+
android:layout_width="0dp"
80+
android:layout_height="0dp"
81+
android:background="#F44336"
82+
android:gravity="center"
83+
android:text="A"
84+
android:textSize="42sp"
85+
tools:layout_editor_absoluteX="156dp"
86+
tools:layout_editor_absoluteY="34dp" />
87+
88+
<TextView
89+
android:id="@+id/view_b"
90+
android:layout_width="0dp"
91+
android:layout_height="0dp"
92+
android:background="#00BCD4"
93+
android:gravity="center"
94+
android:text="B"
95+
android:textSize="42sp"
96+
android:visibility="visible"
97+
tools:layout_editor_absoluteX="156dp"
98+
tools:layout_editor_absoluteY="182dp" />
99+
100+
<TextView
101+
android:id="@+id/view_c"
102+
android:layout_width="0dp"
103+
android:layout_height="0dp"
104+
android:background="#8BC34A"
105+
android:gravity="center"
106+
android:text="C"
107+
android:textSize="42sp"
108+
tools:layout_editor_absoluteX="156dp"
109+
tools:layout_editor_absoluteY="331dp" />
110+
</androidx.constraintlayout.motion.widget.MotionLayout >

0 commit comments

Comments
 (0)