您的位置:首页 > 移动开发 > Android开发

android之布局优化

2015-07-29 16:59 330 查看
标签(空格分隔): android 性能优化 xml

前期知识储备

1. LayoutInflate

主要是用于加载布局的.其实setContentView()方法的内部也是使用LayoutInflater来加载布局的.只不过这部分源码是internal的,不太容易查看到。

LayoutInflater技术广泛应用于需要动态添加View的时候,比如在ScrollView和ListView中,经常都可以看到LayoutInflater的身影。

LayoutInflater其实就是使用Android提供的pull解析方式来解析布局文件的.

在setContentView()方法中,Android会自动在布局文件的最外层再嵌套一个FrameLayout.

public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs){ ... }


平时我们经常使用layout_width和layout_height来设置View的大小,就好像这两个属性确实是用于设置View的大小的。而实际上则不然,它们其实是用于设置View在布局中的大小的.也就是说,首先View必须存在于一个布局中,之后如果将layout_width设置成match_parent表示让View的宽度填充满布局,如果设置成wrap_content表示让View的宽度刚好可以包含其内容,如果设置成具体的数值则View的宽度会变成相应的数值。这也是为什么这两个属性叫作layout_width和layout_height,而不是width和height。

2.View绘制流程

每一个视图的绘制过程都必须经历三个最主要的阶段,即onMeasure()、onLayout()和onDraw()。

2.1 onMeasure()

其内部调用View的measure()方法。

measure()方法接收两个参数,widthMeasureSpec和heightMeasureSpec,这两个值分别用于确定视图的宽度和高度的规格和大小。

measure()这个方法是final的,因此我们无法在子类中去重写这个方法,说明Android是不允许我们改变View的measure框架的。

MeasureSpec的值由specSize和specMode共同组成的,其中specSize记录的是大小,specMode记录的是规格。

说明父视图会在一定程度上决定子视图的大小。

根视图总是会充满全屏的。

一个界面的展示可能会涉及到很多次的measure,因为一个布局中一般都会包含多个子视图,每个视图都需要经历一次measure过程。ViewGroup中定义了一个measureChildren()方法来去测量子视图的大小。

onMeasure()方法是可以重写的,也就是说,如果你不想使用系统默认的测量方式,可以按照自己的意愿进行定制,比如:

public class MyView extends View {
......
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(200, 200);
}
}


这样的话就把View默认的测量流程覆盖掉了,不管在布局文件中定义MyView这个视图的大小是多少,最终在界面上显示的大小都将会是200*200。

需要注意的是,在setMeasuredDimension()方法调用之后,我们才能使用getMeasuredWidth()和getMeasuredHeight()来获取视图测量出的宽高,以此之前调用这两个方法得到的值都会是0。

由此可见,视图大小的控制是由父视图、布局文件、以及视图本身共同完成的,父视图会提供给子视图参考的大小,而开发人员可以在XML文件中指定视图的大小,然后视图本身会对最终的大小进行拍板。

2.2 onLayout()

这个方法是用于给视图进行布局的,也就是确定视图的位置.

getWidth()方法和getMeasureWidth()方法到底有什么区别呢?首先getMeasureWidth()方法在measure()过程结束后就可以获取到了,而getWidth()方法要在layout()过程结束后才能获取到。另外,getMeasureWidth()方法中的值是通过setMeasuredDimension()方法来进行设置的,而getWidth()方法中的值则是通过视图右边的坐标减去左边的坐标计算出来的。

2.3 onDraw()

对视图进行绘制。

View是不会帮我们绘制内容部分的,因此需要每个视图根据想要展示的内容来自行绘制.

重写onDraw(),绘制的方式主要是借助Canvas这个类,它会作为参数传入到onDraw()方法中,供给每个视图使用。

3.view的机制

3.1 view的状态

enabled

focused

selected

window_focused

pressed

3.2 视图重绘

invalidate()

调用视图的invalidate()方法后确实会走到performTraversals()方法中,然后重新执行绘制流程。

invalidate()方法虽然最终会调用到performTraversals()方法中,但这时measure和layout流程是不会重新执行的,因为视图没有强制重新测量的标志位,而且大小也没有发生过变化,所以这时只有draw流程可以得到执行。而如果你希望视图的绘制流程可以完完整整地重新走一遍,就不能使用invalidate()方法,而应该调用requestLayout()了。这个方法中的流程比invalidate()方法要简单一些,但中心思想是差不多的,这里也就不再详细进行分析了。

4. 自定义view

4.1 完全自定义View控件

4.2 组合控件

4.3 继承控件

布局优化方法

1.布局标签灵活使用(详细使用方法和场景参考底部blog)

include

merge

viewstub

2.布局调优工具

hierarchy viewer

lint

优化建议

去除不必要的嵌套和View节点

减少不必要的infalte

使用RenderScript

使用OpenGL绘图

尽量为所有分辨率创建资源-减少不必要的硬件缩放,这会降低UI的绘制速度,可借助Android asset studio。

尽量多使用RelativeLayout和LinearLayout,在布局层次一样的情况下, 建议使用LinearLayout代替RelativeLayout, 因为LinearLayout性能要稍高一点,但往往RelativeLayout可以简单实现LinearLayout嵌套才能实现的布局。

需要注意的地方

所有xml界面在通过setContentView()时,会把FrameLayout作为根节点插入。(思考:将setContentView()注释后,运行App,可正常显示,一块白色区域而已,界面渲染前状态,这块白色区域是什么?),因此,当xml中的根节点为FrameLayout时,并且没有其它特殊属性,可去掉,再用《merge》 来包裹。

一些blog上说多使用RelativeLayout,并不是因为它性能高,而是可避免使用复杂的层级关系。

使用include最常见的问题就是findViewById查找不到目标控件,如下,先设置include标签的id,编码中先找到include的id,在基于其找子id。

View titleView = findViewById(R.id.my_title_ly) ;
TextView titleTextView = (TextView)titleView.findViewById(R.id.title_tv) ;
titleTextView.setText("new Title");


参考文章:

http://blog.csdn.net/guolin_blog/article/details/17357967 郭霖的View系列文章(1~4)

http://www.trinea.cn/android/layout-performance/ Trinea性能优化之布局详细

http://www.stormzhang.com/android/2014/04/10/android-optimize-layout/ 布局优化

http://www.androidchina.net/2485.html 布局优化

http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0125/2356.html 推荐阅读较详细-布局优化

http://www.infoq.com/cn/articles/android-optimise-layout hierarchyviewer 的使用介绍

http://www.codekk.com/open-source-project-analysis/detail/Android/lightSky/%E5%85%AC%E5%85%B1%E6%8A%80%E6%9C%AF%E7%82%B9%E4%B9%8B%20View%20%E7%BB%98%E5%88%B6%E6%B5%81%E7%A8%8B View绘制流程的源码解析
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息