美文网首页
63.LinearLayout和RelativeLayout性能

63.LinearLayout和RelativeLayout性能

作者: SlideException | 来源:发表于2020-08-06 10:28 被阅读0次

    1.LinearLayout和RelativeLayout两者绘制同样的界面时layout和draw的过程时间消耗相差无几,关键在于onMeasure方法,RelativeLayout比LinearLayout慢一些,因为RelativeLayout会让它的子view调用两次measure过程,LinearLayout在不设置weight属性时只需要调用一次,但是如果设置了weight,那么LinearLayout同样也需要两次measure

    2.RelativeLayout的子View如果高度和RelativeLayout不同,会引发效率问题,可以使用padding代替margin以优化此问题

    1. 在不响应层级深度的情况下,使用Linearlayout而不是RelativeLayout。

    [https://blog.csdn.net/qq_28260521/article/details/78746396]

    为开发者默认新建RelativeLayout是希望开发者能采用尽量少的View层级,很多效果是需要多层LinearLayout的嵌套,这必然不如一层的RelativeLayout性能更好。因此我们应该尽量减少布局嵌套,减少层级结构,使用比如viewStub,include等技巧。可以进行较大的布局优化。

    OverDraw
    Overdraw就是过度绘制,是指在一帧的时间内(16.67ms)像素被绘制了多次,理论上一个像素每次只绘制一次是最优的,但是由于重叠的布局导致一些像素会被多次绘制,而每次绘制都会对应到CPU的一组绘图命令和GPU的一些操作,当这个操作耗时超过16.67ms时,就会出现掉帧现象,也就是我们所说的卡顿,所以对重叠不可见元素的重复绘制会产生额外的开销,需要尽量减少Overdraw的发生。
    -开发者选项-调试GPU过度绘制
    没有颜色: 意味着没有overdraw。像素只画了一次。
      蓝色: 意味着overdraw 1倍。像素绘制了两次。大片的蓝色还是可以接受的(若整个窗口是蓝色的,可以摆脱一层)。
      绿色: 意味着overdraw 2倍。像素绘制了三次。中等大小的绿色区域是可以接受的但你应该尝试优化、减少它们。
      浅红: 意味着overdraw 3倍。像素绘制了四次,小范围可以接受。
      暗红: 意味着overdraw 4倍。像素绘制了五次或者更多。这是错误的,要修复它们。

    那么我们怎么来消灭overdraw呢?总的原则就是:尽量避免重叠不可见元素的绘制,基于这个原则,大致有以下几种方法:
    1.RelativeLayout优先于LinearLayout

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

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

    4.ClipRect & QuickReject
    自定义的View,Android系统无法检测在onDraw里面具体会执行什么操作,无法监控并自动优化,
    可以通过canvas.clipRect()来帮助系统识别那些可见的区域。这个方法可以指定一块矩形区域,只有在这个区域内才会被绘制,其他的区域会被忽视。节约CPU与GPU资源,在clipRect区域之外的绘制指令都不会被执行,那些部分内容在矩形区域内的组件,仍然会得到绘制。还可以使用canvas.quickreject()来判断是否没和某个矩形相交,从而跳过那些非矩形区域内的绘制操作。

    5.使用ViewStub
    ViewStub是个什么东西?一句话总结:高效占位符。
    在开发应用程序的时候,经常会遇到这样的情况,会在运行时动态根据条件来决定显示哪个View或某个布局。那么最通常的想法就是把可能用到的View都写在上面,先把它们的可见性都设为View.GONE,然后在代码中动态的更改它的可见性。这样的做法的优点是逻辑简单而且控制起来比较灵活。但是它的缺点就是,耗费资源。虽然把View的初始可见View.GONE但是在Inflate布局的时候View仍然会被Inflate,也就是说仍然会创建对象,会被实例化,会被设置属性。也就是说,会耗费内存等资源。推荐使用ViewStub,ViewStub是一个轻量级的View,它一个看不见的,不占布局位置,占用资源非常小的控件。可以为ViewStub指定一个布局,在Inflate布局的时候,只有ViewStub会被初始化,然后当ViewStub被设置为可见的时候,或是调用了ViewStub.inflate()的时候,ViewStub所向的布局就会被Inflate和实例化,然后ViewStub的布局属性都会传给它所指向的布局。这样,就可以使用ViewStub来方便的在运行时,要还是不要显示某个布局。
    ViewStub的一些特点:

    1. ViewStub只能Inflate一次,之后ViewStub对象会被置为空。按句话说,某个被ViewStub指定的布局被Inflate后,就不会够再通过ViewStub来控制它了。
    2. ViewStub只能用来Inflate一个布局文件,而不是某个具体的View,当然也可以把View写在某个布局文件中。
      <ViewStub
      android:id="@+id/viewstub_demo_text"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout="@layout/viewstub_demo_text_layout"/> //@layout为要infate的布局

    (ViewStub) findViewById(R.id.stub_view)).setVisibility(View.VISIBLE);
    View importPanel = ((ViewStub) findViewById(R.id.stub_view)).inflate();
    ViewStub不支持merge标签,意味着你不能引入包含merge标签的布局到ViewStub中。
    否则会报错:android.view.InflateException: Binary XML file line #1: <merge /> can be used only with a valid ViewGroup root and attachToRoot=true

    6.使用Merge
    Merge标签有什么用呢?简单粗暴点回答:干掉一个view层级。
    Merge的作用很明显,但是也有一些使用条件的限制。有两种情况下我们可以使用Merge标签来做容器控件。第一种子视图不需要指定任何针对父视图的布局属性,就是说父容器仅仅是个容器,子视图只需要直接添加到父视图上用于显示就行。另外一种是假如需要在LinearLayout里面嵌入一个布局(或者视图),而恰恰这个布局(或者视图)的根节点也是LinearLayout,这样就多了一层没有用的嵌套,无疑这样只会拖慢程序速度。而这个时候如果我们使用merge根标签就可以避免那样的问题。另外Merge只能作为XML布局的根标签使用,当Inflate以merge 开头的布局文件时,必须指定一个父ViewGroup,并且必须设定attachToRoot为true。
    不使用merge
    layout1.xml
    <FrameLayout>
    <include layout="@layout/layout2"/>
    </FrameLayout>

    layout2.xml:
    <FrameLayout>
    <TextView />
    </FrameLayout>

    实际效果:
    <FrameLayout>
    <FrameLayout>
    <TextView />
    </FrameLayout>
    </FrameLayout>

    使用merge
    layout1.xml
    <FrameLayout>
    <include layout="@layout/layout2"/>
    </FrameLayout>
    layout2.xml:
    <merge>
    <TextView />
    </merge>
    实际效果:
    <FrameLayout>
    <TextView />
    </FrameLayout>

    merge必须放在布局文件的根节点上。
    merge并不是一个ViewGroup,也不是一个View,它相当于声明了一些视图,等待被添加
    因为merge标签并不是View,所以在通过LayoutInflate.inflate方法渲染的时候, 第二个参数必须指定一个父容器,且第三个参数必须为true,也就是必须为merge下的视图指定一个父亲节点。
    因为merge不是View,所以对merge标签设置的所有属性都是无效的。

    7.善用draw9patch

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

    9.使用include标签
    include并不能减少overdraw,但是他能给打UI布局很大的优化,增加复用性,可以灵活的修改布局,所以这儿就把它也加了进来

    相关文章

      网友评论

          本文标题:63.LinearLayout和RelativeLayout性能

          本文链接:https://www.haomeiwen.com/subject/qagrrktx.html