2012-09-24 84 views
113

LayoutInflater.inflate文檔對我來說不完全清楚attachToRoot參數的用途。LayoutInflater attachToRoot參數的含義是什麼?

attachToRoot:膨脹層次結構是否應附加到根參數?如果爲false,則root僅用於爲XML中的根視圖創建LayoutParams的正確子類 。更詳細

可能有人請解釋一下,特別是根的觀點是什麼,也許表明truefalse值之間的行爲變化的一個例子嗎?

+1

相關:[製作LayoutInflater感](http://stackoverflow.com/questions/5026926/making-sense-of-layoutinflater) – blahdiblah

+0

重複:https://stackoverflow.com/questions/22326314/什麼是使用附着到根佈局的inflater – mikebabcock

回答

1

當您定義父級時,attachToRoot將確定您是否希望inflater將其實際附加到父級或不是父級。在某些情況下,這會導致問題,就像在ListAdapter中一樣,它將導致異常,因爲列表試圖將視圖添加到列表中,但它表示它已經連接。在其他情況下,您只需將視圖自己添加到Activity中就可以很方便併爲您節省一行代碼。

66

如果設置爲true,那麼當您的佈局充氣時,它將自動添加到第二個參數中指定的ViewGroup的視圖層次結構中作爲子項。例如,如果根參數是LinearLayout,那麼您的充氣視圖將自動添加爲該視圖的子視圖。

如果它被設置爲false,那麼你的佈局將被誇大,但不會被附加到任何其他佈局(所以它不會被繪製,接收觸摸事件等)。

+10

我很困惑。直到我閱讀[這個答案](http://stackoverflow.com/a/6036484/403455)時,我得到了「指定的孩子已經有一個父錯誤」,它指示我在我的「attachToRoot」期間使用'false'片段的'onCreateView'。這解決了問題,但片段的佈局是可見的和活躍的,儘管你的答案。這是怎麼回事? –

+52

因爲片段自動附加從onCreateView返回的佈局,所以如果你手動附加onCreateView然後你查看會附加到2個父母(這會產生你提到的錯誤) –

+8

我在這裏有點困惑,@JosephEarl你說如果設置爲'true',視圖被附加到'容器'的第二參數,但是那麼你說片段是從'onCreateView()'自動附加的,所以就我的理解而言,第三個參數是無用的,應該總是設置爲'false'? – unmultimedio

25

該文檔和前兩個答案應該足夠了,只是我的一些想法。

inflate方法用於給佈局文件充氣。使用這些膨脹的佈局,您必須可以將它們直接附加到父項ViewGroup,或者只是從該佈局文件中膨脹視圖層次結構,並在普通視圖層次結構外使用它。

在第一種情況的attachToRoot參數將必須被設置爲true(或許多簡單的採用了inflate方法,它的佈局文件和母根ViewGroup(非null))。在這種情況下,返回的View只是在方法中傳遞的ViewGroupViewGroup將向其添加充值視圖層次結構。

對於第二個選項,返回的View是佈局文件中的根ViewGroup。如果您還記得include-merge pair question的最後一次討論,這是merge的侷限性的原因之一(以merge作爲根的佈局文件被誇大時,您必須提供父項並且attachedToRoot必須設置爲true)。如果您有一個佈局文件,其根目錄爲merge標記,並且attachedToRoot設置爲false,則inflate方法將無法返回,因爲merge沒有等效項。 另外,如文檔所述,inflate版本的attachToRoot設置爲false非常重要,因爲您可以使用父級的正確LayoutParams創建視圖層次結構。這在一些情況下是重要的,其中最值得注意的是AdapterView的子類ViewGroup的子類,其中addView()方法組不受支持。我敢肯定,你還記得使用該線路getView()方法:

convertView = inflater.inflate(R.layout.row_layout, parent, false); 

該行確保膨脹R.layout.row_layout文件具有從AdapterView子在其根ViewGroup設置正確的LayoutParams。如果你不這樣做,如果根目錄是RelativeLayout,你可能會在佈局文件中遇到一些問題。 TableLayout/TableRow也有一些特殊和重要的LayoutParams,你應該確保其中的視圖有正確的LayoutParams

33

好像很多的反應,但沒有代碼文本的,這就是爲什麼我決定恢復同一個代碼示例這個老問題,在若干答覆人提到:

如果設置爲true,那麼當你的佈局充氣後,它將自動添加到第二個參數中指定的ViewGroup的視圖層次結構中作爲子項。

什麼,實際上意味着代碼(大多數程序員理解)是:

public class MyCustomLayout extends LinearLayout { 
    public MyCustomLayout(Context context) { 
     super(context); 
     // Inflate the view from the layout resource and pass it as child of mine (Notice I'm a LinearLayout class). 

     LayoutInflater.from(context).inflate(R.layout.child_view, this, true); 
    } 
} 

注意,上面的代碼被添加布局R.layout.child_view作爲MyCustomLayout因爲attachToRoot參數是true兒童和分配佈局父母的參數完全相同,就好像我將以編程方式使用addView一樣,或者如果我在xml中執行此操作:

<LinearLayout> 
    <View.../> 
    ... 
</LinearLayout> 

下面的代碼說明路過的時候attachRootfalse場景:

LinearLayout linearLayout = new LinearLayout(context); 
linearLayout.setLayoutParams(new LayoutParams(
    LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); 
linearLayout.setOrientation(LinearLayout.VERTICAL); 
    // Create a stand-alone view 
View myView = LayoutInflater.from(context) 
    .inflate(R.layout.ownRootView, null, false); 
linearLayout.addView(myView); 

在上面的代碼你指定你想myView是它自己的根對象,並且不附加到任何父母,後來我們將其作爲LinearLayout的一部分加入,但暫時它是一個獨立的(無父母)視圖。

同樣的事情發生的片段,你可以將它們添加到已存在的組,併成爲其中的一部分,或者只是傳遞的參數:

inflater.inflate(R.layout.fragment,空,假的);

指定它將是它自己的根。

+1

最重要的是,這是最有幫助的。 –

16

我自己也對attachToRootinflate方法中的真實目的感到困惑。有點UI的學習後,我終於得到了答案:

父:

在這種情況下是widget /佈局是圍繞要使用findViewById()膨脹的視圖對象。

attachToRoot:

附加意見,它們的母體(包括它們在父層級),所以任何觸摸事件,該觀點收到也將被轉移到父視圖。無論是想要接受這些事件還是忽視它們,現在都取決於父母。如果設置爲false,則不會將其添加爲父級的直接子級,並且父級也不會從視圖中接收任何觸摸事件。

希望這將清除混亂

+0

你的答案已經在這裏提供:http://stackoverflow.com/questions/22326314/what-is-the-use-of-attach-to-root-in-layout-inflater –

4

attachToRoot設置爲true意味着inflatedView將被添加到父視圖的層次。因此可能被用戶「看到」並感知觸摸事件(或任何其他UI操作)。否則,它只是被創建,沒有被添加到任何視圖層次結構,因此無法看到或處理觸摸事件。

對於iOS開發新到Android,attachToRoot設置爲true意味着你調用這個方法:

[parent addSubview:inflatedView]; 

如果要進一步的你可能會問:爲什麼我應該通過,如果我設置attachToRootfalse父視圖?這是因爲您的XML樹中的根元素需要父視圖來計算一些LayoutParams(如匹配父項)。

9

由於inflate()方法的文檔,對此主題存在很多混淆。

一般來說,如果attachToRoot設置爲true,那麼在第一個參數中指定的佈局文件將被充滿並附加到當時第二個參數中指定的ViewGroup。當attachToRoot爲false時,來自第一個參數的佈局文件將被膨脹並返回,因爲View和任何View都會在其他時間發生。

這可能並不意味着很多,除非你看到很多例子。在Fragment的onCreateView方法內部調用LayoutInflater.inflate()時,您會想爲attachToRoot傳入false,因爲與該Fragment關聯的Activity實際上負責添加該Fragment的視圖。如果在稍後的某個時間點(例如使用addView()方法)手動膨脹並向其他視圖添加視圖,則需要爲attachToRoot傳遞false,因爲附件是在稍後的時間點傳入的。

你可以在我寫的關於這個話題的博客文章中閱讀關於對話框和自定義視圖的其他幾個獨特例子。

https://www.bignerdranch.com/blog/understanding-androids-layoutinflater-inflate/

0

例如,我們有一個ImageView,一個LinearLayoutRelativeLayout。 LinearLayout是RelativeLayout的子項。 View Hierarchy會。

RelativeLayout 
      ------->LinearLayout 

,我們有ImageView的

image_view_layout一個單獨的佈局文件。XML

附加到根:

//here container is the LinearLayout 

    View v = Inflater.Inflate(R.layout.image_view_layout,container,true); 
  1. 這裏五載容器佈局即 參考LinearLayout.and如果你想設置的參數,如的ImageView的setImageResource(R.drawable.np);你會必須通過父親的參考找到它,例如view.findById()
  2. v的父母將是FrameLayout。
  3. LayoutParams將是FrameLayout。

不重視根:

//here container is the LinearLayout 
    View v = Inflater.Inflate(R.layout.image_view_layout,container,false); 
  1. 這裏五載無參考容器的佈局,但直接 參考該充氣,所以你可以設置像view.setImageResource(R.drawable.np);其 參數沒有ImageView的 參考findViewById。但容器被指定爲使得ImageView獲取容器的LayoutParams,因此您可以說 容器的引用僅用於LayoutParams,其他都不用 。
  2. 所以在特殊情況下,Parent將爲空。
  3. LayoutParams將爲LinearLayout。
+0

需要更多的解釋,代碼應該精心製作 – blackHawk

+0

是的,當然你需要一些詞lemme知道:) –

0

attachToRoot設置爲true:

如果attachToRoot被設置爲真,則在所述第一參數指定的佈局文件被充氣並連接到在所述第二參數指定的的ViewGroup。

想象一下,我們在其佈局寬度和佈局高度設置爲match_parent的XML佈局文件中指定了一個按鈕。

<Button xmlns:android="http://schemas.android.com/apk/res/android" 
      android:layout_width="match_parent" 
      android:layout_height="match_parent" 
      android:id="@+id/custom_button"> 
</Button> 

我們現在想以編程方式將此按鈕添加到片段或活動的LinearLayout中。如果我們的LinearLayout已經是一個成員變量,mLinearLayout,我們可以簡單地用下面的添加按鈕:

inflater.inflate(R.layout.custom_button, mLinearLayout, true); 

我們指定了我們想要誇大其佈局資源文件的按鈕;然後我們告訴LayoutInflater我們要將它附加到mLinearLayout。我們的佈局參數很榮幸,因爲我們知道Button被添加到LinearLayout中。按鈕的佈局參數類型應該是LinearLayout.LayoutParams。

attachToRoot設置爲false(用假不是必需的)

如果attachToRoot被設置爲假,則在所述第一參數指定的佈局文件被充氣並連接到在第二個參數中指定的ViewGroup,但是虛擬的視圖獲取父級的LayoutParams,該視圖使該視圖能夠在父級中正確匹配。


讓我們來看看,當你想attachToRoot設置爲false。在這種情況下,inflate()的第一個參數中指定的視圖在此時未附加到第二個參數中的ViewGroup。

回想一下前面我們的按鈕示例,我們希望將一個自定義按鈕從佈局文件附加到mLinearLayout。我們仍然可以通過爲attachToRoot傳遞false來將我們的Button附加到mLinearLayout-之後我們手動添加它。

Button button = (Button) inflater.inflate(R.layout.custom_button, mLinearLayout, false); 
mLinearLayout.addView(button); 

這兩行代碼相當於我們在一行代碼中寫入的內容,當我們爲attachToRoot傳入true時。通過傳入false,我們說我們不想將View添加到根ViewGroup中。我們說它會在其他時間點發生。在這個例子中,另一個時間點就是直接在通貨膨脹之下使用的addView()方法。

當我們手動將View添加到ViewGroup時,false attachToRoot示例需要更多的工作。

attachToRoot設置爲false(假是必需的)

當充氣並返回一個片段在onCreateView(視圖),請務必假以通爲attachToRoot。如果你傳入true,你將得到一個IllegalStateException,因爲指定的孩子已經有了一個父親。你應該已經指定了你的Fragment的視圖將放回到你的Activity中。 FragmentManager的工作是添加,刪除和替換碎片。

FragmentManager fragmentManager = getSupportFragmentManager(); 
Fragment fragment = fragmentManager.findFragmentById(R.id.root_viewGroup); 

if (fragment == null) { 
fragment = new MainFragment(); 
fragmentManager.beginTransaction() 
    .add(R.id.root_viewGroup, fragment) 
    .commit(); 
} 

的root_viewGroup容器,將保留您片段的活動是在你的片段onCreateView給你的ViewGroup中參數()。這也是您傳遞給LayoutInflater.inflate()的ViewGroup。然而,FragmentManager會處理你的Fragment的View到這個ViewGroup。你不想附加兩次。將attachToRoot設置爲false。

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

爲什麼我們要考慮到我們的片段的父ViewGroup中位居第一,如果我們不希望它附加在onCreateView()?爲什麼inflate()方法請求一個根ViewGroup?

事實證明,即使我們沒有立即將其新的充氣View添加到其父ViewGroup中,我們仍然應該使用父級的LayoutParams,以便新View隨時隨地確定其大小和位置。

鏈接:https://youtu.be/1Y0LlmTCOkM?t=409

12

NOW OR NOT NOW

「第三」 參數attachToRoot是真的還是假的之間的主要區別是這樣的。

當你把attachToRoot

真:添加子視圖父現在
假:添加子視圖父不是現在
稍後添加。 `

什麼時候是以後

,後來是當你使用如parent.addView(childView)

一個常見的誤解,如果attachToRoot參數爲假,則子視圖將不會被添加到母公司。 錯誤
在這兩種情況下,子視圖將被添加到parentView。這只是時間的問題。

inflater.inflate(child,parent,false); 
parent.addView(child); 

相當於

inflater.inflate(child,parent,true); 

大的NO-NO
切勿將attachToRoot當你不負責添加子視圖父爲真。
例如,當將片段

public View onCreateView(LayoutInflater inflater,ViewGroup parent,Bundle bundle) 
    { 
     super.onCreateView(inflater,parent,bundle); 
     View v = inflater.inflate(R.layout.image_fragment,parent,false); 
     ..... 
     return true 
    } 

如果傳遞第三個參數爲真,你會得到,因爲這傢伙IllegalStateException異常。

getSupportFragmentManager() 
     .beginTransaction() 
     .add(parent, childFragment) 
     .commit(); 

由於您已經錯誤地在onCreateView()中添加了子片段。調用添加會告訴您,子視圖已添加到父項因此IllegalStateException
在這裏你不負責添加childView,FragmentManager負責。所以在這種情況下總是傳錯。

注意:我也讀過,如果attachToRoot爲false,parentView將不會得到childView touchEvents。但我還沒有測試過它。

3

我寫了這個答案,因爲即使通過幾個StackOverflow頁面後,我無法清楚地掌握attachToRoot的含義。以下是LayoutInflater類中的inflate()方法。

View inflate (int resource, ViewGroup root, boolean attachToRoot) 

activity_main.xml中文件,button.xml佈局,我創建了MainActivity.java文件看看。

activity_main.xml中

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:id="@+id/root" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:orientation="vertical"> 

</LinearLayout> 

button.xml

<Button xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content" /> 

MainActivity.java

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_main); 

    LayoutInflater inflater = getLayoutInflater(); 
    LinearLayout root = (LinearLayout) findViewById(R.id.root); 
    View view = inflater.inflate(R.layout.button, root, false); 
} 

當我們運行的代碼,我們將不會看到在佈局按鈕。這是因爲我們的按鈕佈局未添加到主活動佈局中,因爲attachToRoot設置爲false。

LinearLayout有一個addView(View view)可用於將視圖添加到LinearLayout的方法。這會將按鈕佈局添加到主活動佈局中,並在運行代碼時使按鈕可見。

root.addView(view); 

讓我們刪除上一行,看看將attachToRoot設置爲true時會發生什麼。

View view = inflater.inflate(R.layout.button, root, true); 

我們再次看到按鈕佈局是可見的。這是因爲attachToRoot直接將膨脹佈局附加到指定的父級。在這種情況下,它是根LinearLayout。在這裏,我們不需要像使用addView(View視圖)方法那樣手動添加視圖。

爲什麼人們在爲片段設置attachToRoot爲true時會出現IllegalStateException異常。

這是因爲對於您已經指定將片段佈局放置在活動文件中的片段。

FragmentManager fragmentManager = getSupportFragmentManager(); 
fragmentManager.beginTransaction() 
    .add(R.id.root, fragment) 
    .commit(); 

加載(INT父,片段片段)增加,其具有它的佈局與母體佈局的片段。如果我們將attachToRoot設置爲true,則會得到IllegalStateException:指定的子項已經有父項。由於片段佈局已添加到add()方法中的父佈局中。

當你給片段充氣時,你總是應該爲attachToRoot傳遞false。 FragmentManager的工作是添加,刪除和替換碎片。

回到我的例子。如果我們都這樣做了會怎樣

View view = inflater.inflate(R.layout.button, root, true); 
root.addView(view); 

在第一行中,LayoutInflater附加按鍵佈局到根佈局,並返回保持相同的按鈕佈局的查看對象。在第二行中,我們將相同的View對象添加到父級根佈局。這會導致與我們在Fragments中看到的相同的IllegalStateException(指定的子項已經有父項)。

請記住,還有另一個重載的inflate()方法,默認情況下它將attachToRoot設置爲true。

View inflate (int resource, ViewGroup root) 
相關問題