程序笔记   发布时间:2022-07-05  发布网站:大佬教程  code.js-code.com
大佬教程收集整理的这篇文章主要介绍了❤󾃺ndrod 性能优化之布局优化❤️大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。

作者:帅次

作者简介:CSDN博客专家࿰c;欢迎点赞、收藏、评论

粉丝福利:公众号「帅次」一个分享Android 体系技术·相关知识·面试题库·技术互助·干货·资讯·高薪职位·教程的地方。

        Android的布局管理器本身就是个UI组件࿰c;所有的布局管理器都是ViewGroup的子类࿰c;而ViewGroup是View的子类࿰c;所以布局管理器可以当成普通的UI组件使用࿰c;也可以作为容器类使用࿰c;可以调用多个重载addView()向布局管理器中添加组件࿰c;并且布局管理器可以互相嵌套࿰c;当然不推荐过多的嵌套 (如果要兼容低端机型࿰c;最好不要超过5层)

❤󾃺ndrod 性能优化之布局优化❤️

5; 布局层级管理

        让咱们一起了解一下每当系统绘制一个布局时࿰c;会发生一些什么。这一过程由两个步骤完成:

@H_618_34@5; 绘制(Measurement)
  • 1:根布局测量自身。

  • 2:根布局要求它内部所有子组件测量自身。

  • 3:所有自布局都需要让它们内部的子组件完成这样的操作࿰c;直到遍历完视图层级中所有的View。

@H_618_34@5; 摆放(Positioning)
  • 1:当布局中所有的View都完成了测量࿰c;根布局则开始将它们摆放到合适的位置。

  • 2:所有子布局都需要做相同的事情࿰c;直到遍历完视图层级中所有的View。

@H_618_34@5; 背景设置产生的过度绘制
  • 组件背景:每个组件每设置一次背景, 该组件的区域就会增加一层绘制 , 如 LinearLayout 设置背景颜色 , 里面的 TextView 设置背景颜色 , 都会增加该组件区域内的过渡绘制 ;

  • 主题背景:Activity 界面的主题背景࿰c;会增加一次 GPU 绘制 ;

        不要随意给布局中的 UI 组件设置背景 。如 ImageView 设置一张图片࿰c;会增加一次绘制 ,再给该 ImageView 组件设置背景颜色, 那么又会增加一次绘制, 那么该 ImageView 组件肯定过渡绘制了。

@H_618_34@5; 小结

        当某个View的属性发生变化(如:TextView内容变化或ImageView图像发生变化)࿰c;View自身会调用View.invalidate()方法(必须从 UI 线程调用)࿰c;自底向上传播该请求c;直到根布局(根布局会计算出需要重绘的区域࿰c;进而对整个布局层级中需要重绘的部分进行重绘)。布局层级越复杂࿰c;UI加载的速度就越慢。因此࿰c;在编写布局的时候࿰c;尽可能地扁平化是非常重要的

        FrameLayout和TableLayout有各自的特殊用途࿰c;LinearLayout 和 RelativeLayout 是可以互换的࿰c;ConsTraintLayout和RelativeLayout类似。也就是说࿰c;在编写布局时࿰c;可以选择其中一种࿰c;咱们可以以不同的方式来编写下面这个简单的布局。

5; 小实验(多种方式实现同一布局)

❤󾃺ndrod 性能优化之布局优化❤️

@H_618_34@5; LinearLayout

        第一种方式是使用LinearLayout࿰c;然可读性比较强࿰c;但是性能比较差。由于嵌套LinearLayout会加深视图层级࿰c;每次摆放子组件时࿰c;相对需要消耗更多的计算。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://scheR_120_11845@as.android wangt.cc /apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <View
        android:id="@+id/view_top_1"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:BACkground="@color/color_666666"/>
    <View
        android:id="@+id/view_top_2"
        android:layout_width="200dp"
        android:layout_height="100dp"
        android:BACkground="@color/teal_200"/>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <View
            android:id="@+id/view_top_3"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:BACkground="@color/color_FF773D"/>
        <View
            android:id="@+id/view_top_4"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:BACkground="@color/purple_500"/>
    </LinearLayout>
</LinearLayout>

LinearLayout视图层级如下所示:

❤󾃺ndrod 性能优化之布局优化❤️

@H_618_34@5; 使用RelativeLayout

        第二种方法使用RelativeLayout࿰c;在这种情况下࿰c;你不需要嵌套其他ViewGroup,因为每个子View可以相当于其他View࿰c;或相对与父控件进行摆放。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://scheR_120_11845@as.android wangt.cc /apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <View
        android:id="@+id/view_top_1"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:BACkground="@color/color_666666"/>
    <View
        android:id="@+id/view_top_2"
        android:layout_width="200dp"
        android:layouT_Below="@id/view_top_1"
        android:layout_height="100dp"
        android:BACkground="@color/teal_200"/>
    <View
        android:id="@+id/view_top_3"
        android:layout_width="100dp"
        android:layouT_Below="@id/view_top_2"
        android:layout_height="100dp"
        android:BACkground="@color/color_FF773D"/>
    <View
        android:id="@+id/view_top_4"
        android:layout_width="100dp"
        android:layouT_Below="@id/view_top_2"
        android:layout_toRightOf="@id/view_top_3"
        android:layout_height="100dp"
        android:BACkground="@color/purple_500"/>
</RelativeLayout>

RelativeLayout视图层级如下所示:

❤󾃺ndrod 性能优化之布局优化❤️

        通过两种方式࿰c;可以很容易看出࿰c;第一种方式LinearLayout需要3个视图层级和6个View࿰c;第二种方式RelativeLayout仅需要2个视图层级和5个View。

        当然࿰c;然RelativeLayout效率更高࿰c;但不是所有情况都能通过相对布局的方式来完成控件摆放。所以通常情况下࿰c;这两种方式需要配合使用。

        注意:为了保证应用程序的性能࿰c;在创建布局时࿰c;需要尽量避免重绘c;布局层级应尽可能地扁平化c;这样当View被重绘时࿰c;可以减少系统花费的时间。在条件允许的情况下࿰c;尽量的使用RelativeLayout和ConsTraintLayout,而非LinearLayout࿰c;或者用GridLayoutl来替换LinearLayout

        咱们最常使用的是ViewGroup是LinearLayout,只是因为它很容易看懂࿰c;编写起来简单࿰c;所以它就成了Android开发的首选。出于这个原因࿰c;Google推出了一个全新的ViewGroup。在适当的时候时候使用它࿰c;可以减少冗余࿰c;它就是网格布局GridLayout下。

5; 布局复用(<include/>和 <merge/> )

        Android 提供了一个非常有用的标签。在某些情况下࿰c;当你希望在其他布局中用一些已存在的布局时࿰c; 标签可通过制定相关引用ID࿰c;将一个布局添加到另一个布局。

        比如自定义一个标题栏࿰c;那么可以按照下面的方式࿰c;创建一个可重复用的布局文件。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://scheR_120_11845@as.android wangt.cc /apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <View
        android:id="@+id/view_top_1"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:BACkground="@color/color_666666"/>
</RelativeLayout>

        接着࿰c;将标签放入相应的布局文件中࿰c;替换掉对应的 View:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://scheR_120_11845@as.android wangt.cc /apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <include layout="@layout/include_layout"/>
    ...
</RelativeLayout>

        这么一来࿰c;当你希望重用某些View时࿰c;就不用复制/粘贴的方式来实现c;只需要定义一个layout文件࿰c;然后通过 引用即可。

        但是这样做࿰c;可能会引入一个冗余的ViewGroup(重用的布局文件的根视图)。为此࿰c;Android 提供了另一个标签࿰c;用来帮我们减少布局冗余࿰c;让层级变得更加扁平化。我们只需要将可重用的根视图࿰c;替换为 标签即可。如下所示:

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://scheR_120_11845@as.android wangt.cc /apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <View
        android:id="@+id/view_top_1"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:BACkground="@color/color_666666"/>
</merge>

        如此一来࿰c;就没有了冗余的视图控件࿰c;因为系统会忽略标签࿰c;并将标签中的视图直接放置在相应的布局文件中࿰c;替换标签。

        注意:使用此标签时࿰c;需要记住它的两个主要限制:

        1、它只能作为布局文件的跟来使用。

        2、每次调用LayoutInflater.inflate()时࿰c;必须为布局文件提供一个View,作为它的父容器:LayoutInflater.from(this).inflate(R.layout.merge_layout,parent,truE);

5; Viewstub

        viewstub是一个不可见的零大小View࿰c;可以作为一个节点被加入布局文件࿰c;但他关联的布局࿰c;知道运行时通过调用 Viewstub.inflate() 或 View.setVisibility(View.VISIBLE) 方法࿰c;才会被绘制。

        先看效果图:

❤󾃺ndrod 性能优化之布局优化❤️

@H_618_34@5; Viewstub 设置
        <Viewstub android:id="@+id/viewstub"
            android:inflatedId="@+id/subTree"
            android:layout="@layout/activity_imageview"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
@H_618_34@5; 显示

        上方 Viewstub 所关联的布局 activity_imageview 并不会被实例化(不要调用布局内的控件࿰c;因为还没加载会报空指针异常)࿰c;只有程序在运行期间调用了以下方法:

findViewById(R.id.viewstub).setVisibility(View.VISIBLE);

((Viewstub)findViewById(R.id.viewstub)).inflate();

        在这期间不要调用关联布局内的控件࿰c;因为还没呗加载没有

        一旦 Viewstub 变成 visible 或者被 inflatec;便不再可用(Id:viewstub没了)࿰c;因为它在布局层级中的位置已经实例化出来的布局所替代࿰c;因为不能被访问࿰c;而应该使用 android:inflatedId 属性中的ID。如下:

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case r.id.btn_view:
                break;
            case r.id.btn_scheR_120_11845@e:
                //加载࿰c;选择一种即可。
                findViewById(R.id.v_stud).setVisibility(View.VISIBLE);
                //((Viewstub)findViewById(R.id.v_stud)).inflate();

                //加载后layout所用ID
                subTree  = findViewById(R.id.subTreE);
                findViewById(R.id.btn_iv_basis).setOnClickListener(new view.onClickListener() {
                    @Override
                    public void onClick(View v) {
                        Toast.makeText(MainActivity.this,"我是 viewstud 加载的控件",Toast.LENGTH_SHORT).show();
                    }
                });
                break;
            case r.id.btn_invisible:
                subTree.setVisibility(View.INVISIBLE);
                break;
            case r.id.btn_visible:
                subTree.setVisibility(View.VISIBLE);
                break;
            case r.id.btn_init:
                //Viewstub变为可见后再次调用会报空指针࿰c;因为id:viewstub 已经不存在了。
                view viewstub  = findViewById(R.id.viewstub);
                viewstub.setVisibility(View.GONE);
                break;
        }
    }
@H_618_34@5; 小结

        viewstub 非常有用࿰c;我们可以通过 Viewstub 来延迟部分 View 的加载࿰c;缩短首次加载时间࿰c;以及减少一些不必要的内存分配。

5; 自定义组件优化

在自定义View时需要注意c;避免犯以下的性能错误:

  • 在非必要时࿰c;对View进行重绘。

  • 绘制一些不被用户所看到的的像素࿰c;也就是过度绘制。(被覆盖的地方)

  • 在绘制期间做了一些非必要的操作࿰c;导致内存资源的消耗。

@H_618_34@5; 优化
  • View.invalite()是最最广泛的使用操作࿰c;因为在任何时候都是刷新和更新视图最快的方式。

        在自定义View时要小心避免调用非必要的方法࿰c;因为这样会导致重复强行绘制整个视图层级࿰c;消耗宝贵的帧绘制周期。检查清楚View.invalite()和View.requestLayout()方法调用时间位置࿰c;因为这会影响整个UI࿰c;导致GPU和它的帧速率变慢。

  • 避免过渡重绘。为了避免过渡重绘࿰c;我们可以利用Canvas方法,只绘制控件中所需要的部分。整个一般在重叠部分或控件时特别有用。相应的方法是Canvas.clipRect()(指定要被绘制的区域);

  • 在实现View.onDraw()方法中࿰c;不应该在方法内及调用的方法中进行任何的对象分配。在该方法中进行对象分配࿰c;对象会被创建和初始化。而当View.onDraw()方法执行完毕时。垃圾回收器会释放内存。如果View带动画࿰c;那么View在一秒内会被重绘60次。所以要避免在View.onDraw()方法中分配内存。

        永远不要在View.onDraw()方法中及调用的方法中进行内存分配࿰c;避免带来负担。垃圾回收器多次释放内存࿰c;会导致卡顿。最好的方式就是在View被首次创建出来时࿰c;实例化这些对象。

        布局优化到这里就结束了࿰c;还是那句话后面如果有更好的方案会及时的添加进去࿰c;如果有老大有其他方案也可以留言哈࿰c;感谢!最后别忘记关注我撒

大佬总结

以上是大佬教程为你收集整理的❤󾃺ndrod 性能优化之布局优化❤️全部内容,希望文章能够帮你解决❤󾃺ndrod 性能优化之布局优化❤️所遇到的程序开发问题。

如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。