当前位置: 首页 > news >正文

【Android】布局优化:include、merge、ViewStub以及Inflate()源码浅析

include

引入:

include 是 Android 布局复用的利器,它让你能够将常用的布局组件封装成独立的 XML 文件,然后在多个地方重复使用;比如应用中的统一标题栏、通用底部按钮等,通过 include 只需定义一次,就能在任何需要的界面中嵌入,极大提高了代码的复用性和维护性;

  1. include是为了实现布局的复用;
  2. 使用:

include_title1文件中写我们需要复用的文件;

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"xmlns:app="http://schemas.android.com/apk/res-auto"><Buttonandroid:layout_marginTop="5dp"android:backgroundTint="#000000"app:layout_constraintTop_toTopOf="parent"android:id = "@+id/bt_titles"app:layout_constraintBottom_toBottomOf="parent"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="dianji"></Button></androidx.constraintlayout.widget.ConstraintLayout>

在我们正式的布局中,通过Include写入我们的布局;

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:id="@+id/main"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><includeandroid:id = "@+id/bt_title"android:layout_width="100dp"android:layout_height="100dp"layout="@layout/include_title"></include><includeandroid:id = "@+id/bt_titles"layout="@layout/include_title1"></include>
</androidx.constraintlayout.widget.ConstraintLayout>
  1. 如果需要找到子项,我们需要找到对应的include的布局,然后用布局.findviewbyid查找对应的Id;

  2. 注意事项:

    1. 建议为每个include设置不同的id,因为如果一个布局中重复两个相同的layout或者有重复的Id,此时只会找到第一个被引入的layout中的元素;
    2. 后面修改只能修改布局(除背景颜色等等),而且修改布局的前提也必须重新定义宽高
    3. 如果layout的根布局设置了id,include里也设置了Id,建议保持一致,否则可能返回Null;

merge

引入:

merge 是优化布局层级的精妙工具,它可以自动消除多余的ViewGroup层级。当使用 include 引入布局时,如果外层容器与父布局类型相同,merge 会直接将其中的子视图融合到父容器中,避免产生不必要的嵌套,从而简化视图结构、提升测量和绘制效率,让布局更加高效;

  1. 为了减少视图层级以优化布局,提升UI渲染的性能;

  2. 使用:
    其实很简单,就直接把根布局用Merge替换了就行;

    <?xml version="1.0" encoding="utf-8"?>
    <merge xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"xmlns:app="http://schemas.android.com/apk/res-auto"><Buttonapp:layout_constraintTop_toTopOf="parent"android:id = "@+id/bt_title"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="dsf"></Button>
    </merge>
    
  3. 其他:什么时候用Merge,就是当前layout的父容器和layout的根容器相同的类型,那么layout的根容器可以使用merge 来代替,减少布局嵌套,增加UI渲染效果;

  4. 注意事项:

    1. 因为Activity的默认contentview外层就是一个framelayout,所以当前布局如果是framelayout而且无背景,边距其他属性的设置,就可以用merge来代替;
    2. 等等。。。为什么还有边距?这是因为使用Merge没有根布局,所以设置边距或者宽高默认无效;
    3. merge的使用范围:必须作为根结点使用,否则就会报错;
    4. 在使用inflate时候,必须指定父容器,而且第三个参数设置为true立刻加入到父视图中;
    5. 因为merge无法独立生成视图对象,但是viewStub是动态生成视图,所以viewstub中禁止使用merge

ViewStub

  1. 使用情况: 页面初始化时,可能会隐藏一些不可见的veiw ,就算隐藏了,但是在初始化时还是会创建,为了减少开销,有一个轻量级的方案--viewstub

  2. 使用:

    写一个布局是自己想加载的布局然后在总布局布局中使用viewstub控件,但是必须设置宽高,并且指定layout,最后的宽高也就是这个viewstub内所定义的宽高,所以一定要保持一致,否则就有可能出现布局偏差;

    <ViewStubandroid:id = "@+id/ljx"android:layout="@layout/viewstub_01"android:layout_width="200dp"android:layout_height="100dp"app:layout_constraintTop_toTopOf="parent"app:layout_constraintBottom_toTopOf="@+id/bt_titles"app:layout_constraintLeft_toLeftOf="parent"></ViewStub>
    
  3. 注意事项:

  4. (布局嵌套问题)Viewstub是一个动态加载的布局,所以根布局不可以使用merge,所以它可能会存在一些布局嵌套;

  5. (一次inflate)初始化使用–viewstub懒加载机制决定在第一次inflate之后或者设置可见性,就不能再inflate了,因为inflate会把view从自身替换到布局文件中的目标视图中,并从布局树中移除;

    设置可见性是因为,第一次设置的时候会执行inflate的过程,后面设置的时候,就是对已经在目标视图中的view进行可见性的设置,不会inflate了;

    如果想调用多次呢,保存inflate的引用,设置可见性;

  6. (目标布局的占位符–宽高) viewstuB自身不参与绘制占用空间,但是它仍然是一个有效的占位符,所以布局文件中需要写宽高,也是最终显示出来的宽高;

源码分析:

主要解析都写在注释了,大家自行看

inflate()

public View inflate() {final ViewParent viewParent = getParent();//前置条件检查,当前viewstub必须添加到父容器,才能inflate;if (viewParent != null && viewParent instanceof ViewGroup) {//而且必须设置有效的布局资源;if (mLayoutResource != 0) {final ViewGroup parent = (ViewGroup) viewParent;// 创建替换到viewstub位置的view;final View view = inflateViewNoAdd(parent);// 开始替换replaceSelfWithView(view, parent);//保存弱引用并且触发回调mInflatedViewRef = new WeakReference<>(view);if (mInflateListener != null) {mInflateListener.onInflate(this, 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");}
}
  1. 这里保存的是弱引用,但是内存紧张的时候可能会被GC机制回收,所以我们应该自己保存强引用;

inflateViewNoAdd()

private View inflateViewNoAdd(ViewGroup parent) {final LayoutInflater factory;//获得layoutInfalter的实例if (mInflater != null) {//viewstub可以设置了自定义的布局加载器factory = mInflater;} else {//使用默认的布局加载器factory = LayoutInflater.from(mContext);}// 把我们的布局资源,方到未来父容器中(不是立即添加到父容器)final View view = factory.inflate(mLayoutResource, parent, false);// 这个就是替换后的view的Id;if (mInflatedId != NO_ID) {view.setId(mInflatedId);}return view;
}

这个方法的目的就是创建view对象,解析布局参数,设置视图的属性;

主要过程就是得到布局加载器,然后设置视图,最后设置Id;

replaceSelfWithView()

private void replaceSelfWithView(View view, ViewGroup parent) {//  获得当前viewstub在父容器中的位置final int index = parent.indexOfChild(this);//  移除这个viewstubparent.removeViewInLayout(this);//  获得这个viewstub的布局参数final ViewGroup.LayoutParams layoutParams = getLayoutParams();//  添加新的视图if (layoutParams != null) {parent.addView(view, index, layoutParams);} else {//使用默认的布局参数parent.addView(view, index);}
}
  1. 调用一次inflate之后viewstub就会从布局树中被移除,所以这里inflate只能调用一次;

过程就是:得到位置,删除viewstub, 最后添加新的视图;

setVisbility()

public void setVisibility(int visibility) {if (mInflatedViewRef != null) {//如果inflate过,通过弱引用从取出来这个view设置可见性;如果被gc机制回收,那么会爆出异常,所以简易我们使用强引用;View view = mInflatedViewRef.get();if (view != null) {view.setVisibility(visibility);} else {throw new IllegalStateException("setVisibility called on un-referenced view");}} else {//没有Inflate过的话,调用;super.setVisibility(visibility);if (visibility == VISIBLE || visibility == INVISIBLE) {inflate(); }}
}
  1. 设置为GONE时,不会触发Inflate,这是为了性能考虑;所以不能通过设置GONE来触发Inflate;

源码总结:inflate的过程其实是判断条件(如果viewstub添加到布局树中而且是有效的布局资源)->通过布局加载器加载这个布局得到view(但是不加入到父布局中,因为你需要加到原来的位置)->得到原来的位置,删除这个viewstub,并且把这个布局加载进去,最后保存弱引用触发回调;

知道了源码剩下的就是我们需要注意的点的总结:

  1. 在创建新view时,我们需要注意我们需要设置InflatedId,这样我们才能得到新布局;
  2. 需要自己保存强引用,否则会有异常;
  3. 因为会移除,所以只能调用一次;
  4. 设置可见性,第一次不能设置为gone,否则不会触发inflate
http://www.dtcms.com/a/614548.html

相关文章:

  • 部署Spring Boot项目到Linux服务器数据盘
  • 网站的建设模式是指什么时候个人公众号做电影网站
  • Spring aop 五种通知类型
  • 千博企业网站管理系统完整版 2014ios认证 东莞网站建设
  • 国外的一些网站精美网站设计欣赏
  • 深度学习:动量梯度下降实战(Momentum Gradient Descent)
  • 电脑做服务器建网站app试玩网站制作
  • 【Janet】数据结构
  • Tensor与NumPy转换
  • 06-文件操作-教程
  • 【ros2】ROS2 C++服务端与客户端开发指南
  • 网站开发成本主要有哪些网络广告发布
  • 【035】Dubbo3从0到1系列之dubbo-remoting核心接口Endpoint
  • 用备份的网站代码做网站步骤小程序模板怎么导入
  • 利用帝国cms网站建设网页源代码怎么搜索关键词
  • 【愚公系列】《腾讯元宝从入门到精通》002-提示词设计与优化
  • C++哈希(包含unordered_set和unordered_map的封装)
  • 编译类语言 | 深入了解编译原理及应用
  • 西安建设银行网站专业公司网站 南通
  • 禁止同ip网站查询抖音小程序注册
  • Chaos-nano:Arduino Pro Mini 轻量级操作系统解析与实战应用
  • 从 0 到 1 学爬虫:Python 基础语法在爬虫中的实战运用用这个标题写一篇文章,在当前对话窗口输出
  • 网页设计网站排行榜中国发达国家还有多远
  • Podman和Docker
  • 自己架设服务器做网站一同看网页打不开
  • 手写LRU 缓存
  • 23-MD5+DES+Webpack:考试宝
  • 前端构建工具环境变量,安全管理
  • 个人网站名称举例网站建设需要的硬件
  • 数据分析笔记09:Python条件语循环