性能优化之布局优化篇一 使用ViewStub视图

Android布局性能优化三剑客:ConstraintLayout、ViewStub与过度绘制检测全解析 本文介绍了Android布局性能优化的关键方法,重点讲解ConstraintLayout的解决方案和按需加载技术。首先分析传统多层嵌套布局的性能问题,展示ConstraintLayout如何通过扁平化结构、约束系统和链式布局等特性提升性能。然后详细讲解<merge>标签消除冗余层级的原理,以及ViewStub实现真正延迟加载的适用场景和使用方法。这些技术能有效减少测量绘制开销,优化应用启动速度和内存使用,是提升Android应用性能的重要实践。 阅读详情
一、概述
ViewStub是一个不显示且不占用布局空间的视图。ViewStub需要指定一个布局layout,在ViewStub初始化时,其指定的layout并未初始化。只有当ViewStub的setVisibility(VISIBLE/INVISIBLE)或inflate()方法被调用,ViewStub所指向的布局layout才会实例化,该layout会使用ViewStub的布局参数LayoutParam,被添加到ViewStub的父视图中来替代ViewStub,即ViewStub将不在布局中存在。

ViewStub类官方文档:
https://developer.android.com/reference/android/view/ViewStub.html

二、使用场景
在开发中,会遇到这样的情况,需要在运行时动态的决定显示某个视图。通常的做法是,将可能会用到的所有视图都添加到布局中,之后在代码中通过View.VISIBILITY和View.GONE来改变视图的可见性。即使在xml布局中为视图设置了android:visibility="gone",该视图仍然会被实例化,这样做带来的缺点是增加了视图加载的时间和内存的消耗。

ViewStub的一个典型的使用案例,当我们启动一个页面,如果请求到数据就显示内容视图,遇到网络出错时显示网络异常视图。

添加网络异常视图net_error_view.xml。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:gravity="center"
              android:orientation="vertical">

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/net_icon"/>

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="网络异常\n点击屏幕重新加载"
        android:textColor="#666666"
        android:textSize="16sp"/>
</LinearLayout>

Activity的布局分两部分,一个内容视图,一个网络异常视图。网络异常视图使用ViewStub,并将layout指定为上面的net_error_view。
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:background="#ffffff">

    <!-- 内容视图 -->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
    </LinearLayout>

    <!-- 网络异常视图 -->
    <ViewStub
        android:id="@+id/viewstub"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:inflatedId="@+id/inflatedview"
        android:layout="@layout/net_error_view"/>
</FrameLayout>

在Activity中,如果需要显示网络异常视图,使用如下代码。
private View netErrorView;

ViewStub viewStub = (ViewStub) findViewById(R.id.net_error);
netErrorView = viewStub.inflate();
或
viewStub.setVisibility(View.VISIBLE);
netErrorView = findViewById(R.id.inflatedview);


当ViewStub的inflate()方法被调用时,其指定的@layout/net_error_view被实例化并显示出来。此时,ViewStub的android:inflatedId即inflatedview,就成为net_error_view视图根布局的id。这里我们将网络异常视图对象保存到成员变量netErrorView中,之后可以直接操作netErrorView,不用再调用findViewById()。


三、源码分析

ViewStub继承自View类,所以本质上还是一个View。
public final class ViewStub extends View {
}

ViewStub视图在加载时,为何没有显示,且指定的layout没有被初始化,原因在于ViewStub重写了View类的onMeasure()和draw()方法。在onMeasure()方法中,调用setMeasuredDimension,传递的参数measuredWidth和measuredHeight都是0。draw()方法的内部实现被置空,也就不会绘制了。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(0, 0);
}

@Override
public void draw(Canvas canvas) {
}

ViewStub除了拥有父类View的属性外,新增了android:inflatedId和android:layout,这两个属性在构造方法中完成初始化。
private int mInflatedId;
private int mLayoutResource;

public ViewStub(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    super(context);

    final TypedArray a = context.obtainStyledAttributes(attrs,
            R.styleable.ViewStub, defStyleAttr, defStyleRes);
    mInflatedId = a.getResourceId(R.styleable.ViewStub_inflatedId, NO_ID);
    mLayoutResource = a.getResourceId(R.styleable.ViewStub_layout, 0);
    mID = a.getResourceId(R.styleable.ViewStub_id, NO_ID);
    a.recycle();

    setVisibility(GONE);
    setWillNotDraw(true);
}
获取到属性inflatedId和layout的值后分别赋值给成员变量mInflatedId和mLayoutResource。并在最后调用了setWillNotDraw(true),只有View就不会绘制自己。

当ViewStub的inflate()方法被调用时,指定的layout被实例化显示出来,并返回。我们来看看inflate()方法的实现。
public View inflate() {
    final ViewParent viewParent = getParent();

    if (viewParent != null && viewParent instanceof ViewGroup) {
        if (mLayoutResource != 0) {
	    // 获取ViewStub的父视图,强转为ViewGroup类型
            final ViewGroup parent = (ViewGroup) viewParent;
	    // 获取LayoutInflater对象
            final LayoutInflater factory;
            if (mInflater != null) {
                factory = mInflater;
            } else {
                factory = LayoutInflater.from(mContext);
            }
	    // 实例化mLayoutResource,赋值给view变量
            final View view = factory.inflate(mLayoutResource, parent,
                    false);

            // 如果设置了mInflatedId,赋值给view的id
            if (mInflatedId != NO_ID) {
                view.setId(mInflatedId);
            }

            // 移除ViewStub自身
            final int index = parent.indexOfChild(this);
            parent.removeViewInLayout(this);

            // getLayoutParams()获取的是ViewStub的布局参数
            final ViewGroup.LayoutParams layoutParams = getLayoutParams();
	    // 把view添加到ViewStub的父视图中
            if (layoutParams != null) {
                parent.addView(view, index, layoutParams);
            } else {
                parent.addView(view, index);
            }

            // 把view赋值给成员变量mInflatedViewRef
            mInflatedViewRef = new WeakReference<View>(view);

            if (mInflateListener != null) {
                mInflateListener.onInflate(this, view);
            }

            // 最后返回view
            return view;
        } else {
            throw new IllegalArgumentException("ViewStub must have a valid layoutResource");
        }
    } else {
        throw new IllegalStateException("ViewStub must have a non-null ViewGroup viewParent");
    }
}
首先,使用LayoutInflater将指定的layout实例化赋值给变量view。如果设置了inflatedId,赋值给该view的id属性。然后,从ViewStub的父视图parent中移除ViewStub自身,使用ViewStub的布局参数把view添加到parent中。最后,把view赋值给成员变量mInflatedViewRef,返回view。

ViewStub的setVisibility()方法被调用时,指定的layout同样也可以被实例化。ViewStub对View中的setVisibility()方法进行了重写。
@Override
public void setVisibility(int visibility) {
    if (mInflatedViewRef != null) {// 如果ViewStub的inflate()已经被调用,执行if
        View view = mInflatedViewRef.get();
	// 此时ViewStub已经被inflate(),view已经有值,直接执行view的setVisibility()方法
        if (view != null) {
            view.setVisibility(visibility);
        } else {
            throw new IllegalStateException("setVisibility called on un-referenced view");
        }
    } else {// 如果ViewStub的inflate()还未被调用,执行else
        // 先调用父类View的setVisibility()
        super.setVisibility(visibility);
	// 如果传入的参数为VISIBLE或INVISIBLE,执行inflate()方法
        if (visibility == VISIBLE || visibility == INVISIBLE) {
            inflate();
        }
    }
}
在setVisibility()方法被调用之前,如果ViewStub的inflate()已经调用,此时成员变量mInflatedViewRef即是ViewStub指定的layout对象,直接执行view的setVisibility()方法。如果ViewStub的inflate()未被调用,先将参数传递到父类View的setVisibility(),然后判断传入的参数为VISIBLE或INVISIBLE时,执行inflate()方法,去实例化layout对象。

所以,当ViewStub的inflate()方法被调用,或setVisibility()方法被调用并传入参数VISIBLE或INVISIBLE,ViewStub所指向的布局layout会被实例化,并添加到ViewStub的父视图中显示出来。
Android APP性能优化之 ---- 布局优化(一) 布局优化的核心思想是优化布局嵌套层级(层级越少,View绘制时越快) 一、Android系统屏幕UI刷新机制 首先需要明白一个概念,如果我们想要屏幕流畅的运行,就必须保证UI全部的测量、布局和绘制的时间在16ms内为什么是16ms? 因为人眼与大脑之间的协作无法感知超过60fps的画面更新,而16ms也就是每秒刷新60fps 16ms=1000/60Hz,也就是说超过16ms用户就会感知到卡... 阅读详情

相关推荐

ViewStub利用ViewStub显示和隐藏布局

利用ViewStub显示和隐藏布局 运用View.VISIBLE和View.GONE去改变布局的可见性.

android 布局优化(三),ViewStub标签

想必我们一定遇到过一个布局有很多的元素,全部显示出来又太混乱,我们能不能根据自己的需求来显示自己想要显示呢?肯定可以。要实现这一个功能,在我没有接触ViewStub标签的话,我会把不常用的元素使用INVISIBLE或者GONE进行隐藏,一些常用的使用VISIBLE显示出来。这肯定是可以的,先不说性能的问题,这样设计的话有时连自己都会弄晕。有没有什么简单的方法呢?android给我们提供一种非常轻量级

稀饭汉堡1995的博客 514

别再手动写JSON了!用Node-RED OPC UA节点5分钟搞定楼宇温湿度数据采集

本文介绍了如何利用Node-RED和OPC UA节点快速实现楼宇温湿度数据的自动化采集与JSON格式转换。通过可视化工具Node-RED,开发者无需编写复杂代码,5分钟内即可完成从传感器到数据输出的全流程配置,大幅提升智慧楼宇和工业物联网项目的实施效率。

weixin_30368405的博客 431

ViewStub原理,解决约束布局( constraintLayout)ViewStub约束失效问题,解决ViewStub约束失效约束不起作用的问题

ViewStub原理,解决约束布局( constraintLayout)ViewStub约束失效问题,解决ViewStub约束失效约束不起作用的问题

blankmargin的博客 1899

别再为淘宝滑块发愁了!一个Python脚本自动获取x5sec cookie的保姆级教程

本文提供了一套完整的Python自动化解决方案,帮助开发者轻松应对淘宝滑块验证码问题。通过模拟浏览器行为、精准识别滑块缺口位置和生成拟人化移动轨迹,脚本能够自动获取关键的x5sec cookie,显著提升爬虫效率。教程涵盖环境配置、验证机制分析、代码实现及反检测策略,是解决淘宝滑块验证的实用指南。

weixin_30300225的博客 574

AT32F403A ADC通道管理与转换模式实战解析

本文深入解析AT32F403A微控制器的ADC模块,重点阐述其通道管理机制与四种转换模式(序列模式、抢占自动转换模式、反复模式、分割模式)的实战应用。通过详细的配置步骤与代码示例,帮助开发者高效实现模拟数字转换,优化数据采集性能与系统响应。

sky77的博客 279

代码动态写 viewstub

在Android中,ViewStub是一个不可见的,大小为0的View,在布局优化中起着重要的作用。ViewStub可以在布局文件中占位,当需要显示ViewStub中的内容时,通过调用。在实际的应用场景中,你可能需要根据条件动态决定是否要替换ViewStub中的内容,或者要替换成什么样的布局。方法,该ViewStub占位符会被替换为指定的布局资源。如果你想要动态地写ViewStub,你可以在代码中使用。是你在XML布局文件中定义的ViewStub的ID,是你想要ViewStub替换成的布局资源的ID。

chuyouyinghe的专栏 350

浙大数值分析lab2

最可怕的一道题,题目是这样的 Root of a Polynomial A polynomial of degree nnn has the common form as p(x)=cnxn+cn−1xn−1+⋯+c1x+c0p(x)=c_n x^n + c_{n-1} x^{n-1} + \cdots + c_1 x + c_0p(x)=c​n​​x​n​​+c​n−1​​x​

yao_zou的博客 4793

为什么ViewStub是默认不显示的?是懒加载?

关于为什么默认不显示,看源码:直接设置了gone。

呆咖啡的博客 345

ESP-01S使用micropython,利用Thonny烧写固件

在家舒服的太久了,想学习点新的东西,这次看上了ESP8266系列的联网设备,想自己做点小玩意出来耍耍,下面跟我一起来学习吧。本篇主要记录固件的烧写及测试。Thonny下载链接不算难,但是网上资源较少。也是提醒自己不要忘记这个步骤,记录一下,也分享给和我一样开始接触的小伙伴,加油加油,万事开头难。

weixin_43422072的博客 1692

【2025年五一数学建模竞赛】A题 完整论文 模型建立与求解

2025年五一数学建模竞赛 A题:基于历史数据与模式识别的道路车流量推测模型研究

Magnolia_He的博客 3783

Android UI优化之ViewStubs使用方法

1.含义解释: ViewStub默认是不显示的,也不占用内存,加载时会被其他的布局所替代才占用内存并且才显示出来,加载后如果只是隐藏用ViewStub.setVisiblitiy(View.GONE)就可以正常隐藏,但是不能释放内存。 ViewStub和include的区别是:ViewStub中的布局不会随着它所在布局的渲染而渲染,而标签中的布局会随着它所在布局的渲染而渲染,ViewS

翰墨飘香的专栏 2411

R | 提取文件夹内栅格数据的最小值、最大值、标准差和平均值

各位同学好,我们长需要用长时间序列栅格数据的平均值做变量的时间变化分析,用代码能够帮助我们省下很多时间。

qq_48539301的博客 448

Android 性能优化 三 布局优化ViewStub标签的使用

小黑与小白的故事,通过虚拟这两个人物进行一问一答的形式来共同学习ViewStub的使用 小白:Hi,小黑,ViewStub是什么?听说可以用来进行布局优化。 小黑:ViewStub 是一个隐藏的,不占用内存空间的视图对象,它可以在运行时延迟加载布局资源文件。(更多详细的API等信息可以查看官方文档ViewStub),计算机行业一向是实践里面出真知,下面用一个例子演示下效果。

Android 9113

第 1 章 Prometheus 监控 | Prometheus 入门

了解 Prometheus 监控系统,学会安装及了解页面功能。

甘蓝的专栏 485

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

Android UI性能优化——ViewStub和Merge的使用

ViewStub虽然实现简单,但是源码设计巧妙。对于页面中的不常用布局使用ViewSutb懒加载有一定的优化效果。正确的使用merge可以有效的减少布局层级,提高页面渲染速度。但是merge使用限制比较多,应用场景比较少。

Hunter2916的博客 2019

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

布局性能优化利器:ViewStub 极简指南

ViewStub 是 Android 开发中优化布局性能的高频实用工具,它通过让布局按需加载,来解决一系列问题。本文将全面讲解 ViewStub 在使用中必知必会的那些点,还有注意事项。

李斯维的博客空间 876

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

Android布局性能优化—从源码角度看ViewStub延迟加载技术

在项目中,难免会遇到这种需求,在程序运行时需要动态根据条件来决定显示哪个View或某个布局,最通常的想法就是把需要动态显示的View都先写在布局中,然后把它们的可见性设为View.GONE,最后在代码中通过控制View.VISIABLE动态的更改它的可见性。这样的做法的优点是逻辑简单而且控制起来比较灵活。但是它的缺点就是,耗费资源,虽然把View的初始可见View.GONE但是在Inflate布局

【博客地址迁移到】:https://zhengxiaoyong.com 5332

Android布局优化include 、merge、ViewStub的应用

前面已经分别介绍了、、的使用,详细过程可以见前面。 :http://blog.youkuaiyun.com/u012721519/article/details/51229107 :http://blog.youkuaiyun.com/u012721519/article/details/51229291 :http://blog.youkuaiyun.com/u012721519/article/details/512314

夏天的夏,夏天的天。 1753

Android性能优化系列之布局优化

我们注意看注释5处,这里就解释了为什么include标签和被引入的布局的根元素都设置了id的情况下,通过被引入的根元素的id来查找子控件会找不到的情况。我们看到,注释5处的会判断include标签的id如果不是View.NO_ID的话会把该id设置给被引入的布局根元素的id,即此时在我们的例子中被引入的id为my_foot_parent_id的根元素RelativeLayout的id被设置成了include标签中的id,即RelativeLayout的id被动态修改成了”my_foot_ly”。

Gaga246的博客 2174

android中 ViewStub、include、merge的使用

Android布局优化三剑客:ViewStub、include和merge的使用指南。ViewStub实现懒加载,适用于不立即显示的布局;include用于布局复用,提取公共UI组件;merge减少层级嵌套,优化布局结构。三者各司其职:ViewStub注重性能优化,include侧重代码复用,merge专注布局扁平化。正确使用可显著提升应用性能,需根据实际场景选择合适的标签,并注意各自的使用限制和最佳实践。

梦想不只是梦与想的博客 722

布局优化 include viewstub merge 及源码解析

我只是一个无情的搬运工 布局是我们再开发应用时必不可少的工作,通常情况下,布局并不会成为工作中的难点。但是,当你的应用变得越来越富咱,页面越来越多时,布局上的优化工作就成了性能优化的第一步。因为布局上的优化并不像其他优化方式那么复杂,通过Android Sdk提供的HierarchyView可以很直接地看到冗余的层级,去除这些多次与的层级将使我们的UI变得更流畅。本小结我们就来学习一些常用的布局优化方式。 1.1 include布局 include标签实现的原理很简单,就是再解析xml布局时,如果检测到i.

AdrianAndroid的专栏 494

“Android“性能优化之布局优化-<include>,<merge>,<ViewStub>

android 性能优化

农展飞的博客 1862

深入了解 Android 中的 ViewStub

在 Android 开发中,性能优化一直是一个重要的话题。`ViewStub` 作为一种轻量级视图容器,可以帮助我们在合适的时机延迟加载视图,从而优化应用性能。

jiet_h的博客 1958

安卓性能优化之界面UI优化——ViewStub

id属性是viewstub的id,可以通过findViewById找到该资源控件,inflatedId是root view id,即viewstub调用inflate之后,不能再次调用,会报错,因为viewstub已经从视图层级中被移除,但是可以通过inflatedId取得viewstub的布局,继续来控制隐藏viewstub,也就是inflatedId的布局和viewstub的布局保持一致。view_stub.xml如下。3、ViewStub针对的事layout布局,不是某一个具体的view;

m0_62089210的博客 1833

Android中ViewStub使用

Android中ViewStub的使用指南 在Android开发中,ViewStub 是一种轻量级的视图,它能够在需要的时候延迟加载其他视图,主要用于提高性能和加速初始加载时间。特别是在复杂的布局中,ViewStub 可以帮助减少内存占用。本文将详细介绍如何使用 ViewStub,并提供相关代码示例。 1. 整体流程 在使...

weixin_28686771的博客 1046
上一篇: selector的使用及执行流程
下一篇: 性能优化之布局优化篇二 使用<merge>标签
ruancoder
博客等级 码龄10年 42粉丝 66原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值