前语

当烘托一个活动时,这个活动的布局或许会有许多visible为invisible和gone的情况,尽管这些控件尽管现在不显现在屏幕上,可是系统在加载这个布局文件时仍是会加载它的,这就影响了这个页面的加载效率,因为这些不可见的控件提早加载它们并没有什么实际的含义,反而会减缓页面的加载时间,所以为了解决这个问题能够运用ViewStub来懒加载暂时不显现的布局.

1.ViewStub的优势

简略来说, ViewStub能够做到按需加载一个布局,咱们能够操控它加载的机遇,而不是在Activity的onCreate办法中去加载.即懒加载

2.ViewStub的运用

    <ViewStub
•        android:id="@+id/stub"
•        android:inflatedId="@+id/text"
•        android:layout="@layout/text_view_stub"
•        android:layout_width="match_parent"
•        android:layout_height="wrap_content"
•        app:layout_constraintTop_toBottomOf="@id/textView3"
•        android:layout_marginTop="180dp"
•        android:layout_marginLeft="100dp"/>

属性 功用

android:inflatedId=”@+id/text” 为咱们要加载的布局供给一个id android:layout 咱们需要加载的布局 除此之外

        app:layout_constraintTop_toBottomOf="@id/textView3"
        android:layout_marginTop="180dp"
        android:layout_marginLeft="100dp"/>

这些代表咱们懒加载的布局在父布局的位置,如果懒加载的布局有相同的属性,将会被覆盖

//经过id得到viewStub方针
ViewStub viewStub = findViewById(R.id.stub);
//动态加载布局
 viewStub.inflate();

简略实战

1.viewstub便是动态加载企图

也便是在咱们的app发动制作页面的时分,他不会制作到view树中;当在代码中执行inflate操作后,她才会被增加到企图中。其实ViewStub便是一个宽高都为0的一个View,它默认是不可见的,只有经过调用setVisibility函数或许Inflate函数才 会将其要装载的方针布局给加载出来,从而达到推迟加载的作用,这个要被加载的布局经过android:layout属性来设置。最终目的是把app加载页面的速度提高了,运用户体会更好。

2.看一个简略的demo

viewstub.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/inflatedStart"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
​
    <TextView
        android:id="@+id/hello_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:text="DATA EMPTY!"/>
​
</android.support.constraint.ConstraintLayout>

activity_myviewstub.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="match_parent">
​
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="inflate"
        android:text="inflate" />
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="setData"
        android:text="setdata"/>
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="hide"
        android:text="hide"/>
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="show"
        android:text="show"/>
    <ViewStub
        android:id="@+id/vs"
        android:inflatedId="@+id/inflatedStart"
        android:layout="@layout/viewstub"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
​
</LinearLayout>

MyViewStubActivity.java

package com.ysl.myandroidbase.viewstub;
​
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.constraint.ConstraintLayout;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.ViewStub;
import android.widget.TextView;
​
import com.ysl.myandroidbase.R;
​
public class MyViewStubActivity extends AppCompatActivity {
    private ViewStub viewStub;
    private TextView textView;
    private View inflate;
    private ConstraintLayout constraintLayout;
​
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_myviewstub);
        viewStub = findViewById(R.id.vs);
        //textView  = (TextView) findViewById(R.id.hello_tv);空指针,因为viewstub没有inflate
    }
    public  void inflate(View view){
        if (inflate == null) {//inflate只会进行一次,当第2次调用的时分,就会抛反常;也能够try catch进行处理
            inflate = viewStub.inflate();
            constraintLayout = findViewById(R.id.inflatedStart);
            System.out.println(constraintLayout);
            System.out.println("viewStub-------->"+viewStub);
            textView  = viewStub.findViewById(R.id.hello_tv);//获取到的textview是空的;
            System.out.println("viewStub textView-------->"+textView);//null
            textView  = constraintLayout.findViewById(R.id.hello_tv);
            System.out.println("constraintLayout textView-------->"+textView);
            textView  = findViewById(R.id.hello_tv);
            System.out.println("textView-------->"+textView);
        }
    }
    public void setData(View view){
        if (constraintLayout != null) {
            textView = constraintLayout.findViewById(R.id.hello_tv);
            textView.setText("HAVE DATA !!!");
        }
    }
    public void hide(View view){
        viewStub.setVisibility(View.GONE);
//        if (constraintLayout != null){
//            constraintLayout.setVisibility(View.GONE);
//        }
    }
    public void show(View view){
        viewStub.setVisibility(View.VISIBLE);
//        if (constraintLayout != null){
//            constraintLayout.setVisibility(View.VISIBLE);
//        }
    }
}

3.当调用第2次inflate的时分,会报错:

Android ViewStub的使用方法——边走边看边学

修改切换为居中

增加图片注释,不超越 140 字(可选)

咱们看一下这是为什么?进入viewStub.inflate();的源码:

public View inflate() {
        final ViewParent viewParent = getParent();
​
        if (viewParent != null && viewParent instanceof ViewGroup) {
            if (mLayoutResource != 0) {
                final ViewGroup parent = (ViewGroup) viewParent;
                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");
        }
    }

能够看到当viewParent为空或许不是viewgroup时才会报这个错误;那么第一次调用的时分,肯定是进去了;发现一个办法replaceSelfWithView(view,parent);view便是咱们在布局文件中给viewstub指定的layout所引证的那个布局;parent便是getParent办法得到的,也便是acticity的填充布局LinearLayout;

进去看一下:

private void replaceSelfWithView(View view, ViewGroup parent) {
        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);
        }
    }

能够发现parent.removeViewInLayout(this);把this便是viewstub从父布局linearlayout中移除了;parent.addView()便是把view(也便是咱们引证的布局)增加到了父布局LinearLayout中。

咱们用layout inspector来检查一下:

inflate前:能够看到viewstub是灰色的

Android ViewStub的使用方法——边走边看边学

修改

增加图片注释,不超越 140 字(可选)

inflate后:能够看到viewstub直接被移除了,把引证布局直接放到view树里了。

Android ViewStub的使用方法——边走边看边学

修改

增加图片注释,不超越 140 字(可选)

所以当咱们第2次再调用inflate办法时,viewstub的parent现已为空了;就会抛出此反常;

当调用textView = viewStub.findViewById(R.id.hello_tv);//获取到的textview是空的;

而运用textView = findViewById(R.id.hello_tv);就能够直接拿到控件方针了;

当实现引证布局的显现和躲藏时,测试发现运用viewstub的setVisibility()办法能够实现,这是为什么呢?;按理说运用constraintLayout.setVisibility()当然也能够;根据上面的view树结构来看,好像运用引证布局的setVisibility()办法更合理一些;

下面咱们再来看看viewstub的setVisibility()为什么也能够;跟进源码看看:

Android ViewStub的使用方法——边走边看边学

修改切换为居中

增加图片注释,不超越 140 字(可选)

源码中运用mInflatedViewRef获取到view,然后设置躲藏与显现;mInflatedViewRef是一个view的弱引证WeakReference

其实在上面的inflate办法中现已为其增加了mInflatedViewRef = new WeakReference<>(view);这个view便是viewstub中的引证布局;

所以,运用viewstub能够实现相同的显现或躲藏作用;

从上图的最后一个红色框中能够发现,假定现在我没有调用inflate办法,而是直接点击了show按钮;然后引证布局也能够制作出来;这便是我在写demo的时分,直接上去点击show按钮,竟然也能够显现的原因。

Android ViewStub的使用方法——边走边看边学

修改切换为居中

增加图片注释,不超越 140 字(可选)

以上便是Android ViewStub的运用与简略的演练;如果想要进阶自己Android技能,能够参阅这份《Android核心技术笔记》里边记载有Android的核心技术与其他前沿技术。 如有其他问题能够点下方链接

传送直达↓↓↓docs.qq.com/doc/DUkNRVF…

文末

Android ViewStub的运用注意事项

inflate办法只能调用一次,再次调用会出反常 咱们看下inflate办法的源码,一旦第2次调用inflate办法,咱们的到viewParent将等于null,会报 throw new IllegalStateException(“ViewStub must have a non-null ViewGroup viewParent”);反常,所以总归一句话,这个懒加载只能加载一次

public View inflate() {
       final ViewParent viewParent = getParent();
​
       if (viewParent != null && viewParent instanceof ViewGroup) {
           if (mLayoutResource != 0) {
               final ViewGroup parent = (ViewGroup) viewParent;
               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");
       }
   }