日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

ViewStub你肯定听过,但是这些细节了解吗?

發布時間:2023/12/20 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ViewStub你肯定听过,但是这些细节了解吗? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1什么是ViewStub

1. ViewStub 是一個看不見的,沒有大小,不占布局位置的 View,可以用來懶加載布局。

2. 當 ViewStub 變得可見或 inflate() 的時候,布局就會被加載(替換 ViewStub)。因此,ViewStub 一直存在于視圖層次結構中直到調用了 setVisibility(int) 或 inflate()。

3. 在?ViewStub 加載完成后就會被移除,它所占用的空間就會被新的布局替換。

在這分享一份整理了2個月的Android進階面試解析筆記文檔,包括了知識點筆記和高頻面試問題解析及部分知識點視頻講解給大家!為了不影響閱讀,在這以圖片展示部分內容于目錄截圖,有需要的朋友麻煩點贊后點擊下面在線鏈接獲取免費領取方式吧!
阿里P6P7【安卓】進階資料分享+加薪跳槽必備面試題

2ViewStub構造方法

先來看看構造方法:

public ViewStub(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {super(context);final TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.ViewStub, defStyleAttr, defStyleRes);// 要被加載的布局 IdmInflatedId = a.getResourceId(R.styleable.ViewStub_inflatedId, NO_ID);// 要被加載的布局mLayoutResource = a.getResourceId(R.styleable.ViewStub_layout, 0);// ViewStub 的 IdmID = a.getResourceId(R.styleable.ViewStub_id, NO_ID);a.recycle();// 初始狀態為 GONEsetVisibility(GONE);// 設置為不會繪制setWillNotDraw(true); }

接下來就看看關鍵的方法,然后看看初始化狀態setVisibility方法。

// 復寫了 setVisibility(int) 方法 @Override @android.view.RemotableViewMethod public void setVisibility(int visibility) {// private WeakReference<View> mInflatedViewRef;// mInflatedViewRef 是對布局的弱引用if (mInflatedViewRef != null) {// 如果不為 null,就拿到懶加載的 ViewView view = mInflatedViewRef.get();if (view != null) {// 然后就直接對 View 進行 setVisibility 操作view.setVisibility(visibility);} else {// 如果為 null,就拋出異常throw new IllegalStateException("setVisibility called on un-referenced view");}} else {super.setVisibility(visibility);// 之前說過,setVisibility(int) 也可以進行加載布局if (visibility == VISIBLE || visibility == INVISIBLE) {// 因為在這里調用了 inflate()inflate();}} }

3inflate()方法解析

核心來了,平時用的時候,會經常調用到該方法。inflate() 是關鍵的加載實現,代碼如下所示:

public View inflate() {// 獲取父視圖final ViewParent viewParent = getParent();if (viewParent != null && viewParent instanceof ViewGroup) {// 如果沒有指定布局,就會拋出異常if (mLayoutResource != 0) {// viewParent 需為 ViewGroupfinal ViewGroup parent = (ViewGroup) viewParent;final LayoutInflater factory;if (mInflater != null) {factory = mInflater;} else {// 如果沒有指定 LayoutInflaterfactory = LayoutInflater.from(mContext);}// 獲取布局final View view = factory.inflate(mLayoutResource, parent,false);// 為 view 設置 Idif (mInflatedId != NO_ID) {view.setId(mInflatedId);}// 計算出 ViewStub 在 parent 中的位置final int index = parent.indexOfChild(this);// 把 ViewStub 從 parent 中移除parent.removeViewInLayout(this);// 接下來就是把 view 加到 parent 的 index 位置中final ViewGroup.LayoutParams layoutParams = getLayoutParams();if (layoutParams != null) {// 如果 ViewStub 的 layoutParams 不為空// 就設置給 viewparent.addView(view, index, layoutParams);} else {parent.addView(view, index);}// mInflatedViewRef 就是在這里對 view 進行了弱引用mInflatedViewRef = new WeakReference<View>(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");} }

Inflate使用特點

1. ViewStub只能被Inflate一次,inflate之后ViewStub對象就會被置為空。即某個被ViewStub指定的布局被Inflate后,就不能夠再通過ViewStub來控制它了。

2. ViewStub只能用來Inflate一個布局文件,而不是某個具體的View,當然也可以把View寫在某個布局文件中。

4WeakReference使用

使用了弱引用管理對象的創建,代碼如下所示

在這里使用了get方法

@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 {} }

在這里創建了弱引用對象

public View inflate() {final ViewParent viewParent = getParent();if (viewParent != null && viewParent instanceof ViewGroup) {if (mLayoutResource != 0) {mInflatedViewRef = new WeakReference<>(view);return view;} else {throw new IllegalArgumentException("ViewStub must have a valid layoutResource");}} }

5ViewStub為何無大小

首先先看一段源碼,如下所示:

@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {setMeasuredDimension(0, 0); }@Override public void draw(Canvas canvas) { }@Override protected void dispatchDraw(Canvas canvas) { }

有沒有覺得很與眾不同

draw和dispatchDraw雖然重寫了,但是看代碼卻都是什么也不做!并且onMeasure還什么也不做,直接setMeasuredDimension(0,0);來把view區域設置位0,原來一個ViewStub雖然是一個view,卻是一個沒有任何顯示內容,也不顯示任何內容的特殊view,并且對layout在加載時候不可見的。

6ViewStub為何不繪制

具體看一下setWillNotDraw(true)方法,代碼如下:

public void setWillNotDraw(boolean willNotDraw) {setFlags(willNotDraw ? WILL_NOT_DRAW : 0, DRAW_MASK); }

View中,對于WILL_NOT_DRAW是這樣定義的:

/*** This view won't draw. {@link #onDraw(android.graphics.Canvas)} won't be* called and further optimizations will be performed. It is okay to have* this flag set and a background. Use with DRAW_MASK when calling setFlags.* {@hide}*/ static final int WILL_NOT_DRAW = 0x00000080;

設置WILL_NOT_DRAW之后,onDraw()不會被調用,通過略過繪制的過程,優化了性能。在ViewGroup中,初始化時設置了WILL_NOT_DRAW,代碼如下:

public ViewGroup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {super(context, attrs, defStyleAttr, defStyleRes);initViewGroup();initFromAttributes(context, attrs, defStyleAttr, defStyleRes); }private void initViewGroup() {// ViewGroup doesn't draw by defaultif (!debugDraw()) {setFlags(WILL_NOT_DRAW, DRAW_MASK);}mGroupFlags |= FLAG_CLIP_CHILDREN;mGroupFlags |= FLAG_CLIP_TO_PADDING;mGroupFlags |= FLAG_ANIMATION_DONE;mGroupFlags |= FLAG_ANIMATION_CACHE;mGroupFlags |= FLAG_ALWAYS_DRAWN_WITH_CACHE;if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB) {mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS;}setDescendantFocusability(FOCUS_BEFORE_DESCENDANTS);mChildren = new View[ARRAY_INITIAL_CAPACITY];mChildrenCount = 0;mPersistentDrawingCache = PERSISTENT_SCROLLING_CACHE; }

所以,在寫自定義布局時,如果需要調用onDraw()進行繪制,則需要在初始化時候,調用setWillNotDraw(false)。若是想要更進一步閱讀View中WILL_NOT_DRAW的相關源碼,可以去看下PFLAG_SKIP_DRAW相關的代碼。

7可以多次inflate()嗎

ViewStub對象只可以Inflate一次,之后ViewStub對象會被置為空。同時需要注意的問題是,inflate一個ViewStub對象之后,就不能再inflate它了,否則會報錯:ViewStub must have a non-null ViewGroup viewParent。

其實看一下源碼就很好理解:

public View inflate() {//獲取viewStub的父容器對象final ViewParent viewParent = getParent();if (viewParent != null && viewParent instanceof ViewGroup) {if (mLayoutResource != 0) {final ViewGroup parent = (ViewGroup) viewParent;//這里是加載布局,并且給它設置id//布局的加載是通過LayoutInflater解析出來的final View view = inflateViewNoAdd(parent);//這行代碼很重要,下面會將到replaceSelfWithView(view, parent);//使用弱引用mInflatedViewRef = new WeakReference<>(view);if (mInflateListener != null) {mInflateListener.onInflate(this, view);}return view;} else {//如果已經加載出來,再次inflate就會拋出異常呢throw new IllegalArgumentException("ViewStub must have a valid layoutResource");}} else {throw new IllegalStateException("ViewStub must have a non-null ViewGroup viewParent");} }

其實也可以用一張圖來理解它,如下所示,摘自網絡

也就是說,一旦調用inflate上面的方法后ViewStub就會變成null了,因此使用該對象特別需要注意空指針問題。

8ViewStub不支持merge

不能引入包含merge標簽的布局到ViewStub中。否則會報錯:

android.view.InflateException: Binary XML file line #1:? can be used only with a valid ViewGroup root and attachToRoot=true

9ViewStub使用場景

一般的app中大多有這么一個功能,當加載的數據為空時顯示一個數據為空的視圖、在數據加載失敗時顯示加載失敗對應的UI,當沒有網絡的時候加載沒有網絡的UI,并支持點擊重試會比白屏的用戶體驗更好一些。

俗稱,頁面狀態切換管理……一般來說,加載中、加載失敗、空數據等狀態的UI風格,在App內的所有頁面中需要保持一致,也就是需要做到全局統一,也支持局部定制。

ViewStub的優勢在于在上面的場景中,并不一定需要把所有的內容都展示出來,可以隱藏一些View視圖,待用戶需要展示的時候再加載到當前的Layout中,這個時候就可以用到ViewStub這個控件了,這樣可以減少資源的消耗,使最初的加載速度變快。

那么就有了之前開發使用的狀態管理器開源庫,就是采用了ViewStub這個控件,讓View狀態的切換和Activity徹底分離開。用builder模式來自由的添加需要的狀態View,可以設置有數據,數據為空,加載數據錯誤,網絡錯誤,加載中等多種狀態,并且支持自定義狀態的布局。可以說完全不影響性能……

10ViewStub總結分析

分析源碼的原理,不管認識到哪一步,最終的目標還是在運用上,即把看源碼獲得的知識用到實際開發中,

總結

以上是生活随笔為你收集整理的ViewStub你肯定听过,但是这些细节了解吗?的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。