ConstraintLayout 相信大家都不陌生,目前最新版的Android Studio 默认的布局方式就是ConstraintLayout
一些复杂的界面可以使用ConstraintLayout实现而不需要再像以前一样嵌套很多布局,这样能提升性能。
所以啊,别说了,学习吧…
一、说明
这里介绍一些比较难理解的内容,所以如果是初学者有些我这里没有介绍的大家可以先阅读官方文档和一些大神写的帖子:
然后有些小伙伴就激动了, 官方文档,是英文啊 .,额…其实吧我觉得那些英文不会太难啦,再说了现在不是有很多xx翻译嘛。
还有一些同学啊,看完了官方文档,回来就说。我看完就懂了…出门右转,再见,哼。。
现在Android Studio 提供可视化操作(拖拖拽拽),郭霖大神写的 ConstraintLayout完全解析 ,写的很明白。
好了,开始介绍了
二、相对定位 (Relative positioning)
属性列表:
- layout_constraintLeft_toLeftOf
- layout_constraintLeft_toRightOf
- layout_constraintRight_toLeftOf
- layout_constraintRight_toRightOf
- layout_constraintTop_toTopOf
- layout_constraintTop_toBottomOf
- layout_constraintBottom_toTopOf
- layout_constraintBottom_toBottomOf
- layout_constraintBaseline_toBaselineOf
- layout_constraintStart_toEndOf
- layout_constraintStart_toStartOf
- layout_constraintEnd_toStartOf
- layout_constraintEnd_toEndOf
看一张官方的图,很好理解
这里就不一一解释了,拿 layout_constraintLeft_toRightOf来简单介绍一下:
假如有一个控件 button1 使用 layout_constraintLeft_toRightOf = “@+id/button2” 意思就是button1左边约束于button2的右边(在控件上可以看到button1左边有一条约束线连到button2右边),也可以理解成相对布局的toRightof。
三、边距(Margins)
有以下属性:
- android:layout_marginStart
- android:layout_marginEnd
- android:layout_marginLeft
- android:layout_marginTop
- android:layout_marginRight
- android:layout_marginBottom
使用方法就不多介绍了,注意一点,Margins的值必须是非负的 Dimension( 尺寸值 )
这里主要介绍 (Margins when connected to a GONE widget),当你对一个 visibility 是View.GONE的控件使用Margin的时候。首先属性有:
- layout_goneMarginStart
- layout_goneMarginEnd
- layout_goneMarginLeft
- layout_goneMarginTop
- layout_goneMarginRight
- layout_goneMarginBottom
一般这些属性是与上面的Margin正常属性一起使用的,或者说这些属性就是为了防止当你Margin连接的那个控件GONE的时候出现预料之外的事情。
比如说如下图两个button ,宽度设置为100dp 。
可以看到B有属性android:layout_marginStart=“32dp” ,A有属性android:layout_marginStart=“16dp” ,如果A此时GONE了,会发生什么呢,现在我为A添加属性 android:visibility=“gone” 。结果如下图
会发现B移动了,只有MarginStart只有32dp了,这是我们不想看到的。
其实这种情况主要发生在Java代码编写的时候,而不是直接给个GONE属性(没必要)。
我们不希望控件A的消失与否影响控件B,这个时候就可以用到 layout_goneMarginStart 了,我们在B属性里加上app:layout_goneMarginStart=“132dp” ,就可以看到B纹丝不动了。( 额 …这里还是显示32 dp,没有显示成132dp)
总结一下:其实这些属性就是配合正常的Margin设置属性一起使用,防止某个控件GONE之后影响布局。单独用的话没有意义。
还有一点,一个控件GONE之后,并不是直接从布局完全消失。从上面的设计图中其实可以看出,GONE之后该控件只是(类似)缩成一个点了,当然margin也是会消失的。
看下官方的图
四、偏向(Bias)
Bias有以下两个属性:
- layout_constraintHorizontal_bias
- layout_constraintVertical_bias
先看张很厉害的图
惊不惊喜,意不意外,哈哈。。
这个TextView有下面几条属性:
- app:layout_constraintBottom_toBottomOf=“parent”
- app:layout_constraintEnd_toEndOf=“parent”
- app:layout_constraintStart_toStartOf=“parent”
- app:layout_constraintTop_toTopOf=“parent”
这其实是矛盾的,控件左边约束于parent,右边也是约束于parent,那到底是靠左边还是右边呢,上面已经有答案了。我哪也不靠。
这种情况默认会变成bias,而且默认不写bias的两个属性意味着都等于0.5 即不偏向任何一边,居中。
现在我给它加上bias属性
- app:layout_constraintHorizontal_bias=“0.1”
- app:layout_constraintVertical_bias=“0.05”
就变成下图所示了
总结: Bias当控件两边(上下或者左右)都有约束的时候,默认为bias约束,可以使用bias的两个属性来控制偏向哪边。
五、尺寸限制(Dimensions constraints)
ConstraintLayout的尺寸限制
ConstraintLayout自身有以下最大最小尺寸限制:
- android:minWidth
- android:minHeight
- android:maxWidth
- android:maxHeight
很好理解就不多解释了,只是注意这是ConstraintLayout的属性。
控件的尺寸限制
控件的尺寸可以通过设置属性android:layout_width 和 android:layout_height 来控制,一般有以下三种值:
- 具体尺寸(如100dp)
- WRAP_CONTENT 控件的自身大小
- 0dp ,就是等于"MATCH_CONSTRAINT"
第一种,第二种就不说了
说下“0dp”。也就是 MATCH_CONSTRAINT ,它的功能有点像 MATCH_PARENT,但是官方建议使用 MATCH_CONSTRAINT而不要使用 MATCH_PARENT。
要注意的是控件会和约束控件一样宽(高),并不只是和ConstraintLayout一样宽(高),比如以下情况
<TextView
android:id="@+id/textView2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="TextView"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@+id/textView"
app:layout_constraintStart_toStartOf="@+id/textView"
app:layout_constraintTop_toBottomOf="@+id/textView"/>
可以看到,textView并没有撑满整个屏幕,只是和“hello world !”一样宽而已,因为 “hello world !”控件是它的约束控件。
六、比例(Ratio)
可以用比例去定义View的宽高。
为了做到这一点,需要以下条件
- 至少一个约束尺寸设置为0dp(即MATCH_CONSTRAINT)
- 为属性layout_constraintDimentionRatio设置比例
例如:
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="2:1"
android:text="Hello World !"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
比例可以用以下两种方式表示:
- 浮点数,代表宽高比的比值(如2, 1.5)
- 宽:高,这种形式(如以上例子)
上面讲的是宽高 只有一个被设置为0dp , 当然两个都为0dp也是可以的。
<TextView
android:id="@+id/textView"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="H,1:1"
android:text="Hello World !"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
宽高都设置为0dp,如果没有设置比例属性,那么应该是撑满整个布局的。可以看到,这里比例设置成了"H,1:1",控件的宽高比为1:1 。
理解一下,当只有一个值为0dp的时候,设置比例就是为了控制设置为0dp的高(宽),所以宽高比是以有固定值得为参考。
如第一个例子,高设置为0dp,宽为自身宽度,所以宽高比(注意,这里的Ratio比例都是 宽:高)比例2:1 意思就可以理解成把高设置为宽的1/2。
那么问题来了,我宽和高都是0dp,怎么搞。不要捉急,Google就提供了在比例前面加一个字母来说明你要设置 哪个属性,‘W’或者‘H’ 。使用的时候用逗号‘,’与比例之间隔开,如"W,1:1"。
再看下第二个例子,其实意思就是我要设置高,那自然以宽为基准设置宽高比为1:1,也就是设置高和宽一样的尺寸。
那假如该例子中,我设置成 “W,1:1”,会发生什么?来看下
恩?我,我刀呢,。欺负我不懂1:1么 。 好好好,先把刀放下,听我慢慢道来。
这里呢,设置成 “W,1:1”,意思就是我要设置宽,宽高比为1:1 ,所以自然是以高为基准,然后宽显然不能和高一样的尺寸啊。
所以就显示成这个样子,因为整个屏幕宽也不可能有和高一样的尺寸。
换个比例验证一下就很好理解了,把比例改为“W,1:2”,看效果
总结一下:
- 首先最重要的 比例都是宽:高 。
- 如果只有一项为0dp,那么就是以另一项为基准,设置该项的尺寸;
- 如果两项都为0dp,那么在比例前加‘W’,或者‘H’,来表示设置该项,以另一项为基准。
七、链条(Chains)
什么是链条
通俗的来理解就是把控件和控件之间用链条连接在一起,组成一个类似控件组,一起控制。
创建链条
这里我先说下我一开始想创建一个链条的时候是怎么操作的。
当时我就在可视化上拖拽,想直接把链条拉出来,然后就发现怎么拉都拉不出来链条,当时我郁闷死了。然后我发现链条原理其实就是类似“ 你连我,我连你 ”,然后我又开始拉(有方向了嘛),“ 你连我 ”嗯,可以拉出一条约束,到“我连你”的时候死活拉不上去,因为那个点发出了一条约束,拉不上了。额…好吧。。
说这个其实就是想说,一些复杂一点的约束,拖拖拽拽很难搞定,还是写xml吧。
好了,接下来创建链条,其实原理就是我上面说的“你连我,我连你”,所以就很简单了
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="244dp"
android:text="Hello World !"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/textView2"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="244dp"
android:text="TextView"
android:textSize="16sp"
app:layout_constraintEnd_toStartOf="@id/textView"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
两个TextView之间就拉出了一条链条,创建该链条的关键语句
app:layout_constraintStart_toEndOf=“@+id/textView2”
app:layout_constraintEnd_toStartOf=“@id/textView”
链条头
左右排列的链条链条头就是左边第一个连接链条的控件,如上面例子“TextView”和“Hello World !”, “TextView”就是链条头,同理上下排列就是上面第一个。
介绍链条头是因为链条的所有属性都应该在链条头设置才有效(特别注意)
链条样式
总共有以下5种样式:
通过设置layout_constraintHorizontal_chainStyle 或 layout_constraintVertical_chainStyle 这两个属性来控制样式
其实可以设置的值只有3种:
-
spread:对应上图第一种样式Spread Chain
-
spread_inside: 对应上图第二种样式Spread Inside Chain
-
packed: 对应上图第四种样式Packed Chain
那么怎么设置另外两种样式呢?
先说下第五种样式Packed Chain with Bias , 首先样式属性设置为packed ,然后把它看成一个控件,添加Bias属性即可,参考上面讲Bias部分
另外一种,第三种样式,有点熟悉,这不是那个, 那个…
好吧,其实第三种样式 Weighted Chain ,权重链条,就是跟LinearLayout 的Weight一样的效果。
首先需要把样式设置为spread或者 spread_inside,也可以不设置,因为默认就是 spread样式。
然后把控件的宽(高)设置为MATCH_CONSTRAINT(也就是0dp)
最后就跟LinearLayout一样的设置了,用属性layout_constraintHorizontal_weight 或 layout_constraintVertical_weight 为链条上的每个控件设置权重。
例子:
主要属性app:layout_constraintHorizontal_weight=“2” app:layout_constraintHorizontal_weight=“1”
<TextView
android:id="@+id/textView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="244dp"
android:text="Hello World !"
android:textSize="16sp"
app:layout_constraintHorizontal_weight="2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/textView2"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textView2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="244dp"
android:text="TextView"
android:textSize="16sp"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintEnd_toStartOf="@id/textView"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
效果
中间没设置margin 中间不太好看,但是效果很直观。
八、辅助线(Guideline)
Guideline只能用于ConstraintLayout中,是一个工具类,不会被显示,仅仅用于辅助布局。
它可以是horizontal或者 vertical的。(例如:android:orientation=“vertical”)
- vertical的Guideline宽度为零,高度为ConstraintLayout的高度
- horizontal的Guideline高度为零,宽度为ConstraintLayout的高度
定位Guideline有三种方式:
- 指定距离左侧或顶部的固定距离(layout_constraintGuide_begin)
- 指定距离右侧或底部的固定距离(layout_constraintGuide_end)
- 指定在父控件中的宽度或高度的百分比(layout_constraintGuide_percent)
还是看例子吧,很简单
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="100dp"
android:layout_marginStart="50dp"
android:text="Hello World !"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="@+id/guideline"/>
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="200dp"
android:layout_marginEnd="32dp"
android:text="TextView"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/guideline" />
<android.support.constraint.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_end="200dp" />
最后
好吧,到这里就结束了。。但是由于版本一直都在更新,所以多去看看官方文档