博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android开发——布局性能优化的一些技巧(一)
阅读量:4046 次
发布时间:2019-05-24

本文共 3951 字,大约阅读时间需要 13 分钟。

0. 前言

我们分析了为什么LinearLayout会比RelativeLayout性能更高,但是对于布局优化问题,对这两种布局的选择远不如减少布局层级、避免过分绘制、按需加载等效果明显。所以本篇将着重总结Android布局性能优化的各种技巧。

这里再补充一下为什么要布局优化。

Android系统中每16ms就发出一个VSYNC信号,触发UI渲染,要达到界面流程,大多数的操作必须在16ms内完成(对应60fps)。

本文原创,转载请注明出处:。

 

1.   减少嵌套

(1)在不响应层级深度的情况下,如果使用LinearLayout和RelativeLayout都可以实现相同的效果,那么建议使用Linearlayout。如果使用LinearLayout不能完成某些效果,那么使用RelativeLayout,而不是两层或更多层的LinearLayout,这样就可以尽量少的View层级,提高布局性能。

(2)尽量不使用LinearLayout的权重属性,因为它会让LinearLayout多进行一次measure。

(3)RelativeLayout的子View如果高度尽量和RelativeLayout相同,因为如果不同,会导致RelativeLayout在onMeasure()方法中做横向测量时,纵向的测量结果尚未完成,只好暂时使用自己的高度传入子View系统。而父View给子View传入的值没有变化的时候,是不会做无谓的测量的,所以RelativeLayout的子View如果高度和RelativeLayout相同可以进行一些布局上的优化,如果实现不行可以使用padding代替margin。

2.   <include/>

<include>标签把一个布局中加载到另外一个布局,比如titlebar这种布局,很适合复用,这样可以使代码结构清晰,又可统一修改使用。

如我们在主xml文件里调用<include>来使用这个公共布局:

【拓展】include标签仅支持layout_开头的属性(和id),且android:layout_width和android:layout_height属性必须存在,才能使用其它属性(如:android:layout_grivity、android:layout_align...),以避免include引用中的子组件属性影响到include的布局效果。

比如下面这个例子给我们include进的组件设置高度和位置:

3.  <merge/>

<merge/>标签通常和<include/>标签一起使用,避免根标签的重复嵌套,降低布局的层级。

(1)比如说我们的RelativeLayout中只有一个TextView,这个TextView不需要指定任何针对父视图的布局属性,只添加到父视图上并显示,这种情况如果把<RelativeLayout/>标签改为<merge/>标签,那么层级结构里就少了RelativeLayout这层布局。

(2)还有就是比如LinearLayout里面使用include嵌入一个布局,而这个嵌入的布局的根节点也是LinearLayout,这样就多了一层没有用的嵌套,这个时候如果我们使用<merge/>作为嵌入布局的根标签就可以避免重复嵌套的问题。

例子演示如下:

运行后使用“DDMS -> DumpView Hierarchy for UI Automator”工具,截图如下:

 

最下面两层RelativeLayoutTextView就是布局中的内容,上面的FrameLayout顶层视图。

下面如果我们将上述布局代码中的RelativeLayout修改为merge标签再查看层级结构如下:

 

4.   ViewStub

一个最最最可能的使用场景就是请求网络,如果网络异常就需要有一个View来提示用户显然这个View不经常被使用,如果我们通过代码逻辑动态更改这个View的可见性(GONE或者VISIBLE),在Inflate布局的时候View仍然会被Inflate,也就是说仍然会创建对象,会被实例化且耗费内存资源

ViewStub解决了这个布局性能问题,它是一个轻量级的View,宽高都是0,不占布局位置、占用资源非常小。只有ViewStub被设置为可见或调用了ViewStub.inflate()的时候,ViewStub所指向的布局才会被Inflate和实例化,从而达到了按需要才加载的目的,优化了布局性能。

【拓展】(ViewStub的布局属性会传给它所指向的布局,ViewStub对象会被置空,此时查看布局结构ViewStub是不存在的,取而代之的是被inflateLayout

综上ViewStub的原理,就可以使用它来方便的在运行时,决定要不要显示某个布局。使用实例如下:

……

android:layout="@layout/fail_view"指向页面加载失败的布局文件,里面包含一个idtvTextView

当出现网络异常时,我们在代码里这样使用ViewStub

private View hintFailView;if (网络异常) {    if (hintFailView == null) {        ViewStub viewStub = (ViewStub)this.findViewById(R.id.hint_fail_view);        hintFailView = viewStub.inflate(); //注意这里        TextView textView = (TextView) hintFailView.findViewById(R.id.tv);        textView.setText("网络异常");    }    hintFailView.setVisibility(View.VISIBLE);}else{    //网络正常    if (hintFailView!= null) {        hintFailView.setVisibility(View.GONE);    }    //业务逻辑}

5.   避免OverDraw

如果父控件和子控件都设置了Background,那么就形成了OverDraw,我们可以通过设置-开发者选项-显示GPU过度绘制来查看应用是否存在严重的OverDraw问题。

如果你发现应用中有些色块为红色,那么你可要去优化它了,你需要去根据颜色提示去找到你过度绘制的地方。

(优化点1)如果我们有自己的背景色,顶层View的背景色我们可以置空来优化。

setContentView(R.layout.activity_overdraw_01);getWindow().setBackgroundDrawable(null);

(优化点2)在自定义组件的onDraw()中考虑使用画布的clipRect()方法来绘制需要被绘制的区域

在看clipRect方法之前先看看如果将res目录下的图片文件转换为bitmap对象,这里总结了两种方法,大家可以参考使用:

InputStream is = this.getContext().getResources().openRawResource(R.drawable.icon);  Bitmap mBitmap = BitmapFactory.decodeStream(is);  //或者使用BitmapDrawableBitmap mBitmap = new BitmapDrawable(is).getBitmap();

clipRect方法可以截取画布中的一个矩形区域,在此区域外的将不再绘制显示。实例如下:

/**author SEU_Calvin in 2016/10*/@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);int width = mBmp.getWidth();int height = mBmp.getHeight();canvas.save();mPaint.setColor(Color.CYAN);//先在屏幕的0,0处绘制一个与我们Bitmap宽高相等的蓝色矩形canvas.drawRect(0, 0, width, height, mPaint);canvas.restore(); canvas.save();//裁剪画布,左上角为0,0  右下角为指定宽高的2倍和1.5倍canvas.clipRect(0, 0, width*2, height*3/2);//以width,height为左上角绘制我们的Bitmap,由于图片的下半部分在裁剪画布之外所以不显示canvas.drawBitmap(mBmp, width, height, mPaint);canvas.restore();}

结果如下所示,工作过程已经在代码的注释里写的很清楚了。

6.  其他小技巧

避免在自定义控件的onDraw()方法中创建大量的临时对象,比如String,因为该方法可能会被频繁调用,所以可能会频繁的触发GC。

为了控制篇幅,将一些看了让人感到惊艳的布局优化小技巧总结分享到了,希望可以帮助到你~

最后希望各位看官老爷们多点赞支持~

你可能感兴趣的文章
[LeetCode By Python]125. Valid Palindrome
查看>>
[LeetCode By Python]136. Single Number
查看>>
Android/Linux 内存监视
查看>>
用find命令查找最近修改过的文件
查看>>
Android2.1消息应用(Messaging)源码学习笔记
查看>>
android raw读取超过1M文件的方法
查看>>
ubuntu下SVN服务器安装配置
查看>>
MPMoviePlayerViewController和MPMoviePlayerController的使用
查看>>
CocoaPods实践之制作篇
查看>>
[Mac]Mac 操作系统 常见技巧
查看>>
苹果Swift编程语言入门教程【中文版】
查看>>
捕鱼忍者(ninja fishing)之游戏指南+游戏攻略+游戏体验
查看>>
iphone开发基础之objective-c学习
查看>>
iphone开发之SDK研究(待续)
查看>>
计算机网络复习要点
查看>>
Variable property attributes or Modifiers in iOS
查看>>
NSNotificationCenter 用法总结
查看>>
C primer plus 基础总结(一)
查看>>
剑指offer算法题分析与整理(三)
查看>>
Ubuntu 13.10使用fcitx输入法
查看>>