2023年7月13日发(作者:)
ConstraintLayout⽤法全解析*本篇⽂章已授权微信公众号 guolin_blog (郭霖)独家发布本⽂是基于constraint-layout:1.1.2⼀、前⾔在以前,android是使⽤布局如LinearLayout 、RelativeLayout等来构建页⾯,但这些布局使⽤起来很⿇烦,并且经常需要⼀层⼀层嵌套,写⼀个简单的页⾯就需要费很⼤的劲。所以在16年I/O⼤会上,google发布了全新的布局-ConstraintLayout,其他布局和ConstraintLayout⽐起来,根本就没有存在的必要了...ConstraintLayout具有以下优势:1. 较⾼的性能优势。布局嵌套层次越⾼,性能开销越⼤。⽽使⽤ConstraintLayout,经常就⼀层嵌套就搞定了,所以其性能要好很多。详细的性能分析可参见:解析ConstraintLayout的性能优势2. 完美的屏幕适配ConstraintLayout的⼤⼩、距离都可以使⽤⽐例来设置,所以其适配性更好。3. 书写简单4. 可视化编辑。ConstraintLayout也有⼗分⽅便完善的可视化编辑器,不⽤写xml也基本上能实现⼤部分功能。但个⼈还是⽐较喜欢写xml,所以本篇⽂章主要介绍如何使⽤代码控制。如果想看如何使⽤可视化编辑器,可以参考郭霖⼤神的这篇⽂章引⼊:api 'aint:constraint-layout:1.1.2'⼆、ConstraintLayout1. 定位位置确定位置的属性提供了下⾯13个属性,其实本质上都是⼀样的,看名字应该基本上都知道怎么⽤了(就是哪⼀条边和哪⼀条边对齐)layout_constraintLeft_toLeftOflayout_constraintLeft_toRightOflayout_constraintRight_toLeftOflayout_constraintRight_toRightOflayout_constraintTop_toTopOflayout_constraintTop_toBottomOflayout_constraintBottom_toTopOflayout_constraintBottom_toBottomOflayout_constraintBaseline_toBaselineOflayout_constraintStart_toEndOflayout_constraintStart_toStartOflayout_constraintEnd_toStartOflayout_constraintEnd_toEndOf来看个例⼦:实现上述UI的相关代码如下: ...>
从中可以看到,layout_constraint*属性的值可以是某个id或者parent(⽗布局)B要位于A的右边,则使⽤app:layout_constraintLeft_toRightOf="@id/a",C位于A的下边,则使⽤app:layout_constraintTop_toBottomOf="@id/a"对于⼀个View的边界界定,官⽅给了下⾯这张图:2. margin设置margin还是继续⽤以前的属性layout_margin* 。不过需要注意,要使margin⽣效,必须具有对应⽅向的layout_constraint*,否则margin不⽣效.3. 关于view gone假如现在有如下布局: ...>
考虑⼀个问题,如果B动态设为gone了,C会怎么显⽰呢?真实情况如下:为什么会这样显⽰呢?看他的蓝图应该会好理解些:可以看出,b设为gone之后,他的宽、⾼、margin都失效了,变为⼀个点了,但它的constrain还⽣效,位于指定的位置。c还是可以继续以他为锚点。那么如何解决关于View gone引起的⾮预期的布局变化呢?1. 如果可以,尽量使⽤invisible2. 尽量其他view的布局不依赖会gone的view3. google也提供了属性layout_goneMargin*="xdp",意思是⽐如当constrainleft的锚点gone时,layout_goneMarginLeft将⽣效。但因为这个只能设置固定的距离,个⼈感觉灵活性不是很⾼。4. 居中及bias⼀个view如何设置为居中呢?如果查找属性,会发现并没有如RelativeLayout类似的layout_centerVertical属性,那如何设置居中呢?constraint的思想很巧妙。根据第⼀节的知识,⼤家知道如果设置app:layout_constraintLeft_toLeftOf="parent",则view会贴着⽗view的左边,设置app:layout_constraintRight_toRightOf="parent" 则会贴着右边,那如果两个都设置,效果会怎样呢?如图,两个都设置,view则会居中。⾄此可以看出,对constraint的理解其实可以看成是像两个弹簧⼀样,如果只在左边加⼀个弹簧,右边没有,那左边的势必会把view拉到左边去,如果在右边也加⼀根弹簧,两个弹簧⼒相互平衡,则view就居中了。上⾯是view居中,如果我想让view向左偏⼀些,或者位于1/3处该怎么处理?其实也是⼀样的,想象⼀下,如果左边的弹簧⼒⼤⼀些,view不是就⾃然往左偏了嘛。如何使⼒⼤⼀些呢?使⽤如下属性layout_constraintHorizontal_biaslayout_constraintVertical_biasbias即偏移量,他们的取值范围从0~1,0即挨着左边,1是挨着右边,所以要使处于1/3处,可以设置如下属性app:layout_constraintHorizontal_bias="0.33",效果图如下:的尺⼨设置view的⼤⼩除了传统的wrap_content、指定尺⼨、match_parent(虽然官⽅不推荐使⽤match_parent)外,还可以设置为0dp(官⽅取名叫MATCH_CONSTRAINT),0dp在constraint可不是指⼤⼩是0dp,⽽是有特殊含义的。他的作⽤会随着不同的设置有不同的含义:1. layout_constraintWidth_defaultlayout_constraintWidth_default有三个取值,作⽤如下:spread,默认值,意思是占⽤所有的符合约束的空间 ...>
可以看到layout_width为0dp,实际的效果则是宽度和约束⼀样,左右两边的留⽩是margin的效果。percent,意思是按照⽗布局的百分⽐设置,需要layout_constraintWidth_percent设置百分⽐例 A的宽度设为0.4,则其宽度为⽗布局的0.4倍。另外,设置了layout_constraintWidth_percent属性,可以不⽤指定layout_constraintWidth_default,他会⾃动设置为percentwrap,意思匹配内容⼤⼩但不超过约束限制,注意和直接指定宽度为wrap_content的区别就是不超过约束限制,如下: ...>
可以看到虽然⽂字很长,但第⼀⾏的绿⾊button宽度达到约束时,就不在增加,⽽第⼆⾏的button显⽰了完整的内容,超过约束的限制。在1.1上 对于wrap_content会超过约束限制,⾕歌⼜新增了如下属性app:layout_constrainedWidth=”true|false”app:layout_constrainedHeight=”true|false”设置为true也可以限制内容不超过约束(这样感觉layout_constraintWidth_default这个属性已经没什么⽤了)2. ratiolayout_constraintDimensionRatio,即宽和⾼成⼀定的⽐例,其值可以是"width:height"的形式,也可以是width/height的值。该属性⽣效的前提:宽和⾼其中有⼀项为0dp,有constraint。下⾯按照有⼏个0dp来分别介绍下:如果只有⼀项为0dp,则该项值按照⽐例计算出来。⽐如⾼为20dp,宽为0dp,radio为"2:1",则最终宽为40dp如果两项都为0dp,则尺⼨会设置为满⾜约束的最⼤值并保持⽐例。因为这是系统计算的,有的时候不是我们想要的,我们也可以通过在前⾯加H、W来指定是哪⼀个边需要计算。例如"H,2:1",则是指宽度匹配约束,⾼度是宽度的1/23. max min有如下属性可以设置其的最⼤最⼩值,含义如字⾯值⼀样:layout_constraintWidth_minlayout_constraintWidth_maxlayout_constraintHeight_maxlayout_constraintHeight_min4. weight该属性在下⾯讲解6. 链如图,在⼀个⽔平或者竖直⽅向上,⼀排view两两互相约束,即为链链的第⼀个元素称为链头,可以通过设置layout_constraintHorizontal_chainStyle来控制链的分布形式spread默认模式,分布样式如上图spread_inside如图,和spread的区别是没算两端的约束packed所有元素挤在中间,也可以配合使⽤bias来改变位置偏移可以看出,链与LinearLayout效果⼤致⼀样。和LinearLayout⼀样,链也可以使⽤layout_constraintHorizontal_weight,来分割剩余空间。但⼜和android:layout_weight不太⼀样,不⼀样的地⽅如下:layout_weight ,不管当前view的⼤⼩设的是多⼤,都会继续占据剩余空间layout_constraintHorizontal_weight,这个只对0dp并且layout_constraintWidth_default为spread的view⽣效,使其⼤⼩按⽐例分割剩余空间,对于已经设定⼤⼩的view不⽣效如下⾯的⽰例: ...> />
可以看出,LinearLayout和ConstraintLayout虽然三个⼦view的layout_width值是⼀样的,weight也都设置了1,但效果完全不⼀样7. 圆形布局7. 圆形布局ConstraintLayout还提供了⼀种⽐较炫酷的圆形布局,这是以往的布局所做不到的。涉及到的属性也很简单,就下⾯三个:layout_constraintCircle : 圆⼼,值是某个view的idlayout_constraintCircleRadius : 半径layout_constraintCircleAngle :⾓度,值是从0-360,0是指整上⽅⽰例如下: />三、辅助组件除了ConstraintLayout⾃⾝属性之外,⾕歌还提供了很多辅助布局(只是在布局中起辅助作⽤,并不会在界⾯真正显⽰),来使ConstraintLayout的功能更加强⼤。下⾯,我们就⼀⼀来了解下这些布局1. GuideLine即参考线的意思,有⽔平参考线和竖直参考线两种。他的作⽤就像是⼀个虚拟的参考线,只是⽤来⽅便其他View以他为锚点来布局。如上⼀篇所了解到的,ConstraintLayout 的定位原则就是⼀个View参考其他View的相对布局,如果有的时候当前布局没有合适的参考View,⽽建⼀个专门⽤于定位的View⼜会太重,这种情况正是GuideLine的⽤武之地。例如: ...>
可以看到我分别添加了⼀个⽔平参考线和竖直参考线,之后的Button的布局就参考与这两个参考线,⽽在布局中并不会显⽰。Guideline的⼤部分的属性如layout_width都是不会⽣效的,⽽他的位置的确定是由下⾯三个属性之⼀来确定的:layout_constraintGuide_begin:距离⽗布局的左边或者上边多⼤距离layout_constraintGuide_end:距离⽗布局的右边或者下边多⼤距离layout_constraintGuide_percent:百分⽐,0~1,距离⽗布局的左边或者上边占⽗布局的⽐例2. GroupGroup是⼀个可以同时控制多个view 可见性的虚拟View。例如: ...> < ... android:visibility="invisible" app:constraint_referenced_ids="a,c" /> < ... android:visibility="visible" app:constraint_referenced_ids="b,d" />
可以看到,第⼀个Group通过app:constraint_referenced_ids指定了a、c两个控件,这样当该Group可见性为invisible时,a、c的可见性都会变为invisible,为gone则都为gone。所以Group很适合处理有⽹⽆⽹之类的场景,不再需要像之前那样⼀个⼀个view控制可见性,通过Group就可以统⼀处理了。Group有⼀些注意事项:xml中,可见性配置的优先级:Group优先于View,下层Group优先于上层。Group只可以引⽤当前ConstraintLayout下的View,⼦Layout 下的View不可以。app:constraint_referenced_ids⾥直接写的是id的字符串,初始化后会通过getIdentifier来反射查找叫该名字的id。所以如果你的项⽬⽤了类似AndResGuard的混淆id名字的功能,切记不要混淆app:constraint_referenced_ids⾥的id,否则在release版本就会因找不到该id⽽失效。或者也可以通过代码setReferencedIds来设置id。3. Placeholder占位布局。他⾃⼰本⾝不会绘制任何内容,但他可以通过设置app:content="id",将id View的内容绘制到⾃⼰的位置上,⽽原id的 View就像gone了⼀样。如下:...>
效果如图:可以看到,原本B是位于A的右边并且顶部对齐的,但因为A被Placeholder引⽤,使A 相当于Gone了。⽽Placeholder的位置则显⽰了A的内容,并且⼤⼩也和A相符,Placeholder的⼤⼩设置并没有⽣效。⼤概总结可以认为,Placeholder引⽤A后的效果是,原本位置的A gone,原本位置的Placeholder变为Placeholder的约束属性+A的内容属性。另外,Placeholder也⽀持使⽤代码setContentId动态的修改设置内容。关于Placeholder的应⽤场景,⽹上其他⼈也都列出了⼀些例⼦:⽐如可以作为位置模板,引⼊后只需要写内容view;使⽤代码动态改变内容,结合TransitionManager可以做⼀些有趣的过度动画等。4. Barrier屏障,⼀个虚拟View。他主要解决下⾯遇到的问题:如上图布局,两个TextView,⼀个button位于他们的右边。现在button设置的是在下⾯TextView的右边。假设有时候上⾯的TextView⽂本变长了,则布局会变为下⾯这个样⼦:上⾯的TextView和Button重叠了。这时该怎么解决这个问题呢?Button只能设置⼀个View作为锚点,设置了上⾯就顾不了下⾯了。。。所以就诞⽣了Barrier,他可以设置N个View作为锚点,使⽤⽅式如下:则Barrier始终位于text1,text2两个View最⼤宽度的右边,⽰意图如下:这⾥基本的⽤法就讲完了。下⾯再考虑⼀个情况,假如有如下的布局: ...>
⽬前Button C和Button a、b的最上值对齐,没有问题。但如果a Gone了呢?效果如下:其实也是符合逻辑,a gone后,会变为⼀个点,所以C顶齐⽗布局也没问题。但有的时候这不符合我们的需求,我们希望Barrier不要关注Gone的View了,所以⾕歌提供了属性barrierAllowsGoneWidgets,设为false后,就不在关注Gone的View了,效果如下:四、结束本篇已基本上介绍完ConstraintLayout所有的属性了(除了代码写布局的ConstraintSet类)。在2018.8.9号,⾕歌⼜发布了2.0.0-alpha2版本,⾥⾯加⼊了许多好玩的新特性,相信ConstraintLayout之后会越来越强⼤。
发布者:admin,转转请注明出处:http://www.yc00.com/web/1689218041a222532.html
评论列表(0条)