Constraint Layout Practice and S

2021-04-25  本文已影响0人  十八子记

转自: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来设置宽高比,该比率有两种设置方式,如下:

// 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,链的属性由链头控制
<TextView
    android:id="@+id/textX"
    android:text="A"
    app:layout_constraintHorizontal_chainStyle="spread"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toLeftOf="@id/textD"
    />

<TextView
    android:id="@+id/textD"
    android:text="Better"
    app:layout_constraintLeft_toRightOf="@id/textX"
    app:layout_constraintRight_toLeftOf="@id/textF" />

<TextView
    android:id="@+id/textF"
    android:text="You"
    app:layout_constraintLeft_toRightOf="@id/textD"
    app:layout_constraintRight_toRightOf="parent" />

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


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
<TextView
    android:id="@+id/textX"
    android:layout_width="wrap_content"
    app:layout_constraintHorizontal_chainStyle="spread" />

// 文本D占用2份可用空白空间
<TextView
    android:id="@+id/textD"
    android:layout_width="0dp"
    app:layout_constraintHorizontal_weight="2" />

// 文本F占用2份可用空白空间
<TextView
    android:id="@+id/textF"
    android:layout_width="0dp"
    app:layout_constraintHorizontal_weight="2" />

执行代码,效果如下:


10.png
4) Packed

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

执行代码,效果如下:


11.png

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

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

<ImageView
    android:id="@+id/iv_avatar" />

<TextView
    android:id="@+id/tv_name"
    app:layout_constraintLeft_toRightOf="@id/iv_avatar"
    app:layout_constraintTop_toTopOf="@id/iv_avatar"
    app:layout_constraintBottom_toBottomOf="@id/iv_avatar"
    
    app:layout_constraintBottom_toTopOf="@id/tv_code"
    app:layout_constraintVertical_chainStyle="packed"

    tools:text="姓名" />

<ImageView
    android:id="@+id/iv_switch_identity"
    
    app:layout_constraintBottom_toBottomOf="@id/tv_name"
    app:layout_constraintLeft_toRightOf="@id/tv_name"
    app:layout_constraintTop_toTopOf="@id/tv_name" />

<TextView
    android:id="@+id/tv_code"
    
    app:layout_constraintLeft_toRightOf="@id/iv_avatar"
    app:layout_constraintTop_toBottomOf="@id/tv_name"
    app:layout_constraintBottom_toBottomOf="parent"/>
12.png

3.5 Center

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

// 水平居中
<TextView
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent/>

// 垂直居中
<TextView 
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintBottom_toBottomOf="parent/>

3.6 Bias

定义控件的方向偏差,使用Bias可以改变两边的权重,类似于LinearLayoutweight属性。

属性说明

<TextView 
    // 水平占比30%
    app:layout_constraintHorizontal_bias="0.3"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent/>

4. 辅助工具

4.1 Guideline

像辅助线,引导线一样,可以添加垂直或水平的引导线来约束视图,并且应用用户看不到该引导线(不显示在设备上,因为默认设置为View.GONE)。可以根据相对于布局边缘的dp单位或百分比在布局中定位引导线。

android_orientation控制是横向or纵向。

三种定位方式:

<TextView
    ... // 省略代码
    app:layout_constraintRight_toLeftOf="@id/guideline"
    app:layout_constraintTop_toTopOf="parent" />

<TextView
    ... // 省略代码
    app:layout_constraintLeft_toRightOf="@id/guideline"
    app:layout_constraintTop_toTopOf="parent" />

<androidx.constraintlayout.widget.Guideline
    android:id="@+id/guideline"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    app:layout_constraintGuide_percent="0.5" />

运行效果如下:


13.png

4.2 Barrier

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

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

属性说明

// 省略部分代码
<!-- 图中width变化为50和150切换 -->
<TextView
    android:id="@+id/tvName"
    android:layout_width="150dp"
    android:text="name"
    app:layout_constraintTop_toTopOf="parent" />

<TextView
    android:id="@+id/tvSchoolAddress"
    android:text="SchoolAddress"
    app:layout_constraintTop_toBottomOf="@id/tvName" />

<androidx.constraintlayout.widget.Barrier
    android:id="@+id/barrier"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:barrierDirection="right"
    app:constraint_referenced_ids="tvName,tvSchoolAddress" />

<TextView
    android:id="@+id/tvClassTime"
    android:text="ClassTime"
    app:layout_constraintLeft_toRightOf="@id/barrier" />
14.gif

4.3 Group

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

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

属性说明

//  控件代码省略,通过group设置一组控件(id1、id2......)的显示状态
<androidx.constraintlayout.widget.Group
    android:id="@+id/group"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:constraint_referenced_ids="id1,id2"
    android:visibility="invisible"
    />

4.4 Circular positioning

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

属性说明

// 省略宽高边距等设置代码
<ImageView
    android:id="@+id/iv_header"
    android:layout_marginLeft="19dp"
    android:layout_marginTop="19dp"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

<ImageView
    android:id="@+id/iv_gender"
    app:layout_constraintCircle="@id/iv_header"
    app:layout_constraintCircleRadius="35dp"
    app:layout_constraintCircleAngle="140"/>

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

15.png

5. Summary

<font size=3 color=#999999>输出是最好的输入方式!</font>

上一篇下一篇

猜你喜欢

热点阅读