ConstraintLayout 填坑记

疫情概要

国内疫情

6月4日0—24时,31个省(自治区、直辖市)和新疆生产建设兵团报告新增确诊病例5例,均为境外输入病例(上海4例,四川1例);无新增死亡病例;无新增疑似病例。

国际疫情

截至6月4日,国际上214个国家/地区、日本领海邮轮上和其他商船(不含港澳台地区)累计确诊6332225例新型冠状病毒肺炎病例,累计死亡378222例;6月4日新增129280例确诊病例,新增4842例死亡病例。无新增报告病例的国家和地区。(来源:世界卫生组织)

前言

本篇记录小编在使用ConstrintLayout组件开发过程中遇到的“坑”。

ViewStub高度异常

场景

ViewStub位于多个子控件之间,宽度设置为 0dp,高度设置为 44dp,start和end都为 parent,即宽为铺满父容器 ConstraintLayout;而Y轴则指定为其他两个子控件之间,即指定了 layout_constraintTop_toBottomoflayout_constraintBottom_toTopf

但实际运行调用ViewStub.inflate后的结果是ViewStub会占满自ViewStub开始的所有空间,即ViewStub下方的控件都不会显示。

解决方法

ViewStub中有这样一个属性:inflatedId,官方文档中的解释如下:

android:inflatedId—Overrides the id of the inflated View with this value

即实际加载的view的ID将会设置为inflatedId所代表的值。

将该属性的值,与ViewStub的android:id的值设置的完全一样,即可解决问题。

通过Android Studio自带的Layout Inspector工具查看ViewStub所在的界面,可以发现ViewStub.inflate后不会再有ViewStub标签,只有android:layout所指向的布局内容,所以使用在ViewStub上的约束必须通过相同的ID获取到。

ConstraintSet相关

其中对于ConstraintLayout.LayoutParams有一段特别的说明:

This class contains the different attributes specifying how a view want to be laid out inside a ConstraintLayout. For building up constraints at run time, using ConstraintSet is recommended.

翻译过来即:该类包含的属性具体说明了子控件在ConstraintLayout内的布局位置。ConstraintLayout不推荐在运行时用LayoutParams来设置属性,而是用ConstraintSet来设置参数。

applyTo

场景

ConstraintLayout的子控件通过View.setVisibility(@Visibility int visibility)修改过可见性后,调用ConstraintSet.applyTo(ConstraintLayout constraintLayout) 后,子控件的可见性会恢复成初始状态。

解决方法

调用View.setVisibility(@Visibility int visibility)后再添加ConstraintSet的更新:ConstraintSet.setVisibility(int viewId, int visibility) 其中viewId:调用View.setVisibility()的id。

示例代码:ConstraintActivity.java

分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
/**
* Apply the constraints to a ConstraintLayout.
*
* @param constraintLayout to be modified
*/
public void applyTo(ConstraintLayout constraintLayout) {
applyToInternal(constraintLayout);
constraintLayout.setConstraintSet(null);
}
/**
* Used to set constraints when used by constraint layout
*/
void applyToInternal(ConstraintLayout constraintLayout) {
int count = constraintLayout.getChildCount();
HashSet<Integer> used = new HashSet<Integer>(mConstraints.keySet());
for (int i = 0; i < count; i++) {
View view = constraintLayout.getChildAt(i);
int id = view.getId();
if (mConstraints.containsKey(id)) {
used.remove(id);
Constraint constraint = mConstraints.get(id);
if (constraint.mHelperType != UNSET) {
switch (constraint.mHelperType) {
case BARRIER_TYPE:
Barrier barrier = (Barrier) view;
barrier.setId(id);
barrier.setReferencedIds(constraint.mReferenceIds);
barrier.setType(constraint.mBarrierDirection);
ConstraintLayout.LayoutParams param = constraintLayout
.generateDefaultLayoutParams();
constraint.applyTo(param);
break;
}
}
ConstraintLayout.LayoutParams param = (ConstraintLayout.LayoutParams) view
.getLayoutParams();
constraint.applyTo(param);
view.setLayoutParams(param);
//此处会调用setVisibility刷新控件可见性
view.setVisibility(constraint.visibility);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
view.setAlpha(constraint.alpha);
view.setRotation(constraint.rotation);
view.setRotationX(constraint.rotationX);
view.setRotationY(constraint.rotationY);
view.setScaleX(constraint.scaleX);
view.setScaleY(constraint.scaleY);
view.setPivotX(constraint.transformPivotX);
view.setPivotY(constraint.transformPivotY);
view.setTranslationX(constraint.translationX);
view.setTranslationY(constraint.translationY);
if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
view.setTranslationZ(constraint.translationZ);
if (constraint.applyElevation) {
view.setElevation(constraint.elevation);
}
}
}
}
}
for (Integer id : used) {
Constraint constraint = mConstraints.get(id);
if (constraint.mHelperType != UNSET) {
switch (constraint.mHelperType) {
case BARRIER_TYPE:
Barrier barrier = new Barrier(constraintLayout.getContext());
barrier.setId(id);
barrier.setReferencedIds(constraint.mReferenceIds);
barrier.setType(constraint.mBarrierDirection);
ConstraintLayout.LayoutParams param = constraintLayout
.generateDefaultLayoutParams();
constraint.applyTo(param);
constraintLayout.addView(barrier, param);
break;
}
}
if (constraint.mIsGuideline) {
Guideline g = new Guideline(constraintLayout.getContext());
g.setId(id);
ConstraintLayout.LayoutParams param = constraintLayout.generateDefaultLayoutParams();
constraint.applyTo(param);
constraintLayout.addView(g, param);
}
}
}

通过上面源码可知:applyTo内部调用了applyToInternal方法,内部又调用了view.setVisibility(constraint.visibility);使用ConstraintSet内保存的值进行刷新。

ConstraintSet主要是对子控件的位置进行控制调整,包括:visibility、alpha、rotation、rotationX、rotationY、scaleX、scaleY、transformPivotX、transformPivotY、translationX、translationY、elevation。

所以不是通过ConstraintSet更新的这些属性值,在调用applyTo后都不会生效。

坚持原创技术分享,您的支持是对我最大的鼓励!