布局优化之ViewStub源码分析

Posted 安卓笔记侠

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了布局优化之ViewStub源码分析相关的知识,希望对你有一定的参考价值。

源码分析

  1 @RemoteView  
  2 public final class ViewStub extends View {  
  3     private int mInflatedId;  
  4     private int mLayoutResource;  
  5   
  6     private WeakReference<View> mInflatedViewRef;  
  7   
  8     private LayoutInflater mInflater;  
  9     private OnInflateListener mInflateListener;  
 10   
 11     public ViewStub(Context context) {  
 12         this(context, 0);  
 13     }  
 14   
 15     /**  
 16      * Creates a new ViewStub with the specified layout resource.  
 17      *  
 18      * @param context The application‘s environment.  
 19      * @param layoutResource The reference to a layout resource that will be inflated.  
 20      */  
 21     public ViewStub(Context context, @LayoutRes int layoutResource) {  
 22         this(context, null);  
 23   
 24         mLayoutResource = layoutResource;  
 25     }  
 26   
 27     public ViewStub(Context context, AttributeSet attrs) {  
 28         this(context, attrs, 0);  
 29     }  
 30   
 31     public ViewStub(Context context, AttributeSet attrs, int defStyleAttr) {  
 32         this(context, attrs, defStyleAttr, 0);  
 33     }  
 34   
 35     public ViewStub(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {  
 36         super(context);  
 37   
 38         final TypedArray a = context.obtainStyledAttributes(attrs,  
 39                 R.styleable.ViewStub, defStyleAttr, defStyleRes);  
 40         mInflatedId = a.getResourceId(R.styleable.ViewStub_inflatedId, NO_ID);  
 41         mLayoutResource = a.getResourceId(R.styleable.ViewStub_layout, 0);  
 42         mID = a.getResourceId(R.styleable.ViewStub_id, NO_ID);  
 43         a.recycle();  
 44   
 45         setVisibility(GONE); // 默认不可见  
 46         setWillNotDraw(true); // 如果View不绘制任何内容,设置这个标记可以优化性能,默认View没有设置这个标记,如果重写onDraw,就不要设置这个标记  
 47     }  
 48   
 49     @Override  
 50     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
 51         setMeasuredDimension(0, 0); // 测量时尺寸为0  
 52     }  
 53   
 54     @Override  
 55     public void draw(Canvas canvas) { // 不绘制内容  
 56     }  
 57   
 58     @Override  
 59     protected void dispatchDraw(Canvas canvas) {  
 60     }  
 61 ..... 省去部分代码  
 62   
 63     private View inflateViewNoAdd(ViewGroup parent) {  
 64         final LayoutInflater factory;  
 65         if (mInflater != null) {  
 66             factory = mInflater;  
 67         } else {  
 68             factory = LayoutInflater.from(mContext);  
 69         } // 通过inflate填充布局  
 70         final View view = factory.inflate(mLayoutResource, parent, false);  
 71   
 72         if (mInflatedId != NO_ID) {  
 73             view.setId(mInflatedId);  
 74         }  
 75         return view;  
 76     }  
 77   
 78     private void replaceSelfWithView(View view, ViewGroup parent) {  
 79         final int index = parent.indexOfChild(this);  
 80         parent.removeViewInLayout(this); // 移除ViewStub,后面不能在inflate  
 81   
 82         final ViewGroup.LayoutParams layoutParams = getLayoutParams(); // 获得ViewStub的布局参数  
 83         if (layoutParams != null) {  
 84             parent.addView(view, index, layoutParams); // 把ViewStub指定的布局添加到parent中  
 85         } else {  
 86             parent.addView(view, index);  
 87         }  
 88     }  
 89   
 90     /**  
 91      * Inflates the layout resource identified by {@link #getLayoutResource()}  
 92      * and replaces this StubbedView in its parent by the inflated layout resource.  
 93      *  
 94      * @return The inflated layout resource.  
 95      *  
 96      */  
 97     public View inflate() {  
 98         final ViewParent viewParent = getParent(); // 获取ViewStub的parent  
 99   
100         if (viewParent != null && viewParent instanceof ViewGroup) {  
101             if (mLayoutResource != 0) {  
102                 final ViewGroup parent = (ViewGroup) viewParent;  
103                 final View view = inflateViewNoAdd(parent);  
104                 replaceSelfWithView(view, parent);  
105   
106                 mInflatedViewRef = new WeakReference<>(view);  
107                 if (mInflateListener != null) {  
108                     mInflateListener.onInflate(this, view);  
109                 }  
110   
111                 return view;  
112             } else {  
113                 throw new IllegalArgumentException("ViewStub must have a valid layoutResource");  
114             }  
115         } else {  
116             throw new IllegalStateException("ViewStub must have a non-null ViewGroup viewParent");  
117         }  
118     }  
119   
120     /**  
121      * Specifies the inflate listener to be notified after this ViewStub successfully  
122      * inflated its layout resource.  
123      *  
124      * @param inflateListener The OnInflateListener to notify of successful inflation.  
125      *  
126      * @see android.view.ViewStub.OnInflateListener  
127      */  
128     public void setOnInflateListener(OnInflateListener inflateListener) {  
129         mInflateListener = inflateListener;  
130     }  
131   
132     /**  
133      * Listener used to receive a notification after a ViewStub has successfully  
134      * inflated its layout resource.  
135      *  
136      * @see android.view.ViewStub#setOnInflateListener(android.view.ViewStub.OnInflateListener)   
137      */  
138     public static interface OnInflateListener {  
139         /**  
140          * Invoked after a ViewStub successfully inflated its layout resource.  
141          * This method is invoked after the inflated view was added to the  
142          * hierarchy but before the layout pass.  
143          *  
144          * @param stub The ViewStub that initiated the inflation.  
145          * @param inflated The inflated View.  
146          */  
147         void onInflate(ViewStub stub, View inflated);  
148     }  
149   
150     /** @hide **/  
151     public class ViewReplaceRunnable implements Runnable {  
152         public final View view;  
153   
154         ViewReplaceRunnable(View view) {  
155             this.view = view;  
156         }  
157   
158         @Override  
159         public void run() {  
160             replaceSelfWithView(view, (ViewGroup) getParent());  
161         }  
162     }  
163 }  

这是什么玩应儿呢?其实就是一个轻量级的页面,我们通常使用它来做预加载处理,来改善页面加载速度和提高流畅性,ViewStub本身不会占用层级,它最终会被它指定的层级取代。 
在一些场合取代android:visibility=”gone”的用法,因为被gone掉的布局不断是会同时创建对象的。那为什么使用ViewStub就高效呢, 
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
setMeasuredDimension(0, 0); 
}

@Override 
public void draw(Canvas canvas) { 

由onMeasure()方法和draw()方法可以看出, ViewStub的初始宽高都是零,所以他开始不会占用空间,其次draw()方法也没有执行任何的绘制,由这两个方法就可以看出,ViewStub的确很高效。 
在代码中要操纵ViewStub的时候,要首先使用viewstub.inflate()方法,将其所拥有的View初始化进去。否则会报空指针错误。 
但ViewStub也不是万能的,下面总结下ViewStub能做的事儿和什么时候该用ViewStub,什么时候该用可见性的控制。

首先来说说ViewStub的一些特点:

1. ViewStub只能Inflate一次,之后ViewStub对象会被置为空。按句话说,某个被ViewStub指定的布局被Inflate后,就不会够再通过ViewStub来控制它了。
2. ViewStub只能用来Inflate一个布局文件,而不是某个具体的View,当然也可以把View写在某个布局文件中。

基于以上的特点,那么可以考虑使用ViewStub的情况有:
1. 在程序的运行期间,某个布局在Inflate后,就不会有变化,除非重新启动。
因为ViewStub只能Inflate一次,之后会被置空,所以无法指望后面接着使用ViewStub来控制布局。所以当需要在运行时不止一次的显示和隐藏某个布局,那么ViewStub是做不到的。这时就只能使用View的可见性来控制了。
2. 想要控制显示与隐藏的是一个布局文件,而非某个View。
因为设置给ViewStub的只能是某个布局文件的Id,所以无法让它来控制某个View。
所以,如果想要控制某个View(如Button或TextView)的显示与隐藏,或者想要在运行时不断的显示与隐藏某个布局或View,只能使用View的可见性来控制。

 

以上是关于布局优化之ViewStub源码分析的主要内容,如果未能解决你的问题,请参考以下文章

Android布局优化ViewStub源码分析

Android布局优化——ViewStub使用和源码梳理

Android之ViewStub源码简析

Android之ViewStub源码简析

[Android]Android布局优化之 ViewStub

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