学习笔记(三) ConstraintLayout的使用详解
Tip1、注意:成组的控件要依赖头部控件约束,因为当头部控件改变时其他的组内控件也会跟着变化,比如改变头部控件的Margin值,组内控件也会跟着相应的移动。
1)、控件依赖其他控件居中(B依赖A)
只需要上边依赖该控件的上边,下边依赖该控件的下边即可
app:layout_constraintBottom_toBottomOf="@id/tv_a"
app:layout_constraintTop_toTopOf="@id/tv_a"
复制代码
2)、控件上下对齐且高度相等(B依赖A,且要求B的高度等于A)
相对简单的可能写死A和B的高度,但是有更优雅的写法。我们写死A的宽高都是100dp,那么B要跟A高度相等应该怎么写呢?
android:layout_height="0dp" //高度写成0dp即可
app:layout_constraintLeft_toRightOf="@id/tv_a"
app:layout_constraintTop_toTopOf="@id/tv_a"
复制代码
3)、实现头像嵌入背景图的效果
头像有一半要嵌入背景图中,以前常规的做法可能计算头像的高度,然后设置一个padding。现在在ConstraintLayout中就很简单。
app:layout_constraintBottom_toBottomOf="@id/tv_a"
app:layout_constraintLeft_toLeftOf="@id/tv_a"
app:layout_constraintRight_toRightOf="@id/tv_a"
app:layout_constraintTop_toBottomOf="@id/tv_a"
复制代码
4)、权重
类似线性布局如何实现?当前是左右权重,写代码的时候一定要注意指定每个控件的左右边界才能生成权重。
背景A
android:layout_width="0dp"
app:layout_constraintHorizontal_weight="2" //权重
app:layout_constraintLeft_toLeftOf="parent" //左边界
app:layout_constraintRight_toLeftOf="@id/bt_b" //右边界
app:layout_constraintTop_toTopOf="parent"
复制代码
背景B
android:layout_width="0dp"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintLeft_toRightOf="@id/bt_a"
app:layout_constraintRight_toLeftOf="@id/bt_c"
app:layout_constraintTop_toTopOf="parent"
复制代码
背景C
android:layout_width="0dp"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintLeft_toRightOf="@id/bt_b"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
复制代码
写垂直方向权重一样,一定要指定每个控件的上下边界, app:layout_constraintVertical_weight
5)、文字的基准线对齐
TextView中文字有基准线的概念,当我们想要二个Textview的文字按基准线对齐时,往往比较困难,但现在在ConstrainLayout中很简单。使用layout_constraintBaseline_toBaselineOf
<com.google.android.material.textview.MaterialTextView
android:id="@+id/bt_a"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/black"
android:gravity="center"
android:text="99"
android:textColor="@color/white"
android:textSize="100sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/bt_b"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/black"
android:text="%"
android:textColor="@color/white"
android:textSize="30sp"
app:layout_constraintBaseline_toBaselineOf="@id/bt_a" //基准线对齐
app:layout_constraintLeft_toRightOf="@id/bt_a" />
复制代码
%号以99的文字基准线对齐了
6)、角度定位
角度定位不同于上面讲到的XY坐标定位,角度定位需要三个条件:圆心、角度、半径。
app:layout_constraintCircle="@id/iv_a" //以哪个View为圆心
app:layout_constraintCircleAngle="45" //角度(12点方向为0度,顺时针方向旋转)
app:layout_constraintCircleRadius="160dp" //半径
复制代码
注意:写的时候控件会”变红”,提示缺少约束This view is not contrained
,在”变红”的控件或者在最顶层的控件添加 tools:ignore="MissingConstraints"
即可。
7)、文本控件适应其他控件的宽或者高
比如下图的情况,希望个人简介的文本框与头像的宽度一致,以往的做法我们会把头像和文本的宽度写死并一样
现在在constraintLayout中一行代码就可以实现了app:layout_constrainedWidth="true"
,这行代码写在TextView中,如下图所示。
上面的示例是以某个控件来约束TextView的宽度,TextView的宽度也可以受二个控件约束(见下条)。
相应的也有app:layout_constrainedHeight="true"
在高度上进行约束。
8)、位置的百分比分布
假如是做一个聊天的界面,不加任何约束就会出现下图的情况:
首先,我们希望文本不要左右超出,需要加上app:layout_constrainedWidth="true"
然后当文字变少的时候会出现下面的情况:
但是聊天界面我们希望文字一直都靠左的,这时候就需要这条属性app:layout_constraintHorizontal_bias="0"
, 这个属性的取值范围是0-1,0是最左边,1是最右边,默认是0.5在中间。
9)、GoneMargin属性
当依赖的控件可见性被设置为Gone时,我们希望二者有点间距,就可以用到这些属性
app:layout_goneMarginBottom="10dp"
app:layout_goneMarginTop="10dp"
app:layout_goneMarginLeft="10dp"
app:layout_goneMarginRight="10dp"
app:layout_goneMarginStart="10dp"
app:layout_goneMarginEnd="10dp"
复制代码
10)、约束链风格(chainStyle)
约束链分水平方向app:layout_constraintHorizontal_chainStyle
与垂直方向 app:layout_constraintVertical_chainStyle
有三种值:packed
,spread
,spread_inside
,其中spread
是默认的,packed
是打包可以让控件联动,spread_inside
内部扩散,一般用到的就是packed
。特别注意的一点是:约束链风格必须写在横向或者纵向的第一个控件上。
比如下面的示例,把头像和简介packed
,然后设置头像的MarginTop,头像和简介就能一起移动。
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.2" //上面四条属性本来在中央的,加了这个变成了百分比
app:layout_constraintVertical_chainStyle="packed" //第一个控件上加packed
复制代码
11)、图片的比例适配
当碰到binner图时,经常会碰到如下的场景,UI给的binner图的宽高比为1.4,要求显示的控件宽高比也是1.4,不然图片就会变形。往往这种情况下,宽度是确定的,但是高度需要动态计算,需要先算出手机的宽度,然后动态算出高度。最后的解决办法就是写自定义控件,现在在ConstraintLayout中一行代码搞定app:layout_constraintDimensionRatio="1.4"
值可以写宽高比的计算结果1.4
,也可以写百分比7:5
代码
android:layout_width="match_parent" //宽度确定的
android:layout_height="0dp" //高度0dp动态计算
app:layout_constraintDimensionRatio="1.4" //宽高比的计算结果1.4/5:7
复制代码
显示图片未变形
详解: 使用这条属性的时候必须至少有一条边的属性是0dp,也就是另一条边是需要动态计算的,假如宽高都是0dp,则必须指定哪条边是动态计算的。
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="W,2:1" //写在前面的W是分母,必须用比例的写法,不能直接写计算结果
复制代码
另一种情况:
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="H,2:1" //写在前面的H是分母,必须用比例的写法,不能直接写计算结果
复制代码
12)、Guideline使用
Guideline辅助控件就是画一条辅助线,让其他控件可以通过它定位来对齐。见如下示例代码:
<androidx.constraintlayout.widget.Guideline //没有包裹关系,就是在图上生成辅助线
android:id="@+id/gl"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_begin="120dp" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/tv1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="我爱我家"
android:textSize="33sp"
app:layout_constraintEnd_toStartOf="@id/gl" //以辅助线来定位
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.textview.MaterialTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="我爱"
android:textSize="33sp"
app:layout_constraintEnd_toStartOf="@id/gl" //以辅助线来定位
app:layout_constraintTop_toBottomOf="@id/tv1" />
复制代码
Guideline的参数:
android:orientation="vertical/horizontal"
//生成水平方向还是垂直方向的辅助线
app:layout_constraintGuide_begin="120dp"
//以开始为起点,距离120dp画辅助线
app:layout_constraintGuide_end="120dp"
//以终点为起点,距离120dp画辅助线
app:layout_constraintGuide_percent="0.3"
//从开始的30%作为起点画辅助线,0-1,可以写负数
13)、Group的使用
使用ConstraintLayout后,控件越来越扁平化,有时候我们需要Gone或者Visibil多个控件就会显得很麻烦(它们没有了父view),Group就是因此应运而生的.
<androidx.constraintlayout.widget.Group
android:id="@+id/group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="tv1,tv2" /> //就是这行属性
复制代码
当需要Gone或者Visibile时候,我们给Group设置就可以了
14)、Layer的使用
layer是让控件统一执行动画,避免单一执行的麻烦
<androidx.constraintlayout.helper.widget.Layer
android:id="@+id/layer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="tv1,tv2" />
复制代码
如果报红,加上tools:ignore="MissingConstraints"
执行动画:
layer.rotation = 45f
layer.translationY = 100f
layer.translationX = 100f
复制代码
15)、Barrier的使用
Barrier是栅栏的意思,主要解决的问题就是多个不定长控件前面或者后面画一条基准线问题。
比如下面二个TextView不定长,但是我们希望二个Textview后面的Imageview竖直对齐
<androidx.constraintlayout.widget.Barrier
android:id="@+id/barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="end" //组控件的前面还是后面生成栅栏"start""end"
app:constraint_referenced_ids="tv1,tv2" /> //组控件
<com.google.android.material.textview.MaterialTextView
android:id="@+id/tv1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="60dp"
android:text="我爱我家我家我家" //不定长文本一
android:textSize="33sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/tv2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="60dp"
android:text="我爱" //不定长文本二
android:textSize="33sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv1" />
<com.google.android.material.imageview.ShapeableImageView
android:layout_width="60dp"
android:layout_height="60dp"
android:background="@color/black"
app:layout_constraintStart_toEndOf="@id/barrier" //通过栅栏来定位
app:layout_constraintTop_toTopOf="@id/tv1" />
<com.google.android.material.imageview.ShapeableImageView
android:layout_width="60dp"
android:layout_height="60dp"
android:background="@color/black"
app:layout_constraintStart_toEndOf="@id/barrier" //通过栅栏来定位
app:layout_constraintTop_toTopOf="@id/tv2" />
复制代码
16)、Flow的使用
Flow网格布局
<androidx.constraintlayout.helper.widget.Flow
android:id="@+id/flow"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:background="@color/colorAccent"
android:orientation="horizontal" //网格是水平方向还是竖直方向
app:flow_wrapMode="chain" //对齐模式:aligned(如果超出换行对齐),chain(链式状态)
app:flow_verticalGap="16dp" //垂直方向的间距
app:flow_horizontalGap="16dp" //水平方向的间距
app:constraint_referenced_ids="view1,view2,view3,view4,view5,view6"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<View
android:id="@+id/view1"
android:layout_width="80dp"
android:layout_height="80dp"
android:background="@color/colorPrimaryDark" />
<View
android:id="@+id/view2"
android:layout_width="80dp"
android:layout_height="80dp"
android:background="@color/colorPrimaryDark" />
<View
android:id="@+id/view3"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_margin="8dp"
android:background="@color/colorPrimaryDark" />
<View
android:id="@+id/view4"
android:layout_width="80dp"
android:layout_height="80dp"
android:background="@color/colorPrimaryDark" />
<View
android:id="@+id/view5"
android:layout_width="80dp"
android:layout_height="80dp"
android:background="@color/colorPrimaryDark" />
<View
android:id="@+id/view6"
android:layout_width="80dp"
android:layout_height="80dp"
android:background="@color/colorPrimaryDark" />
复制代码
17)、自定义ConstraintHelper
有时候我们想对成组的控件做特殊处理,然后官方又没有提供,这个时候我们就可以自定义ConstraintHelper。比如下面让一组控件做特殊的动画:
class CircularRevealHelper(context: Context, attrs: AttributeSet) : ConstraintHelper(context, attrs) {
override fun updatePostLayout(container: ConstraintLayout) {
super.updatePostLayout(container)
//特别注意取view的时候不要用ConstraintHelper中的 "protected int[] mIds = new int[32];"来遍历,
//要用getReferencedIds()方法来取实际的添加进来的控件。
referencedIds.forEach {
val view = container.getViewById(it)
val radius = hypot(view.width.toDouble(), view.height.toDouble()).toInt()
//每个控件播放动画
ViewAnimationUtils.createCircularReveal(view, 0, 0, 0f, radius.toFloat())
.setDuration(2000L)
.start()
}
}
}
复制代码
18)、代码中修改属性
在以前都是通过Layoutparams来修改的,在ConstraintLayout中不一样,是通过ConstraintSet
val constraintSet = ConstraintSet().apply {
clone(constraintLayout)
connect(
R.id.twitter,
ConstraintSet.BOTTOM,
ConstraintSet.PARENT_ID,
ConstraintSet.BOTTOM
)
}
constraintSet.applyTo(constraintLayout)
复制代码
获取整个布局的约束集合
val constraintSet = ConstraintSet().apply {
isForceId = false
clone(this@ConstraintSetX,
R.layout.activity_constraint_end
)
}
//一行代码添加布局修改的过渡动画
TransitionManager.beginDelayedTransition(constraintLayout)
constraintSet.applyTo(constraintLayout)
复制代码