ViewStub详解
生活随笔
收集整理的這篇文章主要介紹了
ViewStub详解
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
源碼:
package android.view;import android.annotation.IdRes; import android.annotation.LayoutRes; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.util.AttributeSet; import android.widget.RemoteViews.RemoteView;import com.android.internal.R;import java.lang.ref.WeakReference;/*** ViewStub是一個用戶不可見的,大小為0的view,用于在app運行時懶加載一些布局。*當ViewStub設置用戶可見(setVisibility)時,或者當調用inflate()方法時,布局會就被初始化。ViewStub會用初始化后的布局文件替換自己放入其父布局中(參考inflate中的replaceSelfWithView方法)。* * 因此ViewStub會在view布局中存在一直到setVisibility或inflate被調用了。* * 當被初始化的View添加到ViewStub的父布局時會使用ViewStub的布局屬性,因此你可以使用ViewStub的inflatedId屬性定義/重寫View的id。例如:* * * <ViewStub android:id="@+id/stub"* android:inflatedId="@+id/subTree"* android:layout="@layout/mySubTree"* android:layout_width="120dip"* android:layout_height="40dip" />* * * 上面例子里邊定義了一個id為stub的ViewStub。在初始化布局subTree之后,這個ViewStub就會從它父布局中移除。新創建的mySubTree布局的View的id就會使用布局中定義的subTree,最后這個新創建的view的寬為120dip,高為40dip.* * * 如下方式是最標準的初始化布局View的方式:*** ViewStub stub = findViewById(R.id.stub);* View inflated = stub.inflate();** * 當inflate()方法被調用后,ViewStub會被替換為初始化后的布局View,并且View會被返回。從而讓app獲取到view的引用而不用在調用一次findViewById().** @attr ref android.R.styleable#ViewStub_inflatedId* @attr ref android.R.styleable#ViewStub_layout*/ @RemoteView public final class ViewStub extends View {private int mInflatedId;private int mLayoutResource;private WeakReference<View> mInflatedViewRef;private LayoutInflater mInflater;private OnInflateListener mInflateListener;public ViewStub(Context context) {this(context, 0);}/*** 通過傳入布局id創建ViewStub(猜測是用于代碼中動態初始化時調用的)。** @param context The application's environment.* @param layoutResource The reference to a layout resource that will be inflated.*/public ViewStub(Context context, @LayoutRes int layoutResource) {this(context, null);mLayoutResource = layoutResource;}public ViewStub(Context context, AttributeSet attrs) {this(context, attrs, 0);}public ViewStub(Context context, AttributeSet attrs, int defStyleAttr) {this(context, attrs, defStyleAttr, 0);}/*** 解析布局文件創建ViewStub,并獲取mInflatedId及mLayoutResource等屬性。最后設置不可見及不繪制自身。* */public ViewStub(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {super(context);final TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.ViewStub, defStyleAttr, defStyleRes);saveAttributeDataForStyleable(context, R.styleable.ViewStub, attrs, a, defStyleAttr,defStyleRes);mInflatedId = a.getResourceId(R.styleable.ViewStub_inflatedId, NO_ID);mLayoutResource = a.getResourceId(R.styleable.ViewStub_layout, 0);mID = a.getResourceId(R.styleable.ViewStub_id, NO_ID);a.recycle();setVisibility(GONE);setWillNotDraw(true);}/*** Returns the id taken by the inflated view. If the inflated id is* {@link View#NO_ID}, the inflated view keeps its original id.** @return A positive integer used to identify the inflated view or* {@link #NO_ID} if the inflated view should keep its id.** @see #setInflatedId(int)* @attr ref android.R.styleable#ViewStub_inflatedId*/@IdRespublic int getInflatedId() {return mInflatedId;}/*** Defines the id taken by the inflated view. If the inflated id is* {@link View#NO_ID}, the inflated view keeps its original id.** @param inflatedId A positive integer used to identify the inflated view or* {@link #NO_ID} if the inflated view should keep its id.** @see #getInflatedId()* @attr ref android.R.styleable#ViewStub_inflatedId*/@android.view.RemotableViewMethod(asyncImpl = "setInflatedIdAsync")public void setInflatedId(@IdRes int inflatedId) {mInflatedId = inflatedId;}/** @hide **/public Runnable setInflatedIdAsync(@IdRes int inflatedId) {mInflatedId = inflatedId;return null;}/*** Returns the layout resource that will be used by {@link #setVisibility(int)} or* {@link #inflate()} to replace this StubbedView* in its parent by another view.** @return The layout resource identifier used to inflate the new View.** @see #setLayoutResource(int)* @see #setVisibility(int)* @see #inflate()* @attr ref android.R.styleable#ViewStub_layout*/@LayoutRespublic int getLayoutResource() {return mLayoutResource;}/*** Specifies the layout resource to inflate when this StubbedView becomes visible or invisible* or when {@link #inflate()} is invoked. The View created by inflating the layout resource is* used to replace this StubbedView in its parent.* * @param layoutResource A valid layout resource identifier (different from 0.)* * @see #getLayoutResource()* @see #setVisibility(int)* @see #inflate()* @attr ref android.R.styleable#ViewStub_layout*/@android.view.RemotableViewMethod(asyncImpl = "setLayoutResourceAsync")public void setLayoutResource(@LayoutRes int layoutResource) {mLayoutResource = layoutResource;}/** @hide **/public Runnable setLayoutResourceAsync(@LayoutRes int layoutResource) {mLayoutResource = layoutResource;return null;}/*** Set {@link LayoutInflater} to use in {@link #inflate()}, or {@code null}* to use the default.*/public void setLayoutInflater(LayoutInflater inflater) {mInflater = inflater;}/*** Get current {@link LayoutInflater} used in {@link #inflate()}.*/public LayoutInflater getLayoutInflater() {return mInflater;}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {setMeasuredDimension(0, 0);}/*** 不做任何繪制,所以比普通view節省了時間與內存。*/@Overridepublic void draw(Canvas canvas) {}@Overrideprotected void dispatchDraw(Canvas canvas) {}/*** 當設置visibility為VISIBLE或INVISIBLE時,inflate方法就會被調用* ,且初始化出來的view對象會替換StubView在其父布局中的位置。** @param visibility One of {@link #VISIBLE}, {@link #INVISIBLE}, or {@link #GONE}.** @see #inflate() */@Override@android.view.RemotableViewMethod(asyncImpl = "setVisibilityAsync")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();}}}/** @hide **/public Runnable setVisibilityAsync(int visibility) {if (visibility == VISIBLE || visibility == INVISIBLE) {ViewGroup parent = (ViewGroup) getParent();return new ViewReplaceRunnable(inflateViewNoAdd(parent));} else {return null;}}private View inflateViewNoAdd(ViewGroup parent) {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);}return view;}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);}}/*** 初始化布局view,替換stub在父布局中的位置(注:stub被替換之后getparent會為空,因此會拋出IllegalArgumentException異常)。* ** @return The inflated layout resource.**/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");}}/*** Specifies the inflate listener to be notified after this ViewStub successfully* inflated its layout resource.** @param inflateListener The OnInflateListener to notify of successful inflation.** @see android.view.ViewStub.OnInflateListener*/public void setOnInflateListener(OnInflateListener inflateListener) {mInflateListener = inflateListener;}/*** 接收布局view被創建的觀察者,如果設置了會在布局view被初始化之后回調 listener的onInflate方法。* ** @see android.view.ViewStub#setOnInflateListener(android.view.ViewStub.OnInflateListener) */public static interface OnInflateListener {/*** Invoked after a ViewStub successfully inflated its layout resource.* This method is invoked after the inflated view was added to the* hierarchy but before the layout pass.** @param stub The ViewStub that initiated the inflation.* @param inflated The inflated View.*/void onInflate(ViewStub stub, View inflated);}/** @hide **/public class ViewReplaceRunnable implements Runnable {public final View view;ViewReplaceRunnable(View view) {this.view = view;}@Overridepublic void run() {replaceSelfWithView(view, (ViewGroup) getParent());}} }使用方法:
1.在布局中定義ViewStub:
2.布局中使用:
ViewStub stub = findViewById(R.id.stub);//inflated就是初始化后的布局view對象View inflated = stub.inflate();//對inflated操作,設置文字,圖片等。
拋磚引玉,不當之處敬請之處,萬分感謝🙏。
注:其實也可以也可以用動態添加的方法添加View:在java/kotlin代碼中動態初始化View,然后添加到對應的viewgroup中。
1.在需要的時候通過layoutInflater.inflate(mLayoutResource, parent, false);
2.方法初始化要添加的view,然后調用對應viewGroup.addView(View child/View child, int index/View child, LayoutParams params);方法添加到布局中;
這樣的話在布局中就不用多了個ViewStub的view了,性能也更好些。
總結
以上是生活随笔為你收集整理的ViewStub详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 课程作业一
- 下一篇: DDD领域模型自动生成?