布局优化之ViewStub源码分析
程序员文章站
2022-04-11 08:40:09
源码分析 这是什么玩应儿呢?其实就是一个轻量级的页面,我们通常使用它来做预加载处理,来改善页面加载速度和提高流畅性,ViewStub本身不会占用层级,它最终会被它指定的层级取代。 在一些场合取代android:visibility=”gone”的用法,因为被gone掉的布局不断是会同时创建对象的。那 ......
源码分析
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的可见性来控制。
上一篇: iOS 设置View阴影
下一篇: Python作用域疑问记录