Android面试一天一题(Day 42:关于Android布局你不知道的)

Android常见的5个布局,我想大家一定不会陌生。LinearLayout、RelativeLayout和FrameLayout也是使用频率较高的布局方式,做Android开发的一定使用过。

传统的5种布局方式:

  • LinearLayout
  • RelativeLayout
  • FrameLayout
  • GridLayout
  • TableLayout

不过我的问题并不是问面试者如何使用这些基础的布局,而是要看面试者怎么解决布局嵌套(影响性能)和屏幕适配问题。

面试题:你是如何解决Android的布局嵌套问题的?

我们都清楚Android界面的布局太复杂,嵌套层次过深,会使整个界面的测量、布局和绘制变得更复杂,对性能会造成影响。所以我们在写Layout文件时,也要尽量避免布局的嵌套层次过深的问题。

在怎么解决问题之前,我们得有一个好方法先判断当前的问题情况。Android SDK工具箱中有一个叫做Hierarchy Viewer的工具,能够在App运行时分析Layout。

注意:在ROOT的手机,或者是安装开发版的ROM的手机可以直接使用Hierarchy Viewer。
如果没有Root的手机(SDK 4.1及以上),需要在你的PC端添加一个环境变量“ANDROID_HVPROTO=ddm”。

Mac系统的配置如下:


保存后运行:source ~/.bash_profile

最后可以DDMS的Hierarchy Viewer看到:


下面列举一些面试者常使用的方式。

merge
merge标签的作用是合并UI布局,使用该标签能降低UI布局的嵌套层次。

merge标签可用于两种情况:

  • 布局顶结点是FrameLayout且不需要设置background或padding等属性,可以用merge代替,因为Activity内容试图的parent view就是个FrameLayout,所以可以用merge消除只剩一个。
  • 某布局作为子布局被其他布局include时,使用merge当作该布局的顶节点,这样在被引入时顶结点会自动被忽略,而将其子节点全部合并到主布局中。

ViewStub
ViewStub标签引入的布局默认不会inflate,既不会显示也不会占用位置。 ViewStub常用来引入那些默认不会显示,只在特殊情况下显示的布局,如数据加载进度布局、出错提示布局等。

需要在使用时手动inflate:

ViewStub stub = (ViewStub)findViewById(R.id.error_layout);
errorView = stub.inflate();
errorView.setVisibility(View.VISIBLE);

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库:

compile 'com.android.support.constraint:constraint-layout:1.0.2'

Android Studio 2.3及之后的版本使用引导创建Empty的Activity时,默认就是使用ConstraintLayout布局。关于本面试题的答案,官方其实已经明确给出信号了。


使用ConstraintLayout可以有效的解决布局嵌套过多导致的性能问题,官方也对其渲染性能进行了优化,并且ConstraintLayout支持可视化的方式编写布局。

不过学会熟练使用ConstraintLayout会需要一点时间,但这是值得的。

官方文档:https://developer.android.com/training/constraint-layout/index.html

FlexBoxLayout
做过前端开发(CSS方面)的同学对FlexBox一定不会陌生,最近我在做微信小程序开发时也涉及到FlexBox。FlexBox(弹性布局)是w3c在2009年提出的一种新的布局方案,解决以前那种传统css的盒模型的局限性。

Google开源了FlexboxLayout布局和前端CSS FlexBox布局具有相同的功能(肯定有不一样的地方),但已经足够在Android上改进布局的构建方式。

项目地址:https://github.com/google/flexbox-layout

FlexBoxLayout可以理解成一种更高级的LinearLayout,不过比LinearLayout更加强大和灵活。如果我们使用LinearLayout布局的话,那么不同的分辨率,也许我们要重新调整布局,势必会需要跟多的布局文件放在不同的资源目录。而使用FlexBoxLayout来布局的话,它可以适应各种界面的改变(所以叫响应式布局)。

看一下官方图片感受一下:


如果对前端的Flexbox不太了解的话,你还需要补一些概念,好在这些东西在网上很容易找到。

小结

可能很多读者会觉这样的面试题是吹毛求疵,很多项目中哪有这么复杂的界面,根本就用不到这些优化措施。

So,你没有成长为厉害的人。

可以说厉害的人,或者叫高手,可能只是比较多在意这些细节而已。在实践中的经历告诉我,很多难于解决的性能问题,并不是因为有一个影响性能的问题无法攻克,而是没有一个明显的制约因素,是有各种小问题一点一点堆积起来,最终积重难返。

所以,把细节做好,或者意识到细节的地方可能引发的问题,对我们解决问题是很有帮助的,不要浪费了让你可以成长的细节。


相关阅读:

Android面试一天一题(Day 37:一套高级工程师的面试题)
Android面试一天一题(Day 30:老外的自定义View面试题)

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 138,244评论 20 591
  • 太长不看版:在 Android UI 布局过程中,遵守一些惯用、有效的布局原则,可以制作出高效且复用性高的 UI。...
    Mupceet阅读 1,822评论 1 10
  • 原文链接如下:http://blog.csdn.net/qq_34902522/article/details/7...
    宇宝守护神y阅读 362评论 0 1
  • 56 在三楼排队等待做内科检查。正对面有个大概一米六七左右的皮肤黝黑,瘦骨嶙峋的男生走过来。一身黑色运动装,后背背...
    青衣雨翼_shape阅读 19评论 0 0
  • 突然间,觉得生活其实是很无趣的。可是听说王小哈,愣是化腐朽为神奇了,她那曲曲折折、平平凡凡的小日子还有点趣味噻。 ...
    王红春不雨棠梨阅读 149评论 2 1