android布局优化

一、原理:尽量减少过度绘制(Overdraw)

     过度绘制:Overdraw就是过度绘制,是指在一帧的时间内(16.67ms)像素被绘制了多次,理论上一个像素每次只绘制一次是最优的,但是由于重叠的布局导致一些像素会被多次绘制,而每次绘制都会对应到CPU的一组绘图命令和GPU的一些操作,当这个操作耗时超过16.67ms时,就会出现掉帧现象,也就是我们所说的卡顿,所以对重叠不可见元素的重复绘制会产生额外的开销,需要尽量减少Overdraw的发生。


1.每个view都会被CPU计算成Polygons,Texture纹理


2.Android需要把XML布局文件转换成GPU能够识别并绘制的对象。这个操作是在DisplayList的帮助下完成的。      DisplayList持有所有将要交给GPU绘制到屏幕上的数据信息。


3.GPU会把纹理进行渲染,之后由硬件展示


4.因为从CPU到GPU是一件很麻烦的事,所以GPU会缓存纹理,如果view的绘制内容没有经过改动可以直接使用       缓存中的纹理进行渲染,如果改动就会重新执行创建DisplayList(CPU),渲染DisplayList(GPU),更新到屏幕上等一系列操作。这个流程的表现性能取决于View的复杂程度,View的状态变化以及渲染管道的执行性能。

     

所以我们需要尽量减少Overdraw。


二、优化方法

综上,布局的优化其实说白了就是减少层级,越简单越好,减少overdraw,就能更好的突出性能。


1.合理选择控件容器,尽量减少布局嵌套,减少layout的层级

2.使用抽象布局标签:include、merge、viewstub

2.1、首先是include标签

  include标签常用于将布局中的公共部分提取出来,比如我们要在activity_main.xml中需要上述LinearLayout的数据,那么就可以直接include进去了。在<include>标签当中,我们是可以覆写所有layout属性的,即include中指定的layout属性将会覆盖掉原布局文件中指定的layout属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<? xml  version="1.0" encoding="utf-8"?>
< RelativeLayout  xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     android:id="@+id/activity_main"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:paddingBottom="@dimen/activity_vertical_margin"
     android:paddingLeft="@dimen/activity_horizontal_margin"
     android:paddingRight="@dimen/activity_horizontal_margin"
     android:paddingTop="@dimen/activity_vertical_margin"
     tools:context="com.jared.layoutoptimise.MainActivity">
 
     < include  layout="@layout/item_test_linear_layout" />
     
</ RelativeLayout >
 2.2、merge标签:

   merge标签是作为include标签的一种辅助扩展来使用,它的主要作用是为了防止在引用布局文件时产生多余的布局嵌套。
  Android渲染需要消耗时间,布局越复杂,性能就越差。如上述include标签引入了之前的LinearLayout之后导致了界面多了一个层级。

 这个时候用merge的话,就可以减少一个层级了,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<? xml  version="1.0" encoding="utf-8"?>
< merge  xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
 
     < ImageView
         android:id="@+id/iv_image"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_margin="10dp"
         android:src="@mipmap/ic_launcher" />
 
     < TextView
         android:id="@+id/tv_title"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_marginLeft="10dp"
         android:layout_marginTop="16dp"
         android:layout_toRightOf="@+id/iv_image"
         android:text="这个是MergeLayout"
         android:textSize="16sp" />
 
     < TextView
         android:id="@+id/tv_content"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_below="@+id/tv_title"
         android:layout_marginLeft="10dp"
         android:layout_marginTop="10dp"
         android:layout_toRightOf="@+id/iv_image"
         android:text="这个是MergeLayout,这个是MergeLayout"
         android:textSize="12sp" />
 
</ merge >

  activity_main就可以直接include了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
< RelativeLayout  xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     android:id="@+id/activity_main"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:paddingBottom="@dimen/activity_vertical_margin"
     android:paddingLeft="@dimen/activity_horizontal_margin"
     android:paddingRight="@dimen/activity_horizontal_margin"
     android:paddingTop="@dimen/activity_vertical_margin"
     tools:context="com.jared.layoutoptimise.MainActivity">
 
     < include  layout="@layout/item_merge_layout" />
 
</ RelativeLayout >

2.3、viewstub标签:

  viewstub是view的子类。他是一个轻量级View, 隐藏的,没有尺寸的View。他可以用来在程序运行时简单的填充布局文件。接着简单试用下viewstub吧。首先修改activity_main.xml文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<? xml  version="1.0" encoding="utf-8"?>
< RelativeLayout  xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     android:id="@+id/activity_main"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:paddingBottom="@dimen/activity_vertical_margin"
     android:paddingLeft="@dimen/activity_horizontal_margin"
     android:paddingRight="@dimen/activity_horizontal_margin"
     android:paddingTop="@dimen/activity_vertical_margin"
     tools:context="com.jared.layoutoptimise.MainActivity">
 
     < include
         android:id="@+id/layout_merge"
         layout="@layout/item_merge_layout" />
 
     < Button
         android:id="@+id/btn_view_show"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_marginTop="10dp"
         android:text="显示ViewStub"
         android:textAllCaps="false"
         android:layout_below="@+id/tv_content"/>
 
     < Button
         android:id="@+id/btn_view_hide"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_marginTop="10dp"
         android:layout_marginLeft="50dp"
         android:layout_toRightOf="@+id/btn_view_show"
         android:text="隐藏ViewStub"
         android:textAllCaps="false"
         android:layout_below="@+id/tv_content"/>
 
     < ViewStub
         android:id="@+id/vs_test"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:layout="@layout/item_test_linear_layout"
         android:layout_below="@+id/btn_view_show"
         android:layout_marginTop="10dp" />
 
</ RelativeLayout >

  这里的ViewStub控件的layout指定为item_test_linear_layout。当点击button隐藏的时候不会显示item_test_linear_layout,而点击button显示的时候就会用item_test_linear_layout替代ViewStub。


3.去掉window的默认背景

    当我们使用了Android自带的一些主题时,window会被默认添加一个纯色的背景,这个背景是被DecorView持有的。当我们的自定义布局时又添加了一张背景图或者设置背景色,那么DecorView的background此时对我们来说是无用的,但是它会产生一次Overdraw,带来绘制性能损耗。
去掉window的背景可以在onCreate()中setContentView()之后调用

getWindow().setBackgroundDrawable(null);
或者在theme中添加:


android:windowbackground="null";


4.去掉其他不必要的背景

     有时候为了方便会先给Layout设置一个整体的背景,再给子View设置背景,这里也会造成重叠,如果子View宽度mach_parent,可以看到完全覆盖了Layout的一部分,这里就可以通过分别设置背景来减少重绘。再比如如果采用的是selector的背景,将normal状态的color设置为“@android:color/transparent",也同样可以解决问题。这里只简单举两个例子,我们在开发过程中的一些习惯性思维定式会带来不经意的Overdraw,所以开发过程中我们为某个View或者ViewGroup设置背景的时候,先思考下是否真的有必要,或者思考下这个背景能不能分段设置在子View上,而不是图方便直接设置在根View上。


5.善用draw9patch

    给ImageView加一个边框,你肯定遇到过这种需求,通常在ImageView后面设置一张背景图,露出边框便完美解决问题,此时这个ImageView,设置了两层drawable,底下一层仅仅是为了作为图片的边框而已。但是两层drawable的重叠区域去绘制了两次,导致overdraw。
优化方案: 将背景drawable制作成draw9patch,并且将和前景重叠的部分设置为透明。由于Android的2D渲染器会优化draw9patch中的透明区域,从而优化了这次overdraw。 但是背景图片必须制作成draw9patch才行,因为Android 2D渲染器只对draw9patch有这个优化,否则,一张普通的Png,就算你把中间的部分设置成透明,也不会减少这次overdraw。


6.慎用Alpha

假如对一个View做Alpha转化,需要先将View绘制出来,然后做Alpha转化,最后将转换后的效果绘制在界面上。通俗点说,做Alpha转化就需要对当前View绘制两遍,可想而知,绘制效率会大打折扣,耗时会翻倍,所以Alpha还是慎用。
如果一定做Alpha转化的话,可以采用缓存的方式。

view.setLayerType(LAYER_TYPE_HARDWARE);
doSmoeThing();
view.setLayerType(LAYER_TYPE_NONE);
通过setLayerType方式可以将当前界面缓存在GPU中,这样不需要每次绘制原始界面,但是GPU内存是相当宝贵的,所以用完要马上释放掉。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值