ViewStub的理解

菜鸟一枚,不擅长用文字解释概念 ,所以,接下来这段出自《Android开发艺术探究》任玉刚 著

引文:

ViewStub继承了View,它非常轻量级且宽高都是0,因为本身不参与任何的布局和绘制过程。ViewStub的意义在于按需加载所需的布局文件,在实际开发中,有很多布局文件在正常情况下不会显示,比如网络异常时的界面,这个时候就没有必要在整个界面初始化的时候将其加载进来,通过ViewStub就可以在使用的时候再加载,提高了程序初始化时的性能。

在需要加载ViewStub中的布局时,可以按照如下两种方式进行:

((ViewStub)findViewById(R.id.mViewStub)).inflate();

((ViewStub)findViewById(R.id.mViewStub)).setVisibility(View.VISIBLE);

当ViewStub通过setVisible或者inflate方法加载后,ViewStub就会被它内部的布局替换掉,这个时候ViewStub不在是整个布局结构中的一部分了。

——引文结束

那我们先通过一个Demo了解一下:

主布局文件:

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.example.liujianbo.viewstub.MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="16sp"
        android:layout_margin="10dp"
        android:layout_gravity="center"
        android:text="曾经沧海难为水,"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="16sp"
        android:layout_margin="10dp"
        android:layout_gravity="center"
        android:text="除却巫山不是云。"/>

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="显示"/>

    <ViewStub
        android:id="@+id/viewStub"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout="@layout/details"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="16sp"
        android:layout_margin="10dp"
        android:layout_gravity="center"
        android:text="测试"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="16sp"
        android:layout_margin="10dp"
        android:layout_gravity="center"
        android:text="测试"/>


</LinearLayout>
接下来我们在写一个ViewStub对应的布局文件:

(比如ViewStub里面有一个android:layout=@layout/details属性)

details.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="wrap_content">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="16sp"
        android:layout_margin="10dp"
        android:layout_gravity="center"
        android:text="取次花丛懒回顾,"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="16sp"
        android:layout_margin="10dp"
        android:layout_gravity="center"
        android:text="半缘修道半缘君。"/>

</LinearLayout>

如果成功用details.xml替换我们的ViewStub的话,就可以看见整首诗了,恩!

接下来是我们的主活动:

MainActivity

package com.example.liujianbo.viewstub;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewStub;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

    private Button mButton;
    private ViewStub mViewStub;
    private boolean isHideViewStub = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }

    private void initView(){
        mViewStub = (ViewStub) findViewById(R.id.viewStub);

        mButton = (Button) findViewById(R.id.button);
        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (isHideViewStub) {
                    mViewStub.setVisibility(View.GONE);
                    isHideViewStub = false;
                }
                else {
                    mViewStub.setVisibility(View.VISIBLE);
                    isHideViewStub = true;
                }
            }
        });
    }

}

效果图:



看到了,开心叭。

那接下来我们开始理解一下任玉刚先生的那些话:

1.   “ViewStub继承了View,他非常轻量级,且宽高都是0,因此它本身不参与任何的布局和绘制过程。

ViewStub就类似于四代目的标记,引入的布局就类似于四代目火影。标记是没有大小的,但是可以引入四代目,所以说,被四代目标记的人,已经预定了死神。(看火影的朋友嗨起来!!!)

那我们用一个布局文件说明一下问题:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.example.liujianbo.viewstub.MainActivity">
    <!--我们在原布局的上面添加了两个100dp高的ViewStub-->
    <ViewStub
        android:layout_width="match_parent"
        android:layout_height="100dp"
        />
    <ViewStub
        android:layout_width="match_parent"
        android:layout_height="100dp"
        />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="16sp"
        android:layout_margin="10dp"
        android:layout_gravity="center"
        android:text="曾经沧海难为水,"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="16sp"
        android:layout_margin="10dp"
        android:layout_gravity="center"
        android:text="除却巫山不是云。"/>

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="显示"/>

    <ViewStub
        android:id="@+id/viewStub"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout="@layout/details"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="16sp"
        android:layout_margin="10dp"
        android:layout_gravity="center"
        android:text="测试"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="16sp"
        android:layout_margin="10dp"
        android:layout_gravity="center"
        android:text="测试"/>


</LinearLayout>

我们在原布局activity_main.xml,“曾经沧海难为水”TextView的上面添加了两个100dp高的ViewStub,看看主界面。

效果图:



我们的“曾经沧海难为水”还是在原来的位置,布局和绘制鸟都不鸟它,老子打烂你的100dp!

那么,ViewStub的layout_width和layout_height有什么用呢?

我们在修改一下activity_main.xml文件,只改ViewStub的高,从wrap_content——>300dp

点击按钮后的效果图:




这次把高度改成50dp。



结论来了:

id就是findViewById的时候,找那个ViewStub用的。

layout=@layout/~~就是,找到ViewStub引用的那个布局,然后插进来。

layout_width和layout_height,就是,只给你这么点地方让你插,不管你多大,也不管你插多少次。

(如果有这样的处女就好了,哈哈。)

2."在需要加载ViewStub中的布局时,可以按照如下两种方式进行:

((ViewStub)findViewById(R.id.mViewStub)).inflate();

((ViewStub)findViewById(R.id.mViewStub)).setVisibility(View.VISIBLE);"

我们理解一下这两个方法。

ViewStub.inflate();

API:Inflates the layout resource identified by getLayoutResource() and replaces this StubbedView in its parent by the inflated layout resource.

大概意思就是:通过getLayoutResource()方法加载一个布局,然后用这个布局替换这个ViewStub。

ViewStub.setVisibility();

API:When visibility is set to VISIBLE or INVISIBLEinflate() is invoked and this StubbedView is replaced in its parent by the inflated layout resource.

大概意思是:当visibility被设置为VISIBLE或者INVISIBLE的时候,inflate()就会被唤醒,并且用inflate()方法加载的布局代替ViewStub。

看完之后还是恍恍惚惚,那我们做实验?

之前在findViewById找到ViewStub对象后,用setVisibility()和一个boolean类型的isHideViewStub来控制的,就是这样:

mButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        if (isHideViewStub) {
            mViewStub.setVisibility(View.GONE);
            isHideViewStub = false;
        }
        else {
            mViewStub.setVisibility(View.VISIBLE);
            isHideViewStub = true;
        }
    }
});
不是说inflate也可以吗?那么我们试试:

mButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        if (isHideViewStub) {
            mViewStub.setVisibility(View.GONE);
            isHideViewStub = false;
        }
        else {
            mViewStub.inflate();
            isHideViewStub = true;
        }
    }
});
效果图:



什么?报错了!错误类型是:java.lang.IllegalStateException: ViewStub must have a non-null ViewGroup viewParent

非法状态异常:ViewStub必须有一个“不为空的”ViewGroup viewParent。(不会翻译了。)

反正就是什么为空了呗。

就是说,我们之前已经用inflate();方法加载一个布局了,后来只是把它隐藏了,你现在就又要加载一个,犯法了!什么法?安卓法!

就好比你老婆出门了,然后你家里就没老婆了,你就重新娶一个?简直就是犯法,重婚罪!!!!!

上面说到:

ViewStub.setVisibility();

API:When visibility is set to VISIBLE or INVISIBLEinflate() is invoked and this StubbedView is replaced in its parent by the inflated layout resource.

大概意思是:当visibility被设置为VISIBLE或者INVISIBLE的时候,inflate()就会被唤醒,并且用inflate()方法加载的布局代替ViewStub。

这不是矛盾了吗?

那我们上源码?(楼主第一次自己找源码,分析源码,写的不好就别看了,免得辣眼睛。)

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");
}
}


public void setVisibility(int visibility) {
    if (mInflatedViewRef != null) {
        View view = mInflatedViewRef.get();
        if (view != null) {
            view.setVisibility(visibility);
        } else {
            throw new IllegalStateException("setVisibility called on un-referenced view");
        }
    } else {
        super.setVisibility(visibility);
        if (visibility == VISIBLE || visibility == INVISIBLE) {
            inflate();
        }
    }
}

当我们第一次调用setVisibility的时候(VISIBLE或者INVISIBLE),就会间接调用inflate。

或者我们第一次直接调用inflate。

就会执行

mInflatedViewRef = new WeakReference<View>(view);

然后在调用setVisibility的时候,mInflatedViewRef就不为空啦,通过它可以找到之前加载的那个VIEW,然后设置,看得见,看不见。

也就是说我们之后调用mViewStub.setVisibility()的时候,实际上是执行了view.setVisibility();那个布局已经被当成一个view了。

那么这个view怎么来的?源码中有这么一句:

final View view = factory.inflate(mLayoutResource, parent,
        false);

就是用inflate加载出来的,这个大家肯定经常用。

解释一下我们的那个异常:

if (viewParent != null && viewParent instanceof ViewGroup) {
 
 
} else {
    throw new IllegalStateException("ViewStub must have a non-null ViewGroup viewParent");
}

这就引出作者任玉刚先生的那句话了:

3.当ViewStub通过setVisible或者inflate方法加载后,ViewStub就会被它内部的布局替换掉,这个时候ViewStub不在是整个布局结构中的一部分了。

viewStub都不存在了,肯定找不到他爸爸viewParent,人家有新儿子了,就你inflate出来的那个,编程的世界就是这么残忍,虎毒食子啊!

不好好学习,新生代分分钟取代你,恩。我就是新生代,哈哈。

ViewStub就好比一个避孕套,用一次就扔了,已经不存在了,恩。


大概就这么多!剩下的自己分析源码去!



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值