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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android --- View.inflate()的详细介绍

發布時間:2025/3/21 Android 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android --- View.inflate()的详细介绍 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

誤用 LayoutInflater 的 inflate() 方法已經不是什么稀罕事兒了……

做 Android 開發做久了,一定會或多或少地對布局的渲染有一些懵逼:

1.View.inflate() 和 LayoutInflator.from().inflate() 有啥區別? 2.調用 inflate() 方法的時候有時候傳 null,有時候傳 parent 是為啥? 3.用 LayoutInflater 有時候還可能傳個 attachToRoot ,這又是個啥?

接下來我們就從源碼的角度來尋找一下這幾個問題的答案,后面再用幾個示例來驗證我們的猜想。

話不多說,Let’s go !

基本介紹

先來看一下這個方法具體做了什么:

/*** Inflate a view from an XML resource. This convenience method wraps the {@link* LayoutInflater} class, which provides a full range of options for view inflation.*/ public static View inflate(Context context, int resource, ViewGroup root) {LayoutInflater factory = LayoutInflater.from(context);return factory.inflate(resource, root); }

當我們查看源碼,就會發現,這個方法的內部實際上就是調用了 LayoutInflater 的 inflate 方法。正如此方法的注釋所言,這是一個方便開發者調用的 LayoutInflater 的包裝方法,而 LayoutInflater 本身則為 View 的渲染提供了更多的選擇。

那么我們現在的問題就變成了, LayoutInflater 又做了什么?

繼續追蹤代碼,我們會發現, LayoutInflator.from().inflate() 是這個樣子的:

// LayoutInflator#inflate(int, ViewGroup) public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {return inflate(resource, root, root != null); }

啥?重載?

// LayoutInflator#inflate(int, ViewGroup, boolean) public View inflate(int resource, ViewGroup root, boolean attachToRoot) {final Resources res = getContext().getResources();final XmlResourceParser parser = res.getLayout(resource);try {return inflate(parser, root, attachToRoot);} finally {parser.close();} }

這里我們看到,通過層層調用,最終會調用到 LayoutInflator#inflate(int, ViewGroup, boolean) 方法,很明顯,這個方法會將我們傳入的布局 id 轉換為 XmlResourceParser,然后進行另一次,也是最后一次重載。

這個方法就厲害了,這里基本上包括了我們所有問題的答案,我們繼續往下看。

源碼分析

話不多說,上代碼。接下來我們來逐段分析下這個 inflate 方法:

public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {final Context inflaterContext = mContext;final AttributeSet attrs = Xml.asAttributeSet(parser);// 默認返回結果為傳入的根布局View result = root;// 通過 createViewFromTag() 方法找到傳入的 layoutId 的根布局,并賦值給 tempfinal View temp = createViewFromTag(root, name, inflaterContext, attrs);ViewGroup.LayoutParams params = null;// 如果傳入的父布局不為空if (root != null) {// 為這個 root 生成一套合適的 LayoutParamsparams = root.generateLayoutParams(attrs);if (!attachToRoot) {// 如果沒有 attachToRoot,那為根布局設置 layoutparamstemp.setLayoutParams(params);}}// 如果傳入的父布局不為空,且想要 attachToRootif (root != null && attachToRoot) {// 那就將傳入的布局以及 layoutparams 通過 addView 方法添加到父布局中 root.addView(temp, params);}// 如果傳入的根布局為空,或者不想 attachToRoot,則返回要加載的 layoutIdif (root == null || !attachToRoot) {result = temp;}return result; }

代碼也分析完了,我再來總結一下:

  • View#inflate 只是個簡易的包裝方法,實際上還是調用的 LayoutInflater#inflate ;
  • LayoutInflater#inflate 由于可以自己選擇 root 和 attachToRoot
    的搭配(后面有解釋),使用起來更加靈活;
  • 實際上的區別只是在于 root 是否傳空,以及 attachToRoot 真假與否;
  • 當 root 傳空時,會直接返回要加載的 layoutId,返回的 View 沒有父布局且沒有 LayoutParams;
  • 當 root 不傳空時,又分為 attachToRoot 為真或者為假:
    • attachToRoot = true 會為傳入的 layoutId 直接設置參數,并將其添加到 root 中,然后將傳入的 root
      返回;
    • attachToRoot = false 會為傳入的 layoutId 設置參數,但是不會添加到 root ,然后返回 layoutId
      對應的 View;

這里需要注意的是,雖然不馬上將 View 添加到 parent 中,但是這里最好也傳上 parent,而不是粗暴的傳入 null;因為子
View 的 LayoutParams 需要由 parent 來確定;否則會在手動 addView 時調用
generateDefaultLayoutParams() 為子 View 生成一個寬高都為包裹內容的
LayoutParams,而這并不一定是我們想要的。

測試 & 檢驗

單說起來可能有些抽象,下面使用代碼來進行具體的測試與檢驗。

View.inflate(context, layoutId, null)

如之前所說,這實際上調用的是 getLayoutInflater().inflate(layoutId, null) ,結合之前的源碼來看:

public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {View result = root;final View temp = createViewFromTag(root, name, inflaterContext, attrs);if (root == null || !attachToRoot) {result = temp;}return result; }

很明顯,傳入的 root 為空,則會直接將加載好的 xml 布局返回,而這種情況下返回的這個 View 沒有參數,也沒有父布局。

protected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.layout_test);View inflateView = View.inflate(this, R.layout.layout_basic_use_item, null);Log.e("Test", "LayoutParams -> " + inflateView.getLayoutParams());Log.e("Test", "Parent -> " + inflateView.getParent()); }


如圖所示,正如我們想的,root 傳 null 時,參數以及父布局返回結果均為 null。

View.inflate(context, layoutId, mParent)
按之前分析過的,此方法實際調用的是 getLayoutInflater().inflate(layoutId, root, true) ,再來看源碼:

public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {final Context inflaterContext = mContext;final AttributeSet attrs = Xml.asAttributeSet(parser);View result = root; final View temp = createViewFromTag(root, name, inflaterContext, attrs);ViewGroup.LayoutParams params = null;if (root != null) {params = root.generateLayoutParams(attrs);}if (root != null && attachToRoot) {root.addView(temp, params);}return result; }

如源碼所示,返回的 result 會在最開始就被賦值為入參的 root,root 不為空,同時 attachToRoot 為 true,就會將加載好的布局直接通過 addView 方法添加到 root 布局中,然后將 root 返回。

protected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.layout_test);LinearLayout mParent = findViewById(R.id.ll_root);View inflateView = View.inflate(this, R.layout.layout_basic_use_item, mParent);Log.e("Test", "LayoutParams -> " + inflateView.getLayoutParams());Log.e("Test", "Parent -> " + inflateView.getParent());Log.e("Test", "inflateView -> " + inflateView); }


如圖示,返回的 View 正是我們傳入的 mParent,對應的 id 是 ll_root,參數也不再為空。

getLayoutInflater().inflate(layoutId, root, false)

也許會有人問了,現在要么是 root 傳空,返回 layoutId 對應的布局;要么是 root 不傳空,返回傳入的 root 布局。那我要是想 root 不傳空,但是還是返回 layoutId 對應的布局呢?

這就是 View#inflate 的局限了,由于它是包裝方法,因此 attachToRoot 并不能因需定制。這時候我們完全可以自己調用 getLayoutInflater().inflate(layoutId, root, false) 方法,手動的將第三個參數傳為 false,同時為這個方法傳入目標根布局。這樣,我們就可以得到一個有 LayoutParams,但是沒有 parentView 的 layoutId 布局了。

protected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.layout_test);LinearLayout mParent = findViewById(R.id.ll_root);View inflateView = getLayoutInflater().inflate(R.layout.main, mParent, false);Log.e("Test", "LayoutParams -> " + inflateView.getLayoutParams());Log.e("Test", "Parent -> " + inflateView.getParent()); }

與我們分析的一致,有參數,但是沒有父布局,且返回的就是我們加載的布局 id。我們在之后可以通過 addView 方法手動將這個布局加入父布局中。

這里還有個要注意的點,那就是 params = root.generateLayoutParams(attrs); 這句代碼,我們會發現,為 layoutId 設置的 params 參數,實際上是通過 root 來生成的。這也就告訴我們,雖然不馬上添加到 parent 中,但是這里最好也傳上 parent,而不是粗暴的傳入 null,因為子 View 的 LayoutParams 需要由 parent 來確定;當然,傳入 null 也不會有問題,因為在執行 addView() 方法的時候,如果當前 childView 沒有參數,會調用 generateDefaultLayoutParams() 生成一個寬高都包裹的 LayoutParams 賦值給 childView,而這并不一定是我們想要的。

attachToRoot 必須為 false!

代碼寫多了,大家有時候會發現這個 attachToRoot 也不是想怎樣就怎樣的,有時候它還就必須是 false,不能為 true。下面我們就來看看這些情況。

  • RecylerView#onCreateViewHolder()

在為 RecyclerView 創建 ViewHolder 時,由于 View 復用的問題,是 RecyclerView 來決定什么時候展示它的子View,這個完全不由我們決定,這種情況下,attachToRoot 必須為 false:

public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { LayoutInflater inflater = LayoutInflater.from(getActivity()); View view = inflater.inflate(R.layout.item, parent, false); return new ViewHolder(view); }
  • Fragment#onCreateView()

由于 Fragment 需要依賴于 Activity 展示,一般在 Activity 中也會有容器布局來盛放 Fragment:

Fragment fragment = new Fragment(); getSupportFragmentManager().beginTransaction().add(R.id.root_container, fragment).commit();

上述代碼中的 R.id.root_container 便為容器,這個 View 會作為參數傳遞給 Fragment#onCreateView() :

public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {return inflater.inflate(R.layout.fragment_layout, parentViewGroup, false); }

它也是你在 inflate() 方法中傳入的 ViewGroup,FragmentManager 會將 Fragment 的 View 添加到 ViewGroup 中,言外之意就是,Fragment 對應的布局展示或者說添加進 ViewGroup 時也不是我們來控制的,而是 FragmentManager 來控制的。

總結一下就是,當我們不為子 View 的展示負責時,attachToRoot 必須為 false;否則就會出現對應的負責人,比如上面說的 Rv 或者 FragmentManager,已經把布局 id 添加到 ViewGroup 了,我們還繼續設置 attachToRoot 為 true,想要手動 addView,那必然會發生 child already has parent 的錯誤。

總結

以上是生活随笔為你收集整理的Android --- View.inflate()的详细介绍的全部內容,希望文章能夠幫你解決所遇到的問題。

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

主站蜘蛛池模板: 三级视频网站在线观看 | 国产 欧美 自拍 | av日韩一区二区三区 | 五月婷婷综合久久 | 国产精品一区二区三区四区视频 | 黄页网站免费观看 | 在线看片中文字幕 | 日本色呦呦 | 一区二区视频在线观看 | 有码一区二区 | 欧美黄一级| 91av短视频| 久久最新 | 99re视频这里只有精品 | 91超薄肉色丝袜交足高跟凉鞋 | 很色的网站 | 夜夜爽夜夜操 | av网站免费在线观看 | 高清视频在线免费观看 | 在线a天堂| www色com | a视频网站 | 亚洲自拍激情 | 秋霞在线视频 | 亚洲精品污一区二区三区 | 少妇激情偷人三级 | 国产青青草视频 | 精品国产网站 | а√天堂8资源在线官网 | 精品久草 | 天堂va欧美ⅴa亚洲va一国产 | 日韩毛片在线视频 | 97精品久久| 国产精品久久久久久久久久久久久久久久久 | 女人脱下裤子让男人捅 | 一级片黑人 | 国产免费一级片 | 日韩有码一区二区三区 | 国产视频日韩 | 五月天狠狠操 | 成年人免费视频播放 | 三级网站免费看 | 天天操天天干天天爱 | 天天色综合av | 好吊色欧美一区二区三区视频 | 麻豆免费在线视频 | 国产午夜av | 日本在线免费观看视频 | 制服丝袜先锋 | 日日干天天| 伊人久久五月 | 黑料视频在线观看 | 亚洲AV无码一区二区三区蜜桃 | 黄色不卡视频 | 亚洲国产区 | 开心春色激情网 | 91成人免费在线观看视频 | 欧美v视频 | 亚洲自拍网站 | 污污污污污污www网站免费 | 放几个免费的毛片出来看 | 日韩区在线观看 | 艳妇臀荡乳欲伦交换电影 | 综合久久99| 日韩精品视频免费看 | 椎名由奈av一区二区三区 | 国产老妇视频 | 免费在线黄网站 | 欧洲久久久久 | 99热9| 成人免费看片' | 国产又黄又爽又色 | 污污污www精品国产网站 | 肥老熟妇伦子伦456视频 | 国产123区在线观看 91国产一区二区 | 伊人中文字幕在线 | caoprom在线 | 苍井空张开腿实干12次 | 涩涩屋污 | 午夜在线观看视频 | 国产有码视频 | 一区二区在线视频 | 九九久视频 | 精品人妻久久久久久888不卡 | 亚洲精品久久久久久宅男 | 在线中文字幕日韩 | 东北少妇露脸无套对白 | 日本少妇高潮 | 嫩草影院永久入口 | 国产精品一区二区免费在线观看 | 精品伦精品一区二区三区视频 | 日韩精品无码一区二区三区 | 激情综合五月 | 91视频观看 | 男女黄色又爽大片 | 国产色在线观看 | 伊人成人动漫 | 精品一区二三区 | 欧美xxxx非洲 |