美文网首页
Constraint Layout Practice and S

Constraint Layout Practice and S

作者: 十八子记 | 来源:发表于2021-04-25 14:00 被阅读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来设置宽高比,该比率有两种设置方式,如下:

    • 浮点值:表示宽度和高度之间的比率(比如: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,链的属性由链头控制
    <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属性。

    属性说明

    • layout_constraintHorizontal_bias: 水平偏差,取值范围:0.0~1.0。
    • layout_constraintVertical_bias: 垂直偏差,取值范围:0.0~1.0。
    <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纵向。

    三种定位方式:

    • layout_constraintGuide_begin:距离父容器起始位置的距离(对于垂直和水平来讲为左侧或顶部)。
    • layout_constraintGuide_end:距离父容器结束位置的距离(右侧或底部)。
    • layout_constraintGuide_percent:距离父容器宽度或高度的百分比,取值范围0.0-1.0,优先级高于begin和end。
    <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,需要根据具体需求进行选择。

    属性说明

    • barrierDirection: 屏障所在的位置,可设置的值有:start、end、left、right、top、bottom
    • constraint_referenced_ids: 屏障引用的控件,可设置多个(用逗号“,”隔开)。
    // 省略部分代码
    <!-- 图中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中,只有最后一个生效。

    属性说明

    • constraint_referenced_ids: 以逗号分隔的ID列表来引用控件。
    //  控件代码省略,通过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

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

    属性说明

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

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

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

    相关文章

      网友评论

          本文标题:Constraint Layout Practice and S

          本文链接:https://www.haomeiwen.com/subject/ajcerltx.html