国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統(tǒng) > Android > 正文

Android中使用LayoutInflater要注意的一些坑

2019-12-12 02:59:57
字體:
供稿:網(wǎng)友

前言

在平時的開發(fā)過程中,我們經(jīng)常會用LayoutInflater這個類,比如說在Fragment$onCreateViewRecyclerView.Adapter$onCreateViewHolder中都會用到。它的用法也無非就是LayoutInflater.inflate(resourceId, root, attachToRoot),第一個參數(shù)沒什么好說的,但第二個和第三個參數(shù)結(jié)合起來會帶來一定的迷惑性。之前有時候會發(fā)現(xiàn)界面布局上出了一些問題,查了很久之后偶然的改動了這兩個參數(shù),發(fā)現(xiàn)問題解決了,然后也就過去了,并沒有去思考這是為什么,然后下次可能又重復(fù)這種困境了。

所以想在這里總結(jié)一下,避免以后繼續(xù)掉坑。

先來看看inflate方法的注釋:

/** * Inflate a new view hierarchy from the specified xml resource. Throws * {@link InflateException} if there is an error. *  * @param resource ID for an XML layout resource to load (e.g., *  <code>R.layout.main_page</code>) * @param root Optional view to be the parent of the generated hierarchy (if *  <em>attachToRoot</em> is true), or else simply an object that *  provides a set of LayoutParams values for root of the returned *  hierarchy (if <em>attachToRoot</em> is false.) * @param attachToRoot Whether the inflated hierarchy should be attached to *  the root parameter? If false, root is only used to create the *  correct subclass of LayoutParams for the root view in the XML. * @return The root View of the inflated hierarchy. If root was supplied and *   attachToRoot is true, this is root; otherwise it is the root of *   the inflated XML file. */public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot)

首先需要了解的一點(diǎn)是,一個View的測量結(jié)果并不只是由它自己的layout_width和layout_height(即LayoutParams)所決定的,而是由父容器給它的約束(MeasureSpec)和它自身的LayoutParams共同決定的。

達(dá)成這個共識之后,我們再來看看它的參數(shù)。

  • root:給布局文件提供一個父容器。布局文件里面總有一個元素是沒有父容器的(沒錯,就是根元素),所以需要給它提供一個父容器來幫助它完成測量工作。如果root為空的話,就會導(dǎo)致根元素中的layout_xxx全部失效,從而影響到整個布局。同時,如果root為空的話,那么attachToRoot也就沒有意義了。
  • attachToRoot: 如果為true,創(chuàng)建出來的布局系統(tǒng)會幫我們添加到父容器中去。為false的話,就只是給它提供約束,好讓這個布局順利完成測量等工作而已,將布局添加到父容器中去需要我們后續(xù)根據(jù)需要去手動調(diào)用addView方法。
  • 返回值:如果root != null && attachToRoot,返回的View就是傳進(jìn)來的root,否則返回由布局文件所創(chuàng)建的View對象。

用幾個例子來說明一下會比較好理解。Activity的布局是一個LinearLayout,要添加的布局如下:

<?xml version="1.0" encoding="utf-8"?><TextView xmlns:android="http://schemas.android.com/apk/res/android"  android:id="@+id/item_text"  android:layout_width="200dp"  android:layout_height="50dp"  android:layout_marginLeft="16dp"  android:layout_marginTop="16dp"  android:background="@color/colorAccent"  android:gravity="center"  android:text="item: text"/>

正常的情況

// 第一種方法View inflatedView = LayoutInflater.from(this).inflate(R.layout.item_text, mLinearLayout, true);Log.d(TAG, "inflated view is " + inflatedView);// 第二種方法View inflatedView = LayoutInflater.from(this).inflate(R.layout.item_text, mLinearLayout, false);Log.d(TAG, "inflated view is " + inflatedView);mLinearLayout.addView(inflatedView);

視覺上的結(jié)果都是一樣的


但是Log就有一點(diǎn)不一樣了,這就是attachToRoot不同的值所導(dǎo)致的。

第一種方法的Log

D/MainActivity: inflated view is android.widget.LinearLayout{36e9aac V.E...... ......I. 0,0-0,0 #7f0c0051 app:id/linear}

第二種方法的Log

D/MainActivity: inflated view is android.support.v7.widget.AppCompatTextView{3c9d37b V.ED..... ......ID 0,0-0,0 #7f0c0054 app:id/item_text}

還有一個需要注意的地方是:如果在第一種方法的基礎(chǔ)上再加上mLinearLayout.addView(inflatedView)就會造成報錯
IllegalStateException: The specified child already has a parent....  。

而如果第二種方法沒有這句話,界面上是看不到任何東西的。

root為null的情況

mLinearLayout = (LinearLayout) findViewById(R.id.linear);View inflatedView = LayoutInflater.from(this).inflate(R.layout.item_text, null);Log.d(TAG, "inflated view is " + inflatedView);mLinearLayout.addView(inflatedView);

此時再看看它的布局文件:

<?xml version="1.0" encoding="utf-8"?><TextView xmlns:android="http://schemas.android.com/apk/res/android"  android:id="@+id/item_text"  android:layout_width="200dp"  android:layout_height="50dp"  android:layout_marginLeft="16dp"  android:layout_marginTop="16dp"  android:background="@color/colorAccent"  android:gravity="center"  android:text="item: text"/>

不難發(fā)現(xiàn),所有l(wèi)ayout_xxx的屬性全都失效了。

RecyclerView中的Inflater

上面說了,在創(chuàng)建布局的時候,要把布局添加到root中去,并且有兩種方法,但是我們在onCreateViewHolder中添加布局的時候卻是這樣寫的:

@Overridepublic MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_text, parent, false); return new MyViewHolder(view);}

如果第三個參數(shù)傳了true還會報錯,這又是為什么呢?

java.lang.IllegalStateException: The specified child already has a parent.

直觀上來解釋就是,子View的添加與刪除是由RecyclerView來管理的,不需要我們來添加。但我們還是從RecyclerView的代碼來理解一下會好一些。

以LinearLayoutManager為例,RecyclerView在創(chuàng)建子View的時候會調(diào)用到LinearLayoutManager$layoutChunk方法:

void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,  LayoutState layoutState, LayoutChunkResult result) { // 在這里會調(diào)用到Adapter$onCreateViewHolder View view = layoutState.next(recycler); if (view == null) {  if (DEBUG && layoutState.mScrapList == null) {   throw new RuntimeException("received null view when unexpected");  }  // if we are laying out views in scrap, this may return null which means there is  // no more items to layout.  result.mFinished = true;  return; } LayoutParams params = (LayoutParams) view.getLayoutParams(); if (layoutState.mScrapList == null) {  if (mShouldReverseLayout == (layoutState.mLayoutDirection    == LayoutState.LAYOUT_START)) {   addView(view);  } else {   addView(view, 0);  } } else {  if (mShouldReverseLayout == (layoutState.mLayoutDirection    == LayoutState.LAYOUT_START)) {   addDisappearingView(view);  } else {   addDisappearingView(view, 0);  } } // 省略其它大部分代碼}

在初始化的時候,View view = layoutState.next(recycler)里面會調(diào)用到我們熟悉的onCreateViewHolder方法,然后我們在里面inflate的過程中第三個參數(shù)傳了true,將子View添加到了RecyclerView中去了。然而,獲得View之后,調(diào)用到了addView(因?yàn)槭浅跏蓟?,不可能調(diào)用addDisappearingView) ,這里又會去添加一次,所以報出了上面的IllegalStateException異常。

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對各位Android開發(fā)者們能帶來一定的幫助,如果有疑問大家可以留言交流,謝謝大家對武林網(wǎng)的支持。

發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 保靖县| 温宿县| 福海县| 广昌县| 白玉县| 厦门市| 务川| 南安市| 屏东县| 道真| 绍兴市| 柯坪县| 界首市| 纳雍县| 明水县| 雅江县| 乐安县| 清新县| 汉沽区| 宜黄县| 太和县| 邵武市| 周宁县| 邻水| 荥经县| 元阳县| 商水县| 岑溪市| 奉贤区| 宁晋县| 柯坪县| 德惠市| 麦盖提县| 桂阳县| 马鞍山市| 西充县| 聂拉木县| 湟中县| 西藏| 会宁县| 诸城市|