ViewStub延迟加载

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

大家好,又见面了,我是你们的朋友全栈君。

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

推荐的做法是使用android.view.ViewStub,ViewStub是一个轻量级的View,使用非常简单:

mViewStub = (ViewStub) this.findViewById(R.id.viewstub);

mViewStub.inflate();

它一个不可见的,不占布局位置,占用资源非常小的控件,相当于一个“占位控件”。使用时可以为ViewStub指定一个布局,在Inflate布局的时候,只有ViewStub会被初始化,然后当ViewStub被设置为可见的时或调用了ViewStub.inflate()的时候,ViewStub所指向的布局就会被inflate实例化,且此布局文件直接将当前ViewStub替换掉,然后ViewStub的布局属性(layout_margin***、layout_width等)都会传给它所指向的布局。这样,就可以使用ViewStub在运行时动态显示布局,节约内存资源。

下面我们从ViewStub源码来看下inflate()方法的实现原理:

 public View inflate() {
        final ViewParent viewParent = getParent();
 
        if (viewParent != null && viewParent instanceof ViewGroup) {
            if (mLayoutResource != 0) {
                final ViewGroup parent = (ViewGroup) viewParent;
                final LayoutInflater factory;
                if (mInflater != null) {
                    factory = mInflater;
                } else {
                    factory = LayoutInflater.from(mContext);
                }
                final View view = factory.inflate(mLayoutResource, parent,
                        false);
 
                if (mInflatedId != NO_ID) {
                    view.setId(mInflatedId);
                }
 
                final int index = parent.indexOfChild(this);
                parent.removeViewInLayout(this);
 
                final ViewGroup.LayoutParams layoutParams = getLayoutParams();
                if (layoutParams != null) {
                    parent.addView(view, index, layoutParams);
                } else {
                    parent.addView(view, index);
                }
 
                mInflatedViewRef = new WeakReference<View>(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、在第2行,首先是得到ViewStub它的父视图对象。

2、然后在第4行一开始肯定是能进入判断的,mLayoutResource就是需要inflate的布局资源,然后在第13行填充这个布局资源。

3、然后在第21行,重要的来了,parent.removeViewInLayout(this);这段代码是什么意思呢?看方法名字就知道了,this是代表ViewStub对象,意思就是把当前ViewStub对象从父视图中移除了。

4、然后第23~28行,就是得到ViewStub的LayoutParams布局参数对象,如果存在就把它赋给被inflate的布局对象,然后把inflate的布局对象添加到父视图中。
5、最后返回inflate的布局对象。

好了,源码解析完毕!!!

从上述可知,当我们第二次调用ViewStub.inflate()方法的时候,因为已经移除了ViewStub对象,在第2、4行,得到的viewParent就为null,此时判断时候就会走else抛出一个IllegalStateException异常:ViewStub must have a non-null ViewGroup viewParent。

需要注意的几点:
1.ViewStub之所以常称之为“延迟化加载”,是因为在教多数情况下,程序无需显示ViewStub所指向的布局文件,只有在特定的某些较少条件下,此时ViewStub所指向的布局文件才需要被inflate,且此布局文件直接将当前ViewStub替换掉,具体是通过viewStub.infalte()或viewStub.setVisibility(View.VISIBLE)来完成。

2.正确把握住ViewStub的应用场景非常重要,因为使用ViewStub可以优化布局,一般应用在当前布局或控件在用户使用较少情况下,这样可以提高性能,节约内存,加快界面渲染。

3.对ViewStub的inflate操作只能进行一次,因为inflate的时候是将它指向的布局实例化并替换掉当前ViewStub本身(由此体现出了ViewStub“占位”性质),一旦替换后,此时原来的布局文件中就没有ViewStub控件了,因此,如果多次对ViewStub进行infalte,会出现错误信息:ViewStub must have a non-null ViewGroup viewParent。

4.3中所讲到的ViewStub指向的布局文件解析inflate并替换掉当前ViewStub本身,并不是完全意义上的替换(与include标签不太一样),替换时,布局文件的layout params是以ViewStub为准,其他布局属性是以布局文件自身为准。

5.ViewStub本身是不可见的,对ViewStub.setVisibility(int visibility)与其他View控件不一样,我们可以从源码角度来看一下ViewStub.setVisibility()方法的作用:
在这里插入图片描述
这个方法意思就是ViewStub的setVisibility()设置成View.VISIBLE或INVISIBLE如果是首次使用,都会自动inflate其指向的布局文件,并替换ViewStub本身,再次使用则是相当于对其指向的布局文件设置可见性。

好了,原理讲了那么多,来看看代码怎么实现吧:
使用了ViewStub的activity_main.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.example.viewstub.MainActivity" >
 
    <Switch
        android:id="@+id/switch1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
 
    <ViewStub
        android:id="@+id/viewstub"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginBottom="10dp"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="10dp"
        android:layout_marginTop="10dp"
        android:layout="@layout/hide_layout" />
 
</LinearLayout>

hide_layout.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:background="#00ffff"
    android:orientation="vertical" >
    <Button
        android:id="@+id/hide_layout_btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="Click me" />
</LinearLayout>

代码文件:

public class MainActivity extends ActionBarActivity {
	private ViewStub mViewStub;
	private Switch mSwitch;
	private boolean flag = false;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		mViewStub = (ViewStub) this.findViewById(R.id.viewstub);//实例化ViewStub
		mSwitch = (Switch) findViewById(R.id.switch1);
		mSwitch.setOnCheckedChangeListener(new OnCheckedChangeListener() {
			@Override
			public void onCheckedChanged(CompoundButton buttonView,
					boolean isChecked) {
				if (isChecked) {
					if(!flag){
						mViewStub.inflate();//ViewStub只能被inflate一次,会返回一个填充资源的View对象
						//mViewStub.setVisibility(View.VISIBLE);)
						flag = true;
					}else{
						mViewStub.setVisibility(View.VISIBLE);
					}
					Button mBtn = (Button) findViewById(R.id.hide_layout_btn);//ViewStub被替换的布局内的控件
					mBtn.setOnClickListener(new OnClickListener() {
						@Override
						public void onClick(View v) {
							Toast.makeText(getApplicationContext(), "Click me!",
									Toast.LENGTH_SHORT).show();
						}
					});
				} else {
					mViewStub.setVisibility(View.GONE);
				}
			}
		});
	}
}

注:使用ViewStub被替换的布局中的控件,直接findViewById即可。

减少视图层级merge

<merge />标签在UI的结构优化中起着非常重要的作用,它可以删减多余的层级,优化UI。<merge />多用于替换FrameLayout(因为所有的
Activity视图的根结点都是FrameLayout,如果当前的布局根结点是Framelayout,那么可以用merge替代,减少多余的层级)或者当一个布局
包含另一个时,<merge />标签消除视图层次结构中多余的视图组。例如你的主布局文件是垂直布局,又include引入了一个垂直布局,这是
如果include布局使用的LinearLayout就没意义了,使用的话反而减慢你的UI渲染。这时可以使用<merge />标签进行优化。

<merge xmlns:android="http://schemas.android.com/apk/res/android">
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text=""/>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text=""/>
</merge>
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/153969.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • java linkedhashset_Java LinkedHashSet「建议收藏」

    java linkedhashset_Java LinkedHashSet「建议收藏」JavaLinkedHashSet1什么是JavaLinkedHashSetJavaLinkedHashSet类实现了Set接口。并且HashSet的子类。关于JavaLinkedHashSet类的核心要点是:JavaLinkedHashSet类不能存储重复元素,和HashSet相似。JavaLinkedHashSet类提供所有Set接口的操作,并允许空元素。JavaLinkedH…

    2022年10月12日
  • C#并发实战Parallel.ForEach使用

    C#并发实战Parallel.ForEach使用前言:最近给客户开发一个伙食费计算系统,大概需要计算2000个人的伙食。需求是按照员工的预定报餐计划对消费记录进行检查,如有未报餐有刷卡或者有报餐没刷卡的要进行一定的金额扣减等一系列规则。一开始我的想

  • JavaScript触屏滑动API介绍

    随着触屏手机、平板电脑的普及和占有更多用户和使用时间,触屏的触碰、滑动等事件也成为javaScript开发不可避免的知识,现在何问起就和大家一起学习js的触屏操作,js的触屏touchmove事件,为

    2021年12月28日
  • Linux如何设置IP地址_linux添加ip

    Linux如何设置IP地址_linux添加ip对于很多刚刚接触linux的朋友来说,如何设置linux系统的IP地址,作为第一步,下面学习啦小编以centos系统为例,给大家演示如何给centos设置IP地址设置linux系统的IP地址方法1、自动获取IP地址虚拟机使用桥接模式,相当于连接到物理机的网络里,物理机网络有DHCP服务器自动分配IP地址。#dhclient自动获取ip地址命令#ifconfig查询系统里网卡信息,ip地址、MA…

    2022年10月20日
  • windows如何设置定时关机「建议收藏」

    windows如何设置定时关机「建议收藏」电脑定时关机是通过shutdown这个命令来实现的,不论是windows还是linux,这个命令都是关机的意思。下面我们简要的介绍一下windows如何定时关机:思路一:通过shutdown这个命令加参数来实现按“win+R”(注win键即指windows徽标键,该键的最明显的标识就是在键面上有个Windows的徽标),此时会弹出“运行”对话框,如下图所示。在对话框的编辑栏里输入“Shutdown…

  • idea2021年最新永久激活码【在线注册码/序列号/破解码】

    idea2021年最新永久激活码【在线注册码/序列号/破解码】,https://javaforall.cn/100143.html。详细ieda激活码不妨到全栈程序员必看教程网一起来了解一下吧!

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号