<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                [TOC] 寫項目的過程中發現,在需要根據適當條件進行相應 UI 展示時,代碼中充斥了 setVisibility 相關的代碼,相當混亂。我們可以使用 ViewStub 來簡化相應的邏輯,并且 ViewStub 大小為 0,運行時才進行懶加載,所以性能上也有一定優勢。 # ViewStub是什么 1、ViewStub 是一個看不見的,沒有大小,不占布局位置的 View,可以用來懶加載布局。 2、當 ViewStub 變得可見或 inflate() 的時候,布局就會被加載(替換 ViewStub)。因此,ViewStub 一直存在于視圖層次結構中直到調用了 setVisibility() 或 inflate()。 3、在 ViewStub 加載完成后就會被移除,它所占用的空間就會被新的布局替換。 # 簡單使用 ViewStub 的使用很簡單,在布局文件中像引入其他控件一樣引入 ViewStub: ```xml <ViewStub android:id="@+id/stub" android:inflatedId="@+id/subTree" android:layout="@layout/mySubTree" android:layout_width="120dip" android:layout_height="40dip" /> ``` 需要展示 ViewStub 所引用的視圖時,通過 id 獲取到 ViewStub: ```java ViewStub stub = (ViewStub) findViewById(R.id.stub); View inflated = stub.inflate(); // stub.setVisibility(View.VISIBLE); ``` 除了調用 inflate 方法,還可以調用 setVisible 方法來展示 ViewStub 所引用的 View。下面,我們來看下 ViewStub 的源碼。 為統一理解,下面使用 `延遲加載 View` 來指代 ViewStub 所引用的 View。 # 源碼分析 ViewStub 這個類的代碼加上注釋也才三百多行,算是很簡單的源碼類了。先看下成員變量: ```java /** * 延遲加載 View 的 id */ private int mInflatedId; /** * 延遲加載 View 的布局資源 */ private int mLayoutResource; /** * 延遲加載 View 對象的弱引用 */ private WeakReference<View> mInflatedViewRef; /** * 布局加載器 */ private LayoutInflater mInflater; /** * 延遲加載 View 加載成功回調接口 */ private OnInflateListener mInflateListener; ``` 可以通過 ViewStub 的 setOnInflateListener(OnInflateListener inflateListener) 方法設定監聽器,在視圖加載成功后執行相應的操作,其中加載成功回調接口如下: ```java public static interface OnInflateListener { /** * @param stub 把加載延遲加載 View 的那個 ViewStub * @param inflated 加載出來的那個延遲加載 View */ void onInflate(ViewStub stub, View inflated); } ``` ## inflate 方法 通常調用 inflate 方法來延遲加載視圖,來看看其源碼: ```java public View inflate() { final ViewParent viewParent = getParent(); if (viewParent != null && viewParent instanceof ViewGroup) { if (mLayoutResource != 0) { final ViewGroup parent = (ViewGroup) viewParent; // 延遲加載 View inflate 出來 final View view = inflateViewNoAdd(parent); // 把 ViewStub 自身從父布局視圖樹中刪除,延遲加載 View 加入父布局視圖樹中 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"); } } ``` 可以看到,首先獲取到 ViewStub 的父布局并強轉為 ViewGroup,然后根據父布局的約束將延遲加載 View inflate 出來,接著把 ViewStub 自身從父布局視圖樹中刪除,延遲加載 View 加入父布局視圖樹中。 延遲加載 View 加載成功后,通過弱引用對象保存該視圖對象,并回調加載成功回調接口。至此,inflate 方法的工作也就完成了。 其中 inflateViewNoAdd 和 replaceSelfWithView 方法的源碼如下,都是很基礎的代碼: ```java 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); } } ``` ## setVisibility 方法 通過 ViewStub 的 inflate 方法,我們可以順利的把想要延遲加載的 View 加載出來了,然而實際的業務邏輯并不會這么簡單,有時可能加載出來后需要再次隱藏,隱藏后還要再次加載。或者當 inflate 方法多次調用時,就會報錯了: ```plain java.lang.IllegalStateException: ViewStub must have a non-null ViewGroup viewParent ``` 通過剛才的源碼可以看到,在調用 inflate 方法時,viewParent 為 null 了,為什么呢?原來我們在第一次調用 inflate 方法時,執行到 replaceSelfWithView 方法時,就把 ViewStub 從父布局中移除,然后把延遲加載 View 加入到父布局中了。所以現在再次調用 ViewStub 的 getParent 方法當然為 null 了。 那么延遲加載 View 是否可以多次顯示、隱藏呢,當然是可以的,接下來我們看看 ViewStub 的 setVisibility 方法。 ```java 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(); } } } ``` 邏輯很簡單:首先判斷延遲加載 View 的弱引用對象是否為空,不為空直接獲取該 View,并設置相應的可見性;弱引用對象為空的話,如果可見性設置為 VISIBLE 或 INVISIBLE 時,直接調用 inflate 方法進行加載。 需要注意兩點: * 代碼中不調用 inflate 方法,直接調用 setVisibility(View.INVISIBLE) 時,會將延遲加載 View 加載顯示出來的 * ViewStub 在調用一次 inflate 方法后,延遲加載 View 的弱引用對象就不為空了,除非被垃圾回收器掃描到進行回收了。 # 繪制流程方法 ```java public ViewStub(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { //... setVisibility(GONE); setWillNotDraw(true); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(0, 0); } @Override public void draw(Canvas canvas) { } @Override protected void dispatchDraw(Canvas canvas) { } ``` 可以看到,ViewStub在構造方法中調用了setWillNotDraw(true),會在后續過程中進行性能優化,略過繪制過程。在onMeasure方法中直接設置測量寬高為0,重寫了draw方法、dispatchDraw方法,方法內容為空。所以ViewStub 是一個看不見的,沒有大小,不占布局位置的 View。 # ViewStub 與 ListView 結合使用的問題 在 ListView 的 item 中使用 ViewStub 根據條件動態加載視圖,遇到視圖混亂問題,ListView 代碼片段如下: ```java public View getView(int position, View convertView, ViewGroup parent) { /* if (convertView == null) { ... } else { ... } ... */ holder.mViewStub.setOnInflateListener(new ViewStub.OnInflateListener() { @Override public void onInflate(ViewStub stub, View inflated) { inflated.findViewById(R.id.tv_unapply) .setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // unapply(mEntity.getJobId); } }); inflated.findViewById(R.id.tv_contact_company) .setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // contactCompany(); } }); } }); holder.mApplyViewStub.setVisibility(View.VISIBLE); } ``` 上面源碼分析時可知,ViewStub 的 setVisibility 方法在 mInflatedViewRef 不為空時,直接對延遲加載 View 進行可見性設定,并沒有執行 inflate 方法,所以我們上面設置的 onInflateListener 就沒有回調到了。 所以我們在 ViewHolder 中將 ViewStub 進行緩存后,getView 方法中再次取出時就不會調用 inflate 方法了,所以會出現 onInflateListener 方法回調異常。 # 總結 ViewStub 源碼的幾個關鍵成員變量和方法以及介紹完了。可以看到,ViewStub 的延遲加載特性,在提升視圖性能的同時,還可以使業務邏輯更清晰,大量減少 View 的 setVisibility 相關代碼。 使用時,只需要一次性加載的可以直接調用 inflate 方法進行加載。需要多次調用修改可見性的,可以調用 ViewStub 的 setVisibility 方法,同時可以通過設置監聽器在加載完成后執行相應的操作。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看