Constraint Layout Practice and Summary

转自:Constraint Layout Practice and Summary-1

Constraint Layout 2.0 several new features-2

从约束布局发布后,一直没有将其应用到工作中,只是停留在Demo阶段(使用的越多越感觉这种布局更加灵活、高效)。去年下半年到今年的工作中,将一部分原有的相对布局、线性布局和帧布局等切换到使用约束布局。刚开始有一些生疏不适应,元素间的关系相互制约的比较紧密。刚开始用完的感觉就是维护起来比较难,因为关心的要素比以往的布局要多。

本文比较简单,因为用了一段时间了,便将之前用到的部分进行了简单的总结,主要是1.0的基本运用,还包含了2.0的一些新特性。对于运用的知识加以理解和深入,并进行内化,最好的方式就是实践加总结。再者是通过实践后用文章检验纠错,不断调整,不断学习。最好的输入就是输出,文章是众多输出方式中的一种。

1. What is?

ConstraintLayout作为最受欢迎的Jetpack库之一,TA是一个ViewGroup。TA的出现主要是为了解决布局嵌套过多的问题,以灵活的方式定位和调整部件。几个月前2.0也发布了,最新稳定版为2.0.4(December 17, 2020;Alpha Release:2.1.0-alpha2),从studio 2.3以后布局中默认开始使用Constraint Layout

2. Why use?

ConstraintLayout可以使用扁平视图层次结构创建复杂的布局,降低页面布局层级,提升页面渲染性能。与RelativeLayout相似,却有更高的灵活性,并且更易于与Android Studio的布局编辑器配合使用。

先来感受下约束的效果~~

比如一张图片在布局中水平对齐,如下代码

    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"

在studio中Design视图下可以直接进行操作。

视图中添加了正反约束,约束线像是弹簧一样弯弯曲曲的拉着imageview,在两个约束条件下显示水平居中。

1.gif

还可以添加约束偏差,上边视图在两个约束条件之间居中且默认偏差为50%,下图可以在属性窗口中进行自由调整。

2.gif

视图绘制大致分为测量、布局和绘制三个阶段,在绘制过程的每个阶段都需要对视图树进行自顶向下的遍历操作,因此视图层次结构嵌套越多,绘制所需时间和计算功耗就会越多,通过扁平化的层次是解决该问题的方式之一。

官方用LinearLayoutRelativeLayout进行布局,然后使用Constraintlayout做同样界面的布局结构,用OnFrameMetricsAvailableListener分析了两种布局所执行的每次测量和布局操作所花费时间,以收集有关应用界面渲染的逐帧时间信息。结果为ConstraintLayout在测量和布局阶段的性能比传统布局大约高40%(蓝色为Traditional,红色为Constraintlayout)。

3.png

3. Usage

本文使用Android Studio 4.0(Build #AI-193.6911.18.40.6514223)Constraintlayout:2.0.4

对于相对位置(layout_constraintLeft_toLeftOf、layout_constraintLeft_toRightOf等),边距(android:layout_marginStart、android:layout_marginEnd、android:layout_marginRight、layout_goneMarginLeft等),水平垂直居中,水平垂直偏移(layout_constraintHorizontal_bias、layout_constraintVertical_bias)不再进行讲解,可以参考官方文档。

3.1 Add ConstraintLayout to your project

// Add the dependencies for the artifacts you need in the build.gradle file for your app or module:
// Need Sync Project with Gradle Files

dependencies {
    // latest version 2.1.0-alpha2 
    // December 17, 2020
    
    implementation "androidx.constraintlayout:constraintlayout:2.0.4"
}
4.png

3.2 Margin

边距,这里主要介绍个新特性,就是layout_goneMarginStart、layout_goneMarginLeft、layout_goneMarginTop,当目标控件设置为隐藏(GONE)的时候,gonemargin*边距值仍然生效。其他对应的属性查看官方文档即可,这里不再叙述。

举个栗子:
假设text2的左边约束在text1的右边,并给TextView2设置app:layout_goneMarginLeft="16dp",效果如下:

5.png

当将text1的可见性设为android:visibility=gone时,左侧的16dp生效了,效果如下:

6.png

说明: 如Text1显示时距左侧是6dp,当不显示时Text2也应该是距离左侧6dp(但当Text1显示时,Text2距离左侧是0dp),那么使用goneMarginLeft则可以满足该场景。

3.3 Ratio

宽高比,使用app:layout_constraintDimensionRatio来设置宽高比,该比率有两种设置方式,如下:

  • 浮点值:表示宽度和高度之间的比率(比如:1.0F);
  • 比率:'宽度:高度'形式的比率(比如:1:1)。
// formula -> height:width=ratio
h:w=ratio

在使用过程中,刚开始理解有误,所以主要针对比率进行说明。

1、app:layout_constraintDimensionRatio="16:9"表示宽:高=16:9
2、app:layout_constraintDimensionRatio="w,3:4"表示宽:高=4:3,w表示分母是宽。
3、app:layout_constraintDimensionRatio="h,16:9"表示宽:高=16:9,h表示分母是高。
4、指定值情况下:
举栗:
① w已经设定固值,无法通过先获取h再通过ratio进行计算。w=100dp(方便计算),h=0dp,当w,2:1时,有w:h=1:2,然后将值代入,100:h=1:2,换算为1h=200,则h=200。
② w=100dp,h=0p,当h,2:1时,有h已经设置为固定值,不能通过ratio计算h值,有w:h=2:1,为2h=100dp,则h=50dp。

3.4 Chain

链,在横轴或或者竖轴上的控件相互约束时,可以组成一个线性的链式约束,在一个维度(水平or垂直)上,管理一组控件的方式。

多个view相互引用即创建了一个链,第一个为链头,链的属性也由链头的属性进行控制。水平方向左边第一个控件为链头,垂直方向上最上边控件为链头。

总共分为4中样式,可以通过 app:layout_constraintHorizontal_chainStyleapp:layout_constraintVertical_chainStyle设置链式控件的样式,包含spreadspread insidepacked

1) Spread(default)

元素均匀的分散进行分布。

// 省略了layout_width、layout_height等属性
// textX为head,链的属性由链头控制





执行以上代码,效果如下:


7.png
2) Spread inside

类似spread模式,但链的端点不会分散。即第一个和最后一个视图固定在链两端的约束边界上,其余视图均匀分布。

// 其余代码省略
...
app:layout_constraintHorizontal_chainStyle="spread_inside"
9.png

执行以上代码,效果如下:


8.png

当设置为spreadspread inside模式时,可以通过将一个或多个视图设置为“match constraints”(0dp)来填充剩余空间。默认情况下,设置为“match constraints”的每个视图之间的空间均匀分布。也就引出下一模式:权重模式。

android:layout_width="0dp"

效果如下:

3) Weighted

权重模式,类似于LinearLayout中的layout_weight,原理是相同的。权重值最高的视图获得的空间最大;相同权重的视图获得同样大小的空间。

// 省略部分代码
// chain style is spread or spread_inside


// 文本D占用2份可用空白空间


// 文本F占用2份可用空白空间

执行代码,效果如下:


10.png
4) Packed

视图将被包装在一起(在考虑外边距之后)。也可以通过更改链的头视图偏差(Bias,下边有介绍)调整整条链的偏差(左/右或上/下)。

执行代码,效果如下:


11.png

举栗子:
在开发中有显示用户头像、姓名和编号的区域,有个需求是当有编号时三项全部显示,没有编号则只显示头像和姓名,那么采用这种方式能容易的实现。

//  省略layout_width、layout_margin*等基础属性代码








12.png

3.5 Center

居中,这个比较有特色的是彼此互为冲突即可居中。

// 水平居中





运行效果如下:


13.png

4.2 Barrier

直译为屏障、分界线,可以用它来约束视图,不会定义自己的位置。相反,屏障的位置会随着其中所含视图的位置而移动。

比如填写收货地址的表单中,左侧为标签内容提示文案,右侧为输入文本区域,中间一条垂直分界屏障线,左右两侧据此则可以向中间靠拢。当然实现这个效果也可以使用Guideline,需要根据具体需求进行选择。

属性说明

  • barrierDirection: 屏障所在的位置,可设置的值有:start、end、left、right、top、bottom
  • constraint_referenced_ids: 屏障引用的控件,可设置多个(用逗号“,”隔开)。
// 省略部分代码








14.gif

4.3 Group

这个很容易理解, 用于控制一组控件的是否显示,继承于ConstraintHelper。

使用时注意两点:一是组内某个控件设置View.setVisibility(int)来控制控件显示/隐藏时是无效的。二是控件存在于多个group中,只有最后一个生效。

属性说明

  • constraint_referenced_ids: 以逗号分隔的ID列表来引用控件。
//  控件代码省略,通过group设置一组控件(id1、id2......)的显示状态

4.4 Circular positioning

圆形定位,以角度和半径距离约束控件中心相对于另一个控件中心。

属性说明

  • layout_constraintCircle: 引用的另一个控件(目标控件)ID值。
  • layout_constraintCircleRadius: 源控件的中心到其他控件(目标控件)中心的距离。
  • layout_constraintCircleAngle: 源控件应该处于哪个角度(以度为单位,从0到360)。
// 省略宽高边距等设置代码



图片iv_gender在图片iv_header的140度角半径为35dp的位置上,效果如下:

15.png

5. Summary

  • 嵌套层级少。当一个复杂布局中,有id强关联的,要复用比较难。所以需要在开发前进行拆分设计,复杂布局要分拆到不同的文件中。
  • 可视化编辑: ConstraintLayout还有一个独立的编辑器,文章开始页展示了操作效果,只需要托拽就可以完成整个布局。
  • 布局高效,轻松应对复杂布局,适配性好,有百分比布局、设置自身宽高比例,各种辅助组件。
  • 减少测绘/布局时间,提升效率。性能检测方式如下:
    // 1、记录每个帧的界面操作
    window.addOnFrameMetricsAvailableListener(
            frameMetricsAvailableListener, frameMetricsHandler);
    
    // 2、触发OnFrameMetricsAvailableListener回调
    Window.OnFrameMetricsAvailableListener {
            _, frameMetrics, _ ->
            val frameMetricsCopy = FrameMetrics(frameMetrics);
            
            // 布局测量采用纳秒级
            val layoutMeasureDurationNs = 
                    frameMetricsCopy.getMetric(FrameMetrics.LAYOUT_MEASURE_DURATION);
    

输出是最好的输入方式!

你可能感兴趣的:(Constraint Layout Practice and Summary)