http://blog.csdn.net/rwecho/article/details/8951009

Android开发中的布局很重要吗?那是当然。一切的显示样式都是由这个布局决定的,你说能不重要吗。要实现一个好的布局,不只是实现了、显示出来就完了,不管层次,堆砌代码也可以实现功能,但是这显然违背了Android布局设计的原则。可能你会说,Android布局设计哪有什么原则,我可以明确告诉你,当然有,只要有利于提高最终效果的方法、意识,我们都可以把它提升为原则。在Android布局设计中,这个最终效果就是快的页面加载速度,好的流畅度,而这个方法就太多了,鉴于本人水平有限,下面就说几种自己实践过的、切实有效的方法吧。前奏太长,罗哩罗嗦,好吧,开始说起。

最近做演出项目,碰到了一个以前没见过的错误StackOverflowError,具体错误如下:

查过资料之后才发现,原因就出在布局的嵌套层次上,说明白点儿就是“嵌套层次过深,导致栈溢出”,这样就引出了一个问题,我们在Android开发中经常听到的是内存泄漏、堆溢出(听起来都头皮发麻的OOM),好像很少听说有栈溢出的,好了,找找Android中对栈大小的限制吧。抱歉,没找到,只在支离破碎的国外论坛中间看到有说1K的,有说4K的,反正没看到有官方的说明,纠结良久还是一筹莫展,看哪位大虾如果知道的话就指点一下本鸟吧。。。。。。既然遇到了问题,还是转回头来解决问题吧,无法提高栈的大小,那就只能提高栈的使用率了,最直接的方法就是缩减层级,而且缩减层级还有一个重要的好处就是提高页面加载速度,因为Android中的布局是嵌套加载的,多一层布局就要耗费很长的加载时间。这时候我才体会到了merge的妙用,不只是merge,下面我会结合hierarchyviewer工具来分别讲述一下merge,ViewStub,include在布局优化中的作用。

1、merge

顾名思义,就是合并、融合的意思。使用它可以有效的将某些符合条件的多余的层级优化掉。使用merge的场合主要有两处:

(1) 自定义View中使用,父元素尽量是FrameLayout,当然如果父元素是其他布局,而且不是太复杂的情况下也是可以使用的

(2) Activity中的整体布局,根元素需要是FrameLayout

下面这个例子将融合这两种情况,来展示如何缩减布局层次。

总体显示界面如图所示:

其中粉红色圈住的部分为我们的自定义View。

整个界面的布局layout_mergedemo.xml如下:

[html] view plaincopy

 
  1. <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools"
  3. android:layout_width="fill_parent"
  4. android:layout_height="fill_parent">
  5. <TextView
  6. android:layout_width="fill_parent"
  7. android:layout_height="wrap_content"
  8. android:textSize="25sp"
  9. android:textColor="#FF0000FF"
  10. android:gravity="center_horizontal"
  11. android:text="我的家园"/>
  12. <com.example.myandroiddemo.MyItemView
  13. android:id="@+id/myitem_view"
  14. android:layout_width="fill_parent"
  15. android:layout_height="wrap_content"
  16. android:layout_gravity="center" />
  17. </FrameLayout>

自定义布局view_item.xml如下:

[html] view plaincopy

 
  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools"
  3. android:layout_width="fill_parent"
  4. android:layout_height="fill_parent"
  5. android:orientation="horizontal" >
  6. <ImageView
  7. android:id="@+id/view_item_img"
  8. android:layout_width="50dp"
  9. android:layout_height="50dp"
  10. android:scaleType="fitXY"/>
  11. <TextView
  12. android:id="@+id/view_item_txt"
  13. android:layout_width="fill_parent"
  14. android:layout_height="50dp"
  15. android:gravity="center_vertical"
  16. android:layout_marginLeft="10dp"
  17. android:textSize="20sp"
  18. android:textColor="#FFFF0000"/>
  19. </LinearLayout>

自定义View中使用下面来解析布局:

[java] view plaincopy

 
  1. public class MyItemView extends LinearLayout {
  2. ......
  3. private void initView(Context context) {
  4. mContext = context;
  5. View view = LayoutInflater.from(mContext).inflate(R.layout.view_item, this, true);
  6. mMyItemImg = (ImageView)view.findViewById(R.id.view_item_img);
  7. mMyItemText = (TextView)view.findViewById(R.id.view_item_txt);
  8. }
  9. ......
  10. }

整个功能开发完成之后,使用hierarchyviewer来看一下布局层次:

我们发现简单的一个功能竟然使用了六层布局,包括每个Window自动添加的PhoneWindow$DecorView和FrameLayout(id/content)。明显可以看到这个布局存在冗余,比如第二层和第三层的FrameLayout,比如第四层的MyItemView(LinearLayout子类)和第五层的LinearLayout,都可以缩减。

好了,该我们的merge标签大显身手了。将布局这样修改:

简化layout_mergedemo.xml:

[html] view plaincopy

 
 
  1. <merge xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools">
  3. <TextView
  4. android:layout_width="fill_parent"
  5. android:layout_height="wrap_content"
  6. android:textSize="25sp"
  7. android:textColor="#FF0000FF"
  8. android:gravity="center_horizontal"
  9. android:text="我的家园"/>
  10. <com.example.myandroiddemo.MyItemView
  11. android:id="@+id/myitem_view"
  12. android:layout_width="fill_parent"
  13. android:layout_height="wrap_content"
  14. android:layout_gravity="center" />
  15. </merge>

简化view_item.xml:

[html] view plaincopy

 
  1. <merge xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools" >
  3. <ImageView
  4. android:id="@+id/view_item_img"
  5. android:layout_width="50dp"
  6. android:layout_height="50dp"
  7. android:scaleType="fitXY"/>
  8. <TextView
  9. android:id="@+id/view_item_txt"
  10. android:layout_width="fill_parent"
  11. android:layout_height="50dp"
  12. android:gravity="center_vertical"
  13. android:layout_marginLeft="10dp"
  14. android:textSize="20sp"
  15. android:textColor="#FFFF0000"/>
  16. </merge>

因为这里merge代替的布局元素为LinearLayout,而不是FrameLayout,所以我们需要在自定义布局代码中将LinearLayout的属性添加上,比如垂直或者水平布局,比如背景色等,此处设置为水平布局:

[java] view plaincopy

 
 
  1. private void initView(Context context) {
  2. setOrientation(LinearLayout.HORIZONTAL);
  3. ……
  4. }

OK,就是这么简单,再来看看布局层次吧:

哈哈,只有四层了,加载速度也明显加快,特别是如果布局比较复杂,子View较多的情况下,合理使用merge能大大提高程序的速度和流畅性。

但是使用merge标签还是有一些限制的,具体有以下几点:

(1)merge只能用在布局XML文件的根元素

(2)使用merge来inflate一个布局时,必须指定一个ViewGroup作为其父元素,并且要设置inflate的attachToRoot参数为true。(参照inflate(int, ViewGroup, boolean))

(3)不能在ViewStub中使用merge标签。最直观的一个原因就是ViewStub的inflate方法中根本没有attachToRoot的设置

说到这儿,merge的使用也就讲完了。还想唠叨一下做的演出项目,因为本人水平有限,并且产品提的这个显示需求确实比较BT,所以整个项目的显示框架做了很多嵌套,具体点就是ActivityGroup中嵌套ViewFlipper,然后再嵌套TabHost,然后再嵌套自己的Activity,大体数了一下,最多竟然有20几层,My God!知道Android布局设计的原则是什么吗?最好是10层以下,尽量不要超过15层,如果再多性能就会下降,也可能会出现问题,就是我们看到的StackOverFlow,这个项目被证实也确实在某些低端机上发生了这种错误,比如HTC的某某机型,OPPO的某某机型,(不要声讨我,没有恶意贬低的意思<_>),最终我使用merge缩呀缩呀,把大部分的布局都缩减到了15层以下,一测试,通过!

还要说一下,因为Window窗体(比如Activity)加载时会自动添加PhoneWindow$DecorView和FrameLayout(id/content)两层布局,所以如果我们在Activity的自定义布局根元素中使用merge,而且想设置总体背景什么的,可以用(id/content)将FrameLayout取出来,再设置属性,可以这样实现:

[java] view plaincopy

 
  1. //setContentView(R.layout.layout_showset);
  2. FrameLayout frameLayout = (FrameLayout)this.getWindow().getDecorView().findViewById(android.R.id.content);
  3. frameLayout.setBackgroundResource(R.drawable.bg_repeated_main);
  4. LayoutInflater.from(this).inflate(R.layout.layout_showset, frameLayout, true);

将setContentView方法摒弃,改由我们手动添加布局。

好了,就这样吧!!!

2、ViewStub

这是什么玩应儿呢?其实就是一个轻量级的页面,我们通常使用它来做预加载处理,来改善页面加载速度和提高流畅性,ViewStub本身不会占用层级,它最终会被它指定的层级取代。
       还是说说演出项目吧,还说?对了,实践才能发现问题嘛,在哪儿发现问题就在那儿改进。由于项目中用到了比较多的动画,而且嵌套布局比较复杂,所以在Android低端机上进行页面切换时候经常让人感觉卡卡的,不怎么流畅,因为页面切换动画和标题旋转动画是同时进行的,所以为了达到更好的体验就需要使用一种方法,在动画进行时尽量的减少其他操作,特别是页面加载重绘。赶紧想办法,起初我想先将要加载的页面所有的组件都初始为gone显示状态,整个页面只留一下加载滚动条,后来发现这是不行滴,因为在Android的机制里,即使是将某一个控件的visibility属性设置为不可见的gone,在整个页面加载过程中还是会加载此控件的。再后来就用到了ViewStub,在做页面切换动画时,只在页面中放一个loading加载图标和一个ViewStub标签,像下面这样:

layout_loading.xml布局文件:

[html] view plaincopy

 
  1. <merge xmlns:android="http://schemas.android.com/apk/res/android">
  2. <ViewStub
  3. android:id="@+id/viewstub"
  4. android:layout_width="fill_parent"
  5. android:layout_height="fill_parent"/>
  6. <NetErrAndLoadView
  7. android:id="@+id/start_loading_lay"
  8. android:layout_width="fill_parent"
  9. android:layout_height="fill_parent" />
  10. </merge>

这个页面是相当轻量级的,所以导致动画加载速度非常快、而且流畅。等页面切换动画完成之后,我们再指定ViewStub的资源,来加载实际的页面,这个资源就是实际要加载的页面的布局文件。比如要加载MainActivity的布局文件layout_main.xml,onCreate实现如下:

[java] view plaincopy

 
  1. protected void onCreate(Bundle savedInstanceState) {
  2. super.onCreate(savedInstanceState);
  3. setContentView(R.layout.layout_loading);
  4. mLoadHandler = new Handler();
  5. mLoadingView = (NetErrAndLoadView)findViewById(R.id.start_loading_lay);
  6. mLoadingView.startLoading();
  7. mViewStub = (ViewStub)findViewById(R.id.viewstub);
  8. mViewStub.setLayoutResource(R.layout.layout_main);
  9. mLoadHandler.postDelayed(new Runnable() {
  10. @Override
  11. public void run() {
  12. mViewStub.inflate();
  13. mLoadingView.hide();
  14. }
  15. },500);
  16. }

上面的500单位是ms,就是延迟加载的时间。上面使用的是动态添加ViewStub指向布局资源的方法(mViewStub.setLayoutResource(R.layout.layout_main)),当然根据需要可以直接在ViewStub的布局块儿中设置,需要设置ViewStub标签下的layout属性(android:layout="@layout/ layout_main")。
        ViewStub也是有少许缺点的,下面所说:
        1、  ViewStub只能Inflate一次,之后ViewStub对象会被置为空。按句话说,某个被ViewStub指定的布局被Inflate后,就不能够再通过ViewStub来控制它了。所以它不适用     于需要按需显示隐藏的情况。
       2、  ViewStub只能用来Inflate一个布局文件,而不是某个具体的View,当然也可以把View写在某个布局文件中。如果想操作一个具体的view,还是使用visibility属性吧。
       3、  VIewStub中不能嵌套merge标签。(前面好像说过)
不过这些确定都无伤大雅,我们还是能够用ViewStub来做很多事情。
        有时候真的不得不佩服google的Android团队的远见性和架构性,这种细微的设计都能考虑的到,想用就有。

3、include

制造这个标签纯碎是为了布局重用,在项目中通常会存在一些布局公用的部分,比如自定义标题,我们不需要把一份代码Ctrl C, Ctrl V的到处都是,严重违背程序简洁化、模块儿化的设计思想,毕竟作为现代化的码农,也需要与时俱进,不再抱着以前那种卖代码的思想来编写程序了(一行代码几个钱?)。这里就不写例子了,随便网上搜一下,include的使用一大堆,简单说一下如何调用吧,比如现在有一个公用布局head.xml,如果在其他的布局中引用,只需要这样添加include标签:

[html] view plaincopy

 
  1. <include android:layout_width="fill_parent" android:layout_height="wrap_content" layout="@layout/head" />

这个标签下的布局在父布局刷新时候同样是会加载的,所以它即不能像merge那样缩减层级,也不能像ViewStub那样轻量级实现延迟加载,它就是用来布局重用的,各有各的疗效嘛,可以理解。

OK,到这儿我也啰嗦完了,现在脑子里只有一个想法,既然Android的设计者都绞尽脑汁帮我们想到了各种便利用途,我们再不使用就不单是脑子有问题的问题了,就是没把用户需求放在第一位,是得罪上帝的。哈哈,说大了,轻喷。。。。。。不过这几个标签确实很有用,建议在合适的地方都体验一下,还是那句话,谁用谁知道<_>。
 
说明:以上都是基础内容,只不过可能注意用的人不多,这些标签的使用我也是学习,然后用到了项目当中,本人只是结合自己的理解叙述一遍。如果有更好的改进方法,或者是认为上面的文字有误,欢迎交流。

最新文章

  1. 2015暑假多校联合---CRB and His Birthday(01背包)
  2. ArcGIS server 开发实践之【FeatureLayer类】
  3. (十) 一起学 Unix 环境高级编程 (APUE) 之 线程控制
  4. 王垠:完全用Linux工作
  5. 【HTML5】炫丽的时钟效果Canvas绘图与动画基础练习
  6. 用Javascript编写Chrome浏览器插件
  7. protobuf 作为配置文件
  8. 【HDU1198】Farm Irrigation(回溯+记忆化搜索)
  9. swift 图像的压缩上传
  10. inno setup 1
  11. 终结 finalize()和垃圾回收(garbage collection)
  12. Java读取文件存储到mysql
  13. nyoj 鸡兔同笼
  14. Kubernetes 笔记 07 豌豆荚之旅(二)
  15. OSS文件上传到阿里云
  16. 通过Java构造参数列表
  17. BZOJ.4939.[Ynoi2016]掉进兔子洞(莫队 bitset 分组询问)
  18. Centos6.8操作防火墙
  19. 纯绿色 jsonUtil工具
  20. NLTK在自然语言处理

热门文章

  1. windows Batch 脚本的一些常用有效
  2. Apache CXF 102 CXF with REST
  3. safari穿越到chrome
  4. codeforces hungry sequence 水题
  5. root密码
  6. Codeforces Round #108 (Div. 2)
  7. php文件遍历类:FileBianli.class.php
  8. hdu1078  记忆化搜索(DP+DFS)
  9. caffe: compile error : undefined reference to `cv::imread(cv::String const&amp;, int)&#39; et al.
  10. java DecimalFormat