文章目录
android约束布ConstraintLayout
- 官方介绍:https://developer.android.google.cn/training/constraint-layout
- 官方例子:views-widgets-samples/ConstraintLayoutExamples at master · android/views-widgets-samples
说明
- 约束布局中关于控件的大小(也叫视图尺寸)主要有三种:
- wrap_content:自适应控件内容大小
- fixed:固定大小
- match constraints: 填满布局,在xml中就是表现为0dp
常用操作
居中
- 如果想让控件居中,设置bias百分之50即可,如下图
- bias表示控件在水平或垂直方向上的位置比例,必须在水平方向左右或垂直方向上下都有约束并且视图尺寸为fixed或者wrap_content时才会生效
- 利用bias我们就可以把控件放置相应比例的地方,注意bias范围是0.0~1.0
控件按比例显示
- 如果想让控件按比例展示,首先先切换为Match Constraints(即 android:layout_width=“0dp”),如下图
- 然后就可以看到左上角出现三角形
- 点击三角形即可出现设置比例的ratio
- 在xml中则体现为app:layout_constraintDimensionRatio=“w,1:1”,意思就是宽度与原本高度的比例为1:1
- 这里的原本高度就是指如果不加上这一行app:layout_constraintDimensionRatio="w,1:1"控件所呈现出来的高度
- 如下图,控件原本高度是三行文字的高度(这里假设高度是20dp)
- 现在加上这一行app:layout_constraintDimensionRatio="w,2:1"之后宽度就是原本高度的两倍了(为40dp,这里不要误以为"w,2:1"是最终宽度与最终高度比为2:1,而应该是最终宽度与原本高度比为2:1)
- 再举个例子吧,我们有时候希望控件宽度是屏幕宽度,而宽高按16:9的比例动态计算,那么我们就可以如下设置
- layout_constraintDimensionRatio非常适合那种一边长度已经确定了,而另一边想要按比例计算的控件尺寸
- 如果想让最终宽高比完全按照设定的比例,那么就需要控件的宽高都为match constraints,即
<View
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="16:9" <!-- 当layout_width和layout_height都为match_constraints时,16:9和h,16:9是一样的-->
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
- 当只有layout_width为match constraints时,layout_constraintDimensionRatio的"16:9"和"w,16:9"是一样的,同理,当只有layout_height为match constraints时,layout_constraintDimensionRatio的"16:9"和"h,16:9"是一样的
- 当layout_width和layout_height都为match constraints时,“16:9”和"h,16:9"是一样的,都是宽度占满后,宽度与高度比例为16:9,即高度随着宽度变化而变化
- 一般来说,比例前写了“w”,表明宽度是随着高度按比例变化的;同理,“h”则表明高度是随着宽度按比例变化的
- 当宽或高为match_parent或fixed,也就是一边基本已知的情况下,match constraints的这一边的计算规则如下:
boolean verticalDimensionKnown = verticalBehavior == DimensionBehaviour.MATCH_PARENT || verticalBehavior == DimensionBehaviour.FIXED;
boolean horizontalDimensionKnown = horizontalBehavior == DimensionBehaviour.MATCH_PARENT || horizontalBehavior == DimensionBehaviour.FIXED;
boolean horizontalUseRatio = horizontalMatchConstraints && widget.mDimensionRatio > 0.0F;
boolean verticalUseRatio = verticalMatchConstraints && widget.mDimensionRatio > 0.0F;
if (horizontalUseRatio && verticalDimensionKnown) {
ratio = widget.mDimensionRatio;
width = (int)(0.5F + (float)height * ratio);
} else if (verticalUseRatio && horizontalDimensionKnown) {
ratio = widget.mDimensionRatio;
height = (int)(0.5F + (float)width / ratio);
}
视图尺寸为match constraints
- 当视图尺寸为match constraints时,你可以设置控件其他属性
- layout_constraintWidth_default:spread是默认行为,wrap则可以使得控件内容区长度不会超出屏幕
- layout_constraintWidth_min:控件最小宽度
- layout_constraintWidth_max:控件最大宽度
- 当你使用margin和wrap_content时,你会发现控件内容区过长时会超出屏幕,如下
- 上面红框的地方明显不是我们想要的,为了解决这个问题,我们可以改为
- 这样设置后就不会超出屏幕了
- 注意,layout_constraintWidth_default=“wrap” 后期已过时,不再推荐使用
layout_constraintWidth_default=“wrap” is deprecated.
Use layout_width=“WRAP_CONTENT” and layout_constrainedWidth=“true” instead. - 显然上面的问题只是解决了TextView在宽度为铺满的情况下的超出屏幕的问题,但是如果我们想TextView是wrap_content的,内容过长时又不超出屏幕呢
- 这就需要再配合上bias了
- 我们先看内容短的情况
- 再看内容长的情况
删除约束
- 在windows上通过按住Ctrl再点击约束即会变成红色,此时就可以删除约束了
屏障约束
- 首先右键添加一个barrier
- 添加完后在component tree窗口可以看到该组件
- 然后就可以把放在屏障里的控件拖拽到barrier里面,如图
- 最后设置下方向,然后拖动下放进barrier里的控件试试看
链
-
如果想实现如下效果,那么就要使用链
-
框选要组成链的控件后,右键选择chains即可增加水平链或垂直链
-
之后可以选择对齐方式
-
在这里可以选择不同的style
-
Spread:视图是均匀分布的(在考虑外边距之后)。这是默认值。
-
Spread inside:第一个和最后一个视图固定在链两端的约束边界上,其余视图均匀分布。
-
Weighted:当链设置为 spread 或 spread inside 时,您可以通过将一个或多个视图设置为“match constraints”(0dp) 来填充剩余空间。默认情况下,设置为“match constraints”的每个视图之间的空间均匀分布,但您可以使用 layout_constraintHorizontal_weight 和 layout_constraintVertical_weight 属性为每个视图分配重要性权重。如果您熟悉线性布局中的 layout_weight 的话,就会知道该样式与它的原理是相同的。因此,权重值最高的视图获得的空间最大;相同权重的视图获得同样大小的空间。
-
Packed:视图打包在一起(在考虑外边距之后)。 然后,您可以通过更改链的头视图偏差调整整条链的偏差(左/右或上/下)
动画效果
- 在约束布局中可以使用 ConstraintSet 和 TransitionManager 为尺寸和位置元素的变化添加动画效果,注意这种效果只针对位置和尺寸,不包含颜色等其他
- 而且这种效果的实现需要写两个布局文件,分别作为开始和结束关键帧
ActivityConstraintBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityConstraintBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
}
public void onClickAnim(View view) {
if (view.isSelected()) {
view.setSelected(false);
anim(R.layout.activity_constraint);
} else {
view.setSelected(true);
anim(R.layout.activity_constraint_keyframe_two);
}
}
/**
* 这种效果只针对位置和尺寸,不包含颜色等其他
* @param layoutId
*/
private void anim(@LayoutRes int layoutId) {
ConstraintSet constraintSet = new ConstraintSet();
constraintSet.load(this, R.layout.activity_constraint_keyframe_two);
TransitionManager.beginDelayedTransition(binding.getRoot());
constraintSet.applyTo(binding.getRoot());
}
layout_goneMarginXXX
- 你是否遇到过在约束布局中layout_marginBottom设置无效的情况
- 比如
- 此时你会发现text1与text2之间并没有间隔10dp,有的人可能会说直接在text2中设置layout_marginTop即可,在text1与text2皆可见的时候确实可以这么做,但是如果在隐藏text1时,我们想让text2的layout_marginTop自动失效呢,这可咋办
- 我们可以借助layout_goneMarginXXX,如下
- 这样在text1和text2都设置为可见时,它们直接会间隔10dp;在text1隐藏时,layout_goneMarginTop会生效而layout_marginTop则会失效,从而达到我们想要的效果
角标位置
- 我们经常需要某个view(称为anchor)的不同位置放置一些角标,如下图
<!-- 在anchor视图右上角-->
<View
...
app:layout_constraintBottom_toTopOf="@+id/anchor"
app:layout_constraintEnd_toEndOf="@+id/anchor"
app:layout_constraintStart_toEndOf="@+id/anchor"
app:layout_constraintTop_toTopOf="@+id/anchor" />
<!-- 在anchor视图右下角-->
<View
...
app:layout_constraintStart_toEndOf="@id/anchor"
app:layout_constraintEnd_toEndOf="@id/anchor"
app:layout_constraintTop_toBottomOf="@id/anchor"
app:layout_constraintBottom_toBottomOf="@id/anchor" />
<!-- 在anchor视图左上角-->
<View
...
app:layout_constraintStart_toStartOf="@id/anchor"
app:layout_constraintEnd_toStartOf="@id/anchor"
app:layout_constraintTop_toTopOf="@id/anchor"
app:layout_constraintBottom_toTopOf="@id/anchor" />
<!-- 在anchor视图左下角-->
<View
...
app:layout_constraintStart_toStartOf="@id/anchor"
app:layout_constraintEnd_toStartOf="@id/anchor"
app:layout_constraintTop_toBottomOf="@id/anchor"
app:layout_constraintBottom_toBottomOf="@id/anchor" />
右侧跟随左侧,但宽度不够时右侧显示完整,左侧被挤压
- 如下效果
- 首先可以将左侧和右侧绑成链,style设置为packed,然后为左侧设置app:layout_constrainedWidth="true"和app:layout_constraintHorizontal_bias="0"即可
- 示例代码如下
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/left"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:singleLine="true"
app:layout_constrainedWidth="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/right"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" />
<TextView
android:id="@+id/right"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:background="@drawable/bg_label"
android:paddingLeft="6dp"
android:paddingRight="6dp"
android:text="群聊"
app:layout_constraintBottom_toBottomOf="@id/left"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/left"
app:layout_constraintTop_toTopOf="@id/left" />
</androidx.constraintlayout.widget.ConstraintLayout>
参考
- https://developer.android.google.cn/training/constraint-layout
- (8条消息) ConstraintLayout中子布局wrap_content超出屏幕处理方案_TomCat0916的博客-CSDN博客
- (77条消息) Android开发之ConstraintLayout(约束布局)一个控件位于一个控件右上角类似RelativeLayout实现效果_xiayiye5的博客-CSDN博客