Android常见的5个布局,我想大家一定不会陌生。LinearLayout、RelativeLayout和FrameLayout也是使用频率较高的布局方式,做Android开发的一定使用过。
公司主营业务:网站建设、成都网站建设、移动网站开发等业务。帮助企业客户真正实现互联网宣传,提高企业的竞争能力。创新互联是一支青春激扬、勤奋敬业、活力青春激扬、勤奋敬业、活力澎湃、和谐高效的团队。公司秉承以“开放、自由、严谨、自律”为核心的企业文化,感谢他们对我们的高要求,感谢他们从不同领域给我们带来的挑战,让我们激情的团队有机会用头脑与智慧不断的给客户带来惊喜。创新互联推出武陵源免费做网站回馈大家。
传统的5种布局方式:
不过我的问题并不是问面试者如何使用这些基础的布局,而是要看面试者怎么解决布局嵌套(影响性能)和屏幕适配问题。
我们都清楚Android界面的布局太复杂,嵌套层次过深,会使整个界面的测量、布局和绘制变得更复杂,对性能会造成影响。所以我们在写Layout文件时,也要尽量避免布局的嵌套层次过深的问题。
在怎么解决问题之前,我们得有一个好方法先判断当前的问题情况。Android SDK工具箱中有一个叫做Hierarchy Viewer的工具,能够在App运行时分析Layout。
注意: 在ROOT的手机,或者是安装开发版的ROM的手机可以直接使用Hierarchy Viewer。如果没有Root的手机(SDK 4.1及以上),需要在你的PC端添加一个环境变量“ANDROID_HVPROTO=ddm”。
下面列举一些面试者常使用的方式。
merge merge标签的作用是合并UI布局,使用该标签能降低UI布局的嵌套层次。
merge标签可用于两种情况:
ViewStub ViewStub标签引入的布局默认不会inflate,既不会显示也不会占用位置。 ViewStub常用来引入那些默认不会显示,只在特殊情况下显示的布局,如数据加载进度布局、出错提示布局等。
需要在使用时手动inflate:
ViewStub在一定的程度可以起到减少嵌套层次的作用,特别是很多时候我们的程序可能不需要走到ViewStub的界面。
include 将可复用的组件抽取出来并通过include标签使用,但include标签能减少布局的层次吗?
我认为不能。include主要解决的是相同布局的复用问题,它并不能减少布局的层次。
用RelativeLayout代替LinearLayout
很多人为了减少布局层次喜欢用RelativeLayout代替LinearLayout,不过可能达到的效果并不会很明显。层次是减少了,但本身RelativeLayout就会比LinearLayout性能差一点。
有一些界面,比如一个图片和一个文本的布局(ListItem常见的布局方式),可以利用TextView有drawableLeft, drawableRight等属性,完全不需要RelativeLayout或者LinearLayout布局。
传统的布局方式存在一定的缺陷,如RelativeLayout要两次测量(measure)它的子View才能知道确切的高度;如果LinearLayout布局的子View有设置了layout_weight,那么它也需要测量两次才能获得布局的高度。
相对于传统的布局方式,Android官方还推出了两种新的布局方式:ConstraintLayout和FlexboxLayout。
ConstraintLayout ConstraintLayout即约束布局,在2016年由Google I/O推出。ConstraintLayout和RelativeLayout有点类似,控件之间根据依赖关系而存在,但比RelativeLayout更加灵活。创建大型复杂的布局仍然可以使用扁平的层级(不用嵌套View Group),说的简单些就是,再复杂的界面也可以只有2层层次。
要使用ConstraintLayout需要在build.gradle中添加相关的support库:
使用ConstraintLayout可以有效的解决布局嵌套过多导致的性能问题,官方也对其渲染性能进行了优化,并且ConstraintLayout支持可视化的方式编写布局。
不过学会熟练使用ConstraintLayout会需要一点时间,但这是值得的。
FlexBoxLayout 做过前端开发(CSS方面)的同学对FlexBox一定不会陌生,最近我在做微信小程序开发时也涉及到FlexBox。FlexBox(弹性布局)是w3c在2009年提出的一种新的布局方案,解决以前那种传统css的盒模型的局限性。
Google开源了FlexboxLayout布局和前端CSS FlexBox布局具有相同的功能(肯定有不一样的地方),但已经足够在Android上改进布局的构建方式。
FlexBoxLayout可以理解成一种更高级的LinearLayout,不过比LinearLayout更加强大和灵活。如果我们使用LinearLayout布局的话,那么不同的分辨率,也许我们要重新调整布局,势必会需要跟多的布局文件放在不同的资源目录。而使用FlexBoxLayout来布局的话,它可以适应各种界面的改变(所以叫响应式布局)。
如果对前端的Flexbox不太了解的话,你还需要补一些概念,好在这些东西在网上很容易找到。
可能很多读者会觉这样的面试题是吹毛求疵,很多项目中哪有这么复杂的界面,根本就用不到这些优化措施。
可以说厉害的人,或者叫高手,可能只是比较多在意这些细节而已。在实践中的经历告诉我,很多难于解决的性能问题,并不是因为有一个影响性能的问题无法攻克,而是没有一个明显的制约因素,是有各种小问题一点一点堆积起来,最终积重难返。
所以,把细节做好,或者意识到细节的地方可能引发的问题,对我们解决问题是很有帮助的,不要浪费了让你可以成长的细节。
有需要更多Android高级进阶和面试资料的朋友可以私信我获取
参考: ConstraintLayout官方文档
目前可以使用各种类型的约束:
1.相对定位
2.边缘
3.定心定位
4.圆形定位
5.可见性行为
6.尺寸限制
7.链
8.虚拟助手对象
9.优化器
注意,约束中不能具有循环依赖关系。
相对定位是在约束布局中创建布局的基本构建块之一。这些约束允许您将给定的小部件相对于另一个小部件定位。可以在水平和垂直轴上约束小部件:
1.水平轴:左、右、起点和终点
2.垂直轴:顶部、底部和文本基线
一般的概念是将控件的给定边约束到任何其他窗口小部件的另一边。
例如,为了将按钮B定位到按钮A的右边:
这告诉系统,我们希望按钮B的左侧被约束到按钮A的右侧。这样的位置约束意味着系统将试图使双方共享相同的位置。
以下是可用约束的列表:
他们都引用了id对于另一个小部件,或父母(它将引用父容器,即约束布局):
如果设置了边距,则它们将应用于相应的约束(如果存在),将边距强制为目标和源边之间的空间。 通常的布局边距属性可用于此效果:
当位置约束目标的可见性为View.GONE时,您还可以使用以下属性指示要使用的不同边距值:
ConstraintLayout是如何处理“不可能”的约束。例如,如果我们有这样的事情:
除非ConstraintLayout恰好具有与Button完全相同的大小,否则两个约束不能同时满足(双方都不能成为我们希望它们的位置)。
在这种情况下发生的事情是,约束的作用就像是相反的力量将小部件拉平; 这样小部件最终将在父容器中居中。 这同样适用于垂直约束。
遇到这种相反的约束时的默认设置是使窗口小部件居中; 但是您可以使用偏差属性调整定位以支持一侧而不是另一侧:
例如,以下将使左侧具有30%的偏差而不是默认的50%,使得左侧将更短,小部件更倾向于左侧:
使用偏差,您可以制作更好地适应屏幕尺寸变化的用户界面。
你可以以角度和距离约束窗口小部件中心相对于另一个窗口小部件中心。 这允许你将小部件放在圆上。 可以使用以下属性:
像往常一样,GONE小部件不会显示,也不是布局本身的一部分(即如果标记为GONE,它们的实际尺寸将不会更改)。
对于布局传递,它们的尺寸将被视为零(基本上,它们将被解析为一个点)
如果他们对其他小部件有限制,他们仍然会受到尊重,但任何边距都会好像等于零
这种特定的行为允许构建布局,你可以临时标记小部件。GONE,不破坏布局,这在做简单布局动画时特别有用。
约束布局的最小尺寸
你可以为ConstraintLayout本身定义最大和最小大小:
当其尺寸设置为WRAP_CONTENT时,ConstraintLayout将使用这些最小和最大尺寸。
可以通过以3种不同的方式设置android:layout_width和android:layout_height属性来指定控件的大小:
1.使用特定值
2.使用WRAP_CONTENT,它将要求小部件计算其自身大小。
3.使用0dp,相当于“MATCH_CONSTRAINT”
前两个以与其他布局类似的方式工作。 最后一个将以匹配所设置的约束的方式调整窗口小部件的大小((a)是wrap_content,(b)是0dp)。 如果设置了边距,则在计算中将考虑它们((c)中的0dp)。
如果维度设置为WRAP_CONTENT,则在1.1之前的版本中,它们将被视为文字维度 - 这意味着约束不会限制生成的维度。 虽然通常这足够(并且更快),但在某些情况下,您可能希望使用WRAP_CONTENT,但仍然强制执行约束以限制结果维度。 在这种情况下,您可以添加一个相应的属性:
当维度设置为MATCH_CONSTRAINT时,默认行为是使结果大小占用所有可用空间。 还有几个额外的修饰符:
layout_constraintWidth_min和layout_constraintHeight_min:将设置此维度的最小大小
layout_constraintWidth_max和layout_constraintHeight_max:将设置此维度的最大大小
layout_constraintWidth_percent和layout_constraintHeight_percent:将此维度的大小设置为父级的百分比
为min和max指示的值可以是Dp中的维度,也可以是“wrap”,它将使用与WRAP_CONTENT相同的值。
要使用百分比,您需要设置以下内容:
1.尺寸应设置为MATCH_CONSTRAINT(0dp)
2.默认值应设置为app app:layout_constraintWidth_default =“percent”或app:layout_constraintHeight_default =“percent”
3.然后将layout_constraintWidth_percent或layout_constraintHeight_percent属性设置为0到1之间的值
您还可以将窗口小部件的一个维度定义为另一个维度的比率。 为此,您需要将至少一个约束维度设置为0dp(即MATCH_CONSTRAINT),并将属性layout_constraintDimensionRatio设置为给定比率。 例如:
将按钮的高度设置为与其宽度相同。
该比率可表示为:
浮点值,表示宽度和高度之间的比率
“宽度:高度”形式的比率
如果两个尺寸都设置为MATCH_CONSTRAINT(0dp),您也可以使用比率。 在这种情况下,系统设置满足所有约束的最大尺寸并保持指定的纵横比。 要根据另一个的尺寸约束一个特定边,可以预先附加W,“或H,分别约束宽度或高度。例如,如果一个维度受两个目标约束(例如,宽度为0dp并且以父对象为中心) 你可以通过在比率前添加字母W(用于约束宽度)或H(用于约束高度)来指示哪一边应该被约束,用逗号分隔:
将按照16:9的比例设置按钮的高度,而按钮的宽度将匹配父项的约束
链在单轴(水平或垂直)上提供类似组的行为。另一个轴可以独立地约束。
一组小部件被认为是一个链,如果它们通过双向连接被连接在一起(参见图,显示最小链,带有两个小部件)。
链由链的第一个元素(链的“头”)所设定的属性控制:
头部是水平链最左边的小部件,也是垂直链最顶端的小部件。
如果在连接上指定了边距,则会考虑它们。 在扩散链的情况下,将从分配的空间中扣除边距。
在链的第一个元素上设置属性layout_constraintHorizontal_chainStyle或layout_constraintVertical_chainStyle时,链的行为将根据指定的样式更改(默认为CHAIN_SPREAD)。
1.CHAIN_SPREAD - 元素将展开(默认样式)
加权链 - 在CHAIN_SPREAD模式下,如果某些小部件设置为2.MATCH_CONSTRAINT,它们将拆分可用空间
3.CHAIN_SPREAD_INSIDE - 类似,但链的端点不会分散
4.CHAIN_PACKED - 链的元素将被打包在一起。 然后,子项的水平或垂直偏差属性将影响打包元素的定位
链的默认行为是在可用空间中平均分布元素。如果一个或多个元素使用MATCH_CONSTRAINT,它们将使用可用的空白空间(在它们之间平均分配)。 layout_constraintHorizontal_weight和layout_constraintVertical_weight属性将控制如何使用MATCH_CONSTRAINT在元素之间分配空间。例如,在使用MATCH_CONSTRAINT的包含两个元素的链上,第一个元素使用权重2,第二个元素使用权重1,第一个元素占用的空间将是第二个元素占用的空间的两倍。
在链中的元素上使用边距时,边距是相加的。
例如,在水平链上,如果一个元素定义了10dp的右边距,而下一个元素定义了5dp的左边距,则这两个元素之间产生的边距为15dp。
在计算链用于定位项目的剩余空间时,会同时考虑项目及其边距。剩余空间不包含边距。
除了前面详述的内在功能之外,您还可以使用ConstraintLayout中的特殊帮助程序对象来帮助您进行布局。 目前,Guideline对象允许您创建相对于ConstraintLayout容器定位的水平和垂直指南。 然后可以通过将小部件限制为这样的指导来定位小部件。
在1.1中我们公开了约束优化器。 您可以通过将标记app:layout_optimizationLevel添加到ConstraintLayout元素来决定应用哪些优化。
none:不应用任何优化
standard :默认。 仅优化直接和屏障约束
direct:优化直接约束
barrier:优化障碍限制
chain:优化链约束(实验)
dimensions:优化维度度量(实验),减少匹配约束元素的度量数量
这里分别以:app:layout_constraintLeft_toLeftOf和app:layout_constraintRight_toLeftOf举例:
定义两个控件:
这里的id为where的TextView就使用了app:layout_constraintLeft_toLeftOf相对于另一个TextView,这个时候id为where的TextView的显示的位置,其实是左边贴在了id为right_top的TextView的左边上。如下图:
这个时候如果将id为where的相对于id为right_top的修改成app:layout_constraintRight_toLeftOf,如下所示:
那么此时显示的样式,就是id为where的TextView相对于id为right_top的TextView的右边显示。如下图:
这个属性,是让A和B两个控件的基准线在同一个水平位置上,比如两个TextView,大小不同,字体的大小也不同,那么想要让这样两个不同大小不同字体大小的文本内容显示在同一个基线,则可以使用该属性。app:layout_constraintBaseline_toBaselineOf
在约束布局中,margin控制需要存在约束关系的才会生效,比如控件A某条边相对于控件B的某条边存在约束关系,则控件A与B之间的margin才会生效。
这里使用了两种margin属性,一种就是app:layout_goneMarginEnd,这个只在right_top被gone的时候生效;一种就是android:layout_marginEnd,任何时候都生效。
但是这里的margin需要生效,只有id为where的TextView被id为right_top的TextView所约束,那么id为where的TextView的margin相对于id为right_top的TextView的才生效。而id为right_top的TextView并没有被id为where的TextView约束,所以id为right_top的TextView的margin_start其实看不到生效。这里其实可以认为right_top的margin_start生效了,只不过是相对于parent的左边,但是因为right_top在parent的右上角,所以看不到这个margin效果
这两个属性单独使用并不能生效,水平方向的必须要给控件的左右两边都添加约束才会生效,垂直方向的必须要给控件的上下两边都添加约束才可以生效。
比如这个例子,Button的左边被TextView的右边约束,Button的右边被parent约束,这样Button的左右两边都有约束,那么给Button添加水平方向的bias属性就可以生效,即根据Button的左边约束偏移一定的比例,这里就是相对于TextView的右边位置偏移40%。
bias的偏移,是根据控件的水平或者垂直方向的剩余位置的百分比来偏移。
如果是在ConstraintLayout中居中,则:
如果是在两个控件的中间,则可以:
设置宽高百分比,首先设置app:layout_constraintHeight_default="percent"采用百分比形式,其次使用app:layout_constraintHeight_percent="0.3"设置一个0-1的百分比值,最后控件的宽高需要设置为 0dp ,只有宽高是设置为 0dp 的,百分比才生效。
在约束布局中,给控件的左右两边或者上下两边添加约束之后,给控件的宽高设置为0dp的时候,可以占满两个约束控件中间所有剩余空间。
比如这个布局,我们需要EditText在TextView的右边,如果不给EditText设置0dp的宽度,而是设置了match_parent的话,则会占满屏幕宽度,没办法实现EditText在TextView的右边
在ConstraintLayout中给控件设置min的宽度和高度,必须是要控件的layout_width或者layout_height为wrap_content或者0dp时。给控件设置max的宽度或者高度的时候,必须是要控件的layout_width或者layout_height为0dp时。
不过在设置max的时候需要注意一点,如果单纯只是设置了0dp,如果没给控件添加对应的左右约束或者上下约束,比如没有设置左右约束,那么layout_constraintWidth_max设置的再大也不会超过整个控件的wrap_content的长度。
这里的绿色区域的控件,宽度显示的明显没有达到550dp,但是也不会继续变长了。
如果是设置了左右约束,那么最大宽度的限制也就是左右两个约束中间的剩余空间宽度
同时给Guide设置layout_constraintGuide_percent和layout_constraintGuide_begin的时候,layout_constraintGuide_begin并不会生效。
layout_constraintGuide_begin是给Guide设置相对于开始位置的偏移,layout_constraintGuide_end是给Guide设置相对于结束位置的偏移。
layout_constraintGuide_percent是设置Guide设置相对于起始位置的偏移百分比。
Guide的作用就是为了给控件的提供一个约束参考线,控件可以依靠这个线条约束。
app:constraint_referenced_ids这里是添加约束的控件的id,app:barrierDirection是添加约束的位置,可以有end、start、left、right、top、bottom
添加不同的值,就可以让Barrier线条在约束的控件的对应位置上,比如end,就是让Barrier线条在username1和password1这两个控件的右边结束位置
这两个都是线条,都是辅助约束的,但是这两个有一点区别,就是当控件比如出现切换手机语言,而造成控件上的文本显示长度出现变化的时候,Guideline并不会随着控件的长度变化而变化,这样就会造成约束不灵活,而Barrier可以根据控件的宽高变化,灵活移动位置。
所以控件宽高是随着内容动态变化的,建议使用Barrier,如果控件的内容是不变的,可以使用Guideline。