前言

最近在面试的时候被面试官问到ViewStub内部是如何使用占位的时候,我是一脸懵逼,说实话我之前对UI上的一些控件内部的代码看的非常少,所以一时答不上来,这个东西其实并不说有多难,而是我们平时在开发的过程中只是简单的去调用了一下但是并没有深入的去了解其内部的原理。其实面试的过程就是我们一个查漏补缺的过程中,让我们的各个知识面都更加的能提高,同时做到知其然更知其所以然。

gone和visible

一个view被设置了visibility=gone,在显示的时候它会不会被绘制? - 知乎

1、invisible

view设置为invisible时,view在layout布局文件中会占用位置,但是view为不可见,该view还是会创建对象,会被初始化,会占用资源。

2、gone

view设置gone时,view在layout布局文件中不占用位置,但是该view还是会创建对象,会被初始化,会占用资源。GONE需要重新的布局和通知上级View去刷新,有缓存还要清空缓存

3、viewstub

viewstub是一个轻量级的view,它不可见,不用占用资源,只有设置viewstub为visible或者调用其inflater()方法时,其对应的布局文件才会被初始化。但是viewstub的引用对象需要是一个布局layout文件,如果要是单个的view的话,viewstub就不能满足要求,就需要调用view的gone或者invisible属性了。

https://www.zhihu.com/question/54416902/answer/476979353

GONE真的隐藏;

INVISIBLE不可见但是预留了View的位置;

/* Check if the GONE bit has changed */if ((changed & GONE) != 0) {needGlobalAttributesUpdate(false);requestLayout();//异同点1111111if (((mViewFlags & VISIBILITY_MASK) == GONE)) {if (hasFocus()) clearFocus();clearAccessibilityFocus();destroyDrawingCache();//异同点2222222if (mParent instanceof View) {// GONE views noop invalidation, so invalidate the parent((View) mParent).invalidate(true);}// Mark the view drawn to ensure that it gets invalidated properly the next// time it is visible and gets invalidatedmPrivateFlags |= PFLAG_DRAWN;}if (mAttachInfo != null) {mAttachInfo.mViewVisibilityChanged = true;}}/* Check if the VISIBLE bit has changed */if ((changed & INVISIBLE) != 0) {needGlobalAttributesUpdate(false);/** If this view is becoming invisible, set the DRAWN flag so that* the next invalidate() will not be skipped.*/mPrivateFlags |= PFLAG_DRAWN;if (((mViewFlags & VISIBILITY_MASK) == INVISIBLE)) {// root view becoming invisible shouldn't clear focus and accessibility focusif (getRootView() != this) {if (hasFocus()) clearFocus();clearAccessibilityFocus();}}if (mAttachInfo != null) {mAttachInfo.mViewVisibilityChanged = true;}}

如果在GONE和INVISIBLE两者都可以完成你的效果,那么你应该选择INVISIBLE。因为从源码中来看GONE需要重新的布局和通知上级View去刷新,有缓存还要清空缓存;从视图变更开销的来说INVISIBLE要更加的划算一些,如果你的View不是十分占用资源的情况!!!

android中View的GONE和INVISIBLE的原理 - soft.push("zzq") - 博客园

ViewStub介绍

一些布局控件在开始时并不需要显示,在程序启动后再根据业务逻辑进行显示,通常的做法是在 xml中将其定义为不可见,然后在代码中通过setVisibility()更新其可见性,但是这样做会对程序性能产生不利影响,因为虽然该控件的初始状态是不可见,但仍然会在程序启动时进行创建和绘制,增加了程序的启动时间。正是由于这种情况的存在,Android系统提供了ViewStub框架,能够很容易实现“懒加载”以提升程序性能,本文从“使用方法”和“实现原理”两个方面对其进行讲解,希望能对大家有所帮助。

平时我们在开发Android的界面的时候,如果遇到不需要显示的控件或者是布局的时候挺通常我们都会将其设置为View.Gone或者是View.INVISIBLE来达到我们想要的目的的,这样做的优点就是逻辑简单而且控制起来比较灵活,但是其缺点就是耗资源,其实在内部Xml解析的时候同样也会将其解析并且实例化、设置属性的,同样还是会消耗系统资源的。这个时候ViewStub就闪亮登场了,为了更好的方便理解,首先我们看看其大概用法,同时分析源代码以后再来总结其结论

用法

首先我们在布局文件里面使用两个ViewStub,然后设置其 id 和layout 布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><Buttonandroid:id="@+id/display"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="显示/隐藏"android:textColor="@color/colorAccent" /><ViewStubandroid:id="@+id/style_1"android:layout_width="200dip"android:layout_height="200dip"android:layout="@layout/layout_content_first" /><ViewStubandroid:id="@+id/style_2"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout="@layout/layout_content_second" /></LinearLayout>
public class MainActivity extends AppCompatActivity {private ViewStub mViewStub1;private ViewStub mViewStub2;private View mViewStubContentView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//首先根据 id 获取 ViewStubmViewStub1 = findViewById(R.id.style_1);mViewStub2 = findViewById(R.id.style_2);//同时在我们需要的时候,初始化 ViewStub 包裹的布局,其实ViewStub的延迟加载就是这么个原理的mViewStubContentView = mViewStub1.inflate();//同时获取 ViewStub的 LayoutParams 参数ViewGroup.LayoutParams params = mViewStub1.getLayoutParams();Log.i("LOH", params.width + "...height..." + params.height);//我们使用display按钮来控制 ViewStub加载出来以后的view的显示与隐藏findViewById(R.id.display).setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if(mViewStubContentView.getVisibility() == View.VISIBLE) {mViewStubContentView.setVisibility(View.GONE);}else {mViewStubContentView.setVisibility(View.VISIBLE);}}});}
}

上面就是一个非常简单的 ViewStub 的使用案例,如果我们需要显示ViewStub 中布局文件的话,可以调用inflate 方法或者是也可以调用 ViewStub.setVisible(View.VISIBLE)就能将布局显示出来。

代码分析

构造方法分析

   public final class ViewStub extends View {.......//一般ViewStub 在xml中引用的话,都是走这个构造方法的。public ViewStub(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {super(context);final TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.ViewStub, defStyleAttr, defStyleRes);saveAttributeDataForStyleable(context, R.styleable.ViewStub, attrs, a, defStyleAttr,defStyleRes);//首先获取 inflateId 编号mInflatedId = a.getResourceId(R.styleable.ViewStub_inflatedId, NO_ID);//然后获取自定义属性 inflatedId 也就是 布局文件(xml 文件)mLayoutResource = a.getResourceId(R.styleable.ViewStub_layout, 0);//获取 ViewStub 定义的 id 编号mID = a.getResourceId(R.styleable.ViewStub_id, NO_ID);//记住这里需要回收(因为编译器会提示说这里内存泄漏)a.recycle();//这是是核心关键,首先设置 当前的View隐藏,同时设置自己不参与绘制,接着我们在 onDraw方法setVisibility(GONE);setWillNotDraw(true);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {setMeasuredDimension(0, 0);}@Overridepublic void draw(Canvas canvas) {}
}

通过上面的代码我们可以很清楚的知道了ViewStub不参与在View的绘制中,首先是设置了View.GONE,接着调用了View方法里面的 setWillNotDraw 不参与界面绘制,而且自己的 draw方法也未任何的实现。最后将自己的宽度和高度都设置为了0。

通过setVisible 方法显示界面

public void setVisibility(int visibility) {//首先第一次调用的 mInflatedViewRef 是为空的,所以就进入else 分支if (mInflatedViewRef != null) {View view = mInflatedViewRef.get();if (view != null) {view.setVisibility(visibility);} else {throw new IllegalStateException("setVisibility called on un-referenced view");}} else {//首先这里会直接调用 view的 setVisibility方法super.setVisibility(visibility);//接着判断我们传进来的 visibility的值,如果还是 GONE的话则不做处理,最后还是调用inflateif (visibility == VISIBLE || visibility == INVISIBLE) {inflate();}}}

通过上面的代码分析我们可以得出 setVisibility 最后调用的还是 inflate,所以这个方法才是关键的

public View inflate() {final ViewParent viewParent = getParent();//首先判断 ViewStub是否存在父控件,同时父控件是否 ViewGroupif (viewParent != null && viewParent instanceof ViewGroup) {//同时必须设置 ViewStub的layout 信息,不能单独设置 View。if (mLayoutResource != 0) {final ViewGroup parent = (ViewGroup) viewParent;//将 ViewStub 的layout布局文件转化为 View,但是不添加到 parent中final View view = inflateViewNoAdd(parent);//最后在viewParen中找到ViewStub的位置,同时将inflate出来的View替换ViewStub。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 {//ViewStub 不能单独使用,比如是 ViewGroup的一个子View。throw new IllegalStateException("ViewStub must have a non-null ViewGroup viewParent");}}

最后将实例化出来的view替换ViewStub在viewParent中的位置

private void replaceSelfWithView(View view, ViewGroup parent) {//首先获取ViewStub 在 parent中的位置final int index = parent.indexOfChild(this);//同时将其从父控件中移除parent.removeViewInLayout(this);//注意这里获取的是 ViewStub的 LayoutParams,也就是 layout出来的参数是没效果的。只能设置ViewStub的参数才行。final ViewGroup.LayoutParams layoutParams = getLayoutParams();if (layoutParams != null) {parent.addView(view, index, layoutParams);} else {parent.addView(view, index);}}

分析

这个类设计的其实非常的简单,其代码也就仅仅只有几百行。通过我们上面对代码的分析可以将其

总结如下

ViewStub 通过设置GONE 以及设置宽和高都为0,以及调用函数setWillNotDraw(true)来达到自己不绘制,不渲染在界面的效果,其实仅仅就是作为一个占着坑的意思。
ViewStub 只能调用一次 setVisibility 方法,而 setVisibility 最后调用的还是 inflate 方法。在 replaceSelfWithView 中 indexOfChild(this)代码中,如果ViewStub被移除了以后,index则是 -1那么 addView的时候则会抛出异常的。
ViewStub layoutParams 加入到载入的android:layotu视图上。而其根节点 layoutParams 设置无效
总结
通过上面的源代码我们可以分析得出ViewStub的懒加载的原理,首先通过 ViewStub占住位置而且又不绘制和显示在界面(原因看分析),接着我们在需要的时候调用ViewStub的setVisible方法和inflate方法来将页面显示,其原来就是将 layout 文件渲染成 view然后添加到其父控件(ViewGroup中),主要是替换之前ViewStub之前占用的位置来达到显示界面。

Viewstub 以及 view.setVisible(GONE/VISIBLE) 的实现原理相关推荐

  1. Android面试收集录12 View测量、布局及绘制原理

    一.View绘制的流程框架 View的绘制是从上往下一层层迭代下来的.DecorView-->ViewGroup(--->ViewGroup)-->View ,按照这个流程从上往下, ...

  2. android布局闪动,设置child.setvisibility(View.Visible)时,Android主屏幕出现效果闪烁问题...

    我已经制作了一个示例应用程序,可以在视镜中浏览不同的布局. XML基本上是(伪代码) 在Java代码中 public boolean onTouchEvent(MotionEvent event) c ...

  3. 安卓中的布局优化之clude、merge、ViewStub

    1.include include标签常用于将布局中的公共部分提取出来供其他layout共用,以实现布局模块化,也是平常我们设计布局时用的最多的. 需要注意的是如果一个根布局引入多个include需要 ...

  4. 深入分析 ViewStub 原理

    前言 最近在面试的时候被面试官问到ViewStub内部是如何使用占位的时候,我是一脸懵逼,说实话我之前对UI上的一些控件内部的代码看的非常少,所以一时答不上来,这个东西其实并不说有多难,而是我们平时在 ...

  5. include、ViewStub、merge优化布局标签

    前言 在写Android的xml布局时,用好 include.ViewStub.merge这三个标签,可以是我们的xml更加简洁.高效. include 按照官方的意思,include就是为了解决重复 ...

  6. 从源代码角度分析ViewStub 疑问与原理

    一.提出疑问 ViewStub比較简单.之前文章都提及到<Android 性能优化 三 布局优化ViewStub标签的使用>.可是在使用过程中有一个疑惑,究竟是ViewStub上设置的參数 ...

  7. Android API 中文(14) —— ViewStub

    前言 关键字: android.view.ViewStub,版本为Android 2.2 r1 本章翻译来自唐明 ,这里本博负责整理和发布,欢迎其他译者一起参与Android API 的中文翻译行动, ...

  8. Android性能优化:布局优化 详细解析(含include、ViewStub、merge讲解 )

    1. 影响的性能 布局性能的好坏 主要影响 :Android应用中的页面显示速度 2. 如何影响性能 布局影响Android性能的实质:页面的测量 & 绘制时间 1个页面通过递归 完成测量 & ...

  9. android support v4 viewstub,Android 控件ViewStub

    Android ViewStub 01. 简介 A ViewStub is an invisible, zero-sized View that can be used to lazily infla ...

  10. android布局的效率对比,Android使用ViewStub提高布局性能

    在Android开发中,View是我们必须要接触的用来展示的技术.通常情况下随着View视图的越来越复杂,整体布局的性能也会随之下降.这里介绍一个在某些场景下提升布局性能的View,它就是ViewSt ...

最新文章

  1. MySQL安装使用的2个问题
  2. sqlserver如何通过管理器设置字段的自增
  3. php mysql 分类_php+mysql实现无限分类实例详解
  4. 宇宙射线会导致路由器 bug,思科你认真的吗
  5. Crawler:爬虫基于urllib.request库实现获取指定网址上的所有图片
  6. emwin 使用外部字库_整6个月的等待,ST终于可以免费使用ThreadX全家桶了
  7. java split 坑_java String split 踩坑记
  8. makefile——小试牛刀
  9. php 导出excel 2007,使用PHPExcel导出Excel表
  10. 科大讯飞发布第三季度业绩报告:扣非净利润同比减少近9成
  11. treeview wpf代码设置选中_C# WPF过渡效果实现(Transitions)
  12. python中颜色表_python 颜色表
  13. 手游方舟怎么输入代码_单机方舟不能输入代码怎么回事 | 手游网游页游攻略大全...
  14. 如何解决下载慢的大问题,如neo4j等
  15. Linux14.04安装Mysql Linux公社
  16. R语言七天入门教程七:项目实战
  17. Angular------使用IDEA开发Angular
  18. 华为服务器串口修改密码,huawei恢复Console口密码
  19. server2003安装python3.4.4
  20. android通知详解

热门文章

  1. nsis出错_cf nsis错误怎么办 nsis错误解决办法全解
  2. 硬盘序列号更改工具 v0.1 下载
  3. 集成电路制造及工艺 主要名词解释
  4. c#WPF 扫雷游戏
  5. 办公自动化计算机操作试题及答案,办公自动化考试试题及答案
  6. Java调用ffmepg+mencoder视频格式转换(*)
  7. 百会:物联网推动CRM创造新型服务
  8. vf计算机教程,VF教程,打印版.pdf
  9. 复制iPhone端百度网盘下载好的视频到电脑(Mac / Windows)- iOS 12.4
  10. java局域网发送文件_Java如何实现局域网文件传输代码案例分享