作者:zhaoyunnidaye_260 | 来源:互联网 | 2023-09-25 11:25
1.viewstub就是动态加载试图;也就是在我们的app启动绘制页面的时候,他不会绘制到view树中;当在代码中执行inflate操作后,她才会被添加到试图中。其实ViewStub就是一个宽高都为0的一个View,它默认是不可见的,只有通过调用setVisibility函数或者Inflate函数才 会将其要装载的目标布局给加载出来,从而达到延迟加载的效果,这个要被加载的布局通过android:layout属性来设置。最终目的是把app加载页面的速度提高了,使用户体验更好。
2.看一个简单的demo
viewstub.xml
activity_myviewstub.xml
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;@Overrideprotected 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只会进行一次,当第二次调用的时候,就会抛异常;也可以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);//nulltextView = 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.当调用第二次inflate的时候,会报错:
我们看一下这是为什么?进入viewStub.inflate();的源码:
public View inflate() {final ViewParent viewParent &#61; getParent();if (viewParent !&#61; null && viewParent instanceof ViewGroup) {if (mLayoutResource !&#61; 0) {final ViewGroup parent &#61; (ViewGroup) viewParent;final View view &#61; inflateViewNoAdd(parent);replaceSelfWithView(view, parent);mInflatedViewRef &#61; new WeakReference<>(view);if (mInflateListener !&#61; 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时才会报这个错误&#xff1b;那么第一次调用的时候&#xff0c;肯定是进去了&#xff1b;发现一个方法replaceSelfWithView(view,parent)&#xff1b;view就是我们在布局文件中给viewstub指定的layout所引用的那个布局&#xff1b;parent就是getParent方法得到的&#xff0c;也就是acticity的填充布局LinearLayout&#xff1b;
进去看一下&#xff1a;
private void replaceSelfWithView(View view, ViewGroup parent) {final int index &#61; parent.indexOfChild(this);parent.removeViewInLayout(this);final ViewGroup.LayoutParams layoutParams &#61; getLayoutParams();if (layoutParams !&#61; null) {parent.addView(view, index, layoutParams);} else {parent.addView(view, index);}}
可以发现parent.removeViewInLayout(this);把this就是viewstub从父布局linearlayout中移除了&#xff1b;parent.addView()就是把view&#xff08;也就是我们引用的布局&#xff09;添加到了父布局LinearLayout中。
我们用layout inspector来查看一下&#xff1a;
inflate前&#xff1a;可以看到viewstub是灰色的
inflate后&#xff1a;可以看到viewstub直接被移除了&#xff0c;把引用布局直接放到view树里了。
所以当我们第二次再调用inflate方法时&#xff0c;viewstub的parent已经为空了&#xff1b;就会抛出此异常&#xff1b;
当调用textView &#61; viewStub.findViewById(R.id.hello_tv);//获取到的textview是空的&#xff1b;
而使用textView &#61; findViewById(R.id.hello_tv);就可以直接拿到控件对象了&#xff1b;
当实现引用布局的显示和隐藏时&#xff0c;测试发现使用viewstub的setVisibility()方法可以实现&#xff0c;这是为什么呢&#xff1f;&#xff1b;按理说使用constraintLayout.setVisibility()当然也可以&#xff1b;根据上面的view树结构来看&#xff0c;好像使用引用布局的setVisibility()方法更合理一些&#xff1b;
下面我们再来看看viewstub的setVisibility()为什么也可以&#xff1b;跟进源码看看&#xff1a;
源码中使用mInflatedViewRef获取到view&#xff0c;然后设置隐藏与显示&#xff1b;mInflatedViewRef是一个view的弱引用WeakReference
其实在上面的inflate方法中已经为其添加了mInflatedViewRef &#61; new WeakReference<>(view);这个view就是viewstub中的引用布局&#xff1b;
所以&#xff0c;使用viewstub可以实现相同的显示或隐藏效果&#xff1b;
从上图的最后一个红色框中可以发现&#xff0c;假设现在我没有调用inflate方法&#xff0c;而是直接点击了show按钮&#xff1b;然后引用布局也可以绘制出来&#xff1b;这就是我在写demo的时候&#xff0c;直接上去点击show按钮&#xff0c;竟然也可以显示的原因。