web-dev-qa-db-ja.com

LayoutInflater attachToRootパラメーターの意味は何ですか?

LayoutInflater.inflate のドキュメントでは、attachToRootパラメーターの目的について明確にはわかりません。

attachToRoot:膨張した階層をルートパラメータにアタッチする必要があるかどうかfalseの場合、ルートは、XMLのルートビューのLayoutParamsの正しいサブクラスを作成するためにのみ使用されます。

誰かがより詳細に、具体的にはルートビューとは何かを説明してもらえますか?また、truefalseの値の間の動作の変化の例を示すことができますか?

160
Jeff Axelrod

Trueに設定されている場合、レイアウトが膨張すると、2番目のパラメーターで子として指定されたViewGroupのビュー階層に自動的に追加されます。たとえば、ルートパラメータがLinearLayoutの場合、膨張したビューはそのビューの子として自動的に追加されます。

Falseに設定されている場合、レイアウトは膨張しますが、他のレイアウトにアタッチされません(したがって、描画されず、タッチイベントなどを受け取りません)。

91
Joseph Earl

NOW OR NOT NOT NOW

Trueかfalseである「3番目」のパラメータattachToRootの主な違いはこれです。

AttachToRootを置くとき

true:親に子ビューを追加RIGHT NOW
false:親に子ビューを追加NOT NOW
後で追加します。 `

それはいつですか後で

後で使用するのは、たとえばparent.addView(childView)

よくある誤解です。attachToRootパラメーターがfalseの場合、子ビューは親に追加されません。 間違った
どちらの場合でも、子ビューはparentViewに追加されます。 timeの問題です。

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

と同等

inflater.inflate(child,parent,true);

A NO NO NO
親に子ビューを追加する責任がない場合、決してattachToRootをtrueに渡さないでください。
たとえば、フラグメントを追加する場合

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

3番目のパラメーターをtrueとして渡すと、この男のためにIllegalStateExceptionが発生します。

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

誤ってonCreateView()に子フラグメントをすでに追加しているため。 addを呼び出すと、子ビューがすでに親に追加されていることがわかります。したがって、IllegalStateExceptionです。
ここでは、childViewを追加する責任はありません。FragmentManagerが責任を負います。したがって、この場合は常にfalseを渡します。

注: attachToRootがfalseの場合、parentViewはchildView touchEventsを取得しないことも読みました。しかし、私はそれをテストしていません。

87
Rohit Singh

回答には多くのテキストがありますが、コードはないようです。そのため、私はこの古い質問をコード例で復活させることにしました。

Trueに設定されている場合、レイアウトが膨張すると、2番目のパラメーターで子として指定された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);
    }
}

前のコードは、MyCustomLayout paramがattachToRootであるため、trueの子としてレイアウトR.layout.child_viewを追加しており、親と同じようにレイアウトparamsを割り当てていることに注意してください。私は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の一部として追加しましたが、しばらくの間はスタンドアロン(親なし)ビュー。

同じことがFragmentsでも起こります。それらを既存のグループに追加してその一部にしたり、単にパラメーターを渡したりすることができます。

inflater.inflate(R.layout.fragment、null、false);

それがそれ自身のルートになることを指定するには。

36
Martin Cazares

ドキュメントと以前の2つの回答で十分なはずです。私からのいくつかの考えです。

inflateメソッドは、レイアウトファイルを膨張させるために使用されます。これらの拡張されたレイアウトでは、親ViewGroupに直接アタッチするか、そのレイアウトファイルからビュー階層を単純に拡張して、通常のビュー階層外で操作する可能性が必要です。

最初のケースでは、attachToRootパラメーターをtrueに設定する必要があります(または、レイアウトファイルと親ルートinflate(non ViewGroup)を使用するnullメソッドを非常に簡単に使用します)。この場合、返されるViewは、メソッドで渡されたViewGroupであり、拡張されたビュー階層が追加されるViewGroupです。

2番目のオプションの場合、返されるViewは、レイアウトファイルのルートViewGroupです。 include-merge pair question からの最後の議論を覚えているなら、これがmergeの制限の理由の1つです(ルートとしてmergeを持つレイアウトファイルが膨張するとき、親を提供しなければなりませんattachedToRoottrueに設定する必要があります。ルートにmergeタグがあり、attachedToRootfalseに設定されたレイアウトファイルがある場合、inflateには同等のものがないため、mergeメソッドは何も返しません。また、ドキュメントにあるように、inflateattachToRootに設定したfalseバージョンは、親から正しいLayoutParamsを使用してビュー階層を作成できるため重要です。これはいくつかの場合に重要であり、AdapterViewのサブクラスであるViewGroupの子では、addView()メソッドセットはサポートされていません。 getView()メソッドでこの行を使用したことを思い出すと思います:

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

この行により、膨張したR.layout.row_layoutファイルのルートLayoutParamsに設定されたAdapterViewサブクラスからの正しいViewGroupが確実に保持されます。これを行わない場合、ルートがRelativeLayoutであると、レイアウトファイルに問題が発生する可能性があります。 TableLayout/TableRowには特別で重要なLayoutParamsもあり、それらのビューに正しいLayoutParamsがあることを確認する必要があります。

26
Luksprog

私自身も、attachToRootメソッドのinflateの本当の目的について混乱していました。 UIを少し調べたところ、ついに答えが得られました。

親:

この場合、findViewById()を使用して膨張させたいビューオブジェクトを囲むウィジェット/レイアウトです。

attachToRoot:

ビューを親にアタッチします(親階層にビューを含めます)。したがって、ビューが受け取るタッチイベントも親ビューに転送されます。これらのイベントを楽しませるか無視するかは、親次第です。 。 falseに設定されている場合、それらは親の直接の子として追加されず、親はビューからタッチイベントを受け取りません。

これで混乱が解消されることを願っています

17
Umer Farooq

Inflate()メソッドのドキュメントのため、このトピックには多くの混乱があります。

一般に、attachToRootがtrueに設定されている場合、最初のパラメーターで指定されたレイアウトファイルは膨張し、その時点で2番目のパラメーターで指定されたViewGroupにアタッチされます。 attachToRootがfalseの場合、最初のパラメーターのレイアウトファイルが膨張し、Viewとして返されます。また、Viewの添付ファイルは他のときに発生します。

多くの例を参照しない限り、これはおそらくあまり意味がありません。 FragmentのonCreateViewメソッド内でLayoutInflater.inflate()を呼び出す場合、そのFragmentに関連付けられているActivityが実際にそのFragmentのビューを追加する責任があるため、attachToRootにfalseを渡します。 addView()メソッドなどを使用して、後の時点でビューを手動で拡大して別のビューに追加する場合、添付ファイルは後の時点で来るため、attachToRootにfalseを渡す必要があります。

このトピックについて書いたブログ投稿で、ダイアログとカスタムビューに関する他のいくつかのユニークな例を読むことができます。

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

9
seanjfarrell

この答えを書いたのは、いくつかの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には、LinearLayoutにビューを追加するために使用できるaddView(View view)メソッドがあります。これにより、ボタンレイアウトがメインアクティビティレイアウトに追加され、コードの実行時にボタンが表示されます。

root.addView(view);

前の行を削除して、attachToRootをtrueに設定するとどうなるかを見てみましょう。

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

ここでも、ボタンレイアウトが表示されていることがわかります。これは、attachToRootが膨張したレイアウトを指定された親に直接アタッチするためです。この場合、これはルートLinearLayoutです。ここでは、前のaddView(View view)メソッドの場合のようにビューを手動で追加する必要はありません。

なぜFragmentのattachToRootをtrueに設定するとIllegalStateExceptionが発生するのですか?

これは、フラグメントの場合、アクティビティファイルのフラグメントレイアウトを配置する場所を既に指定しているためです。

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

add(int parent、Fragment fragment)は、そのレイアウトを持つフラグメントを親レイアウトに追加します。 attachToRootをtrueに設定すると、IllegalStateException:指定された子にはすでに親があります。フラグメントレイアウトは既にadd()メソッドで親レイアウトに追加されているため。

フラグメントを膨らませている場合、attachToRootには常にfalseを渡す必要があります。フラグメントを追加、削除、置換するのはFragmentManagerの仕事です。

私の例に戻ります。両方を行うとどうなりますか。

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

最初の行では、LayoutInflaterはボタンレイアウトをルートレイアウトにアタッチし、同じボタンレイアウトを保持するViewオブジェクトを返します。 2行目では、同じViewオブジェクトを親ルートレイアウトに追加します。これにより、フラグメントで見たIllegalStateExceptionと同じ結果になります(指定された子にはすでに親があります)。

デフォルトでattachToRootをtrueに設定する別のオーバーロードされたinflate()メソッドがあることに注意してください。

View inflate (int resource, ViewGroup root)
7
capt.swag

attachToRootをtrueに設定すると、inflatedViewが親ビューの階層に追加されます。したがって、ユーザーによって「表示」され、タッチイベント(またはその他のUI操作)を検知する可能性があります。それ以外の場合、作成されただけで、ビュー階層に追加されていないため、タッチイベントを表示したり処理したりすることはできません。

Androidを初めて使用するiOS開発者の場合、attachToRootをtrueに設定すると、このメソッドが呼び出されます。

[parent addSubview:inflatedView];

さらに進むと、あなたは尋ねるかもしれません:attachToRootfalseに設定した場合、なぜ親ビューを渡す必要があるのですか?これは、XMLツリーのルート要素がいくつかのLayoutParamを計算するために親ビューを必要とするためです(親と一致するなど)。

4
Alston

attachToRoot trueに設定:

AttachToRootがtrueに設定されている場合、最初のパラメーターで指定されたレイアウトファイルが拡張され、2番目のパラメーターで指定されたViewGroupにアタッチされます。

XMLレイアウトファイルで、レイアウトの幅とレイアウトの高さをmatch_parentに設定したボタンを指定したとします。

<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);

レイアウトリソースファイルからButtonを膨張させることを指定しました。次に、それをmLinearLayoutにアタッチすることをLayoutInflaterに伝えます。 ButtonがLinearLayoutに追加されることがわかっているため、レイアウトパラメーターが使用されます。ボタンのレイアウトパラメータタイプは、LinearLayout.LayoutParamsである必要があります。

attachToRoot falseに設定(falseを使用する必要はありません)

AttachToRootがfalseに設定されている場合、最初のパラメーターで指定されたレイアウトファイルが拡張され、2番目のパラメーターで指定されたViewGroupにnotが添付されますviewは親のLayoutParamsを取得します。これにより、そのビューは親に正しく収まります。


AttachToRootをfalseに設定するタイミングを見てみましょう。このシナリオでは、この時点で、inflate()の最初のパラメーターで指定されたビューは、2番目のパラメーターでViewGroupにアタッチされていません。

レイアウトファイルからmLinearLayoutにカスタムボタンをアタッチする以前のボタンの例を思い出してください。 attachToRootにfalseを渡すことで、ButtonをmLinearLayoutにアタッチできます。後で手動で追加するだけです。

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

これらの2行のコードは、attachToRootにtrueを渡したときに1行のコードで以前に記述したものと同等です。 falseを渡すことにより、ビューをルートViewGroupにまだアタッチしたくないと言います。他の時点で起こると言っています。この例では、他の時点は、インフレーションのすぐ下で使用されるaddView()メソッドです。

FalseのattachToRootの例では、ViewをViewGroupに手動で追加するときにもう少し作業が必要です。

attachToRootをfalseに設定(falseは必須)

onCreateView()でフラグメントのビューを拡大して返すときは、attachToRootに対して必ずfalseを渡します。 trueを渡すと、指定された子にはすでに親があるため、IllegalStateExceptionが発生します。アクティビティ内でフラグメントのビューを配置する場所を指定する必要があります。 Fragmentsの追加、削除、交換は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は、フラグメントのビューをこのViewGroupにアタッチします。 2回添付する必要はありません。 attachToRootをfalseに設定します。

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

OnCreateView()で添付したくないのに、フラグメントの親ViewGroupが最初に与えられるのはなぜですか? inflate()メソッドがルートViewGroupを要求するのはなぜですか?

新しく拡大されたビューをその親ViewGroupにすぐに追加しない場合でも、最終的にアタッチされるたびに新しいビューがそのサイズと位置を決定するために、親のLayoutParamsを使用する必要があります。

リンク: https://youtu.be/1Y0LlmTCOkM?t=409

0
Utshaw

親を定義すると、attachToRootによって、インフレータが実際に親にアタッチするかどうかが決まります。 ListAdapterの場合、リストがビューをリストに追加しようとしますが、既に添付されていると言うため、例外が発生するなどの場合、これにより問題が発生します。ビューを自分で膨らませてアクティビティに追加するだけの場合は、便利でコードを1行節約できます。

0
CaseyB

たとえば、ImageViewLinearLayoutおよびRelativeLayoutがあります。 LinearLayoutはRelativeLayoutの子です。ビュー階層になります。

RelativeLayout
           ------->LinearLayout

そして、ImageView用に別のレイアウトファイルがあります

image_view_layout.xml

ルートに接続:

//here container is the LinearLayout

    View v = Inflater.Inflate(R.layout.image_view_layout,container,true);
  1. ここでvにはコンテナレイアウトの参照、つまりLinearLayoutが含まれます。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. ここでvには参照コンテナレイアウトは含まれませんが、膨張したImageViewへの直接参照が含まれているため、findViewByIdのように参照することなく、view.setImageResource(R.drawable.np);のようなパラメータを設定できます。しかし、コンテナはImageViewがコンテナのLayoutParamsを取得するように指定されているため、コンテナの参照はLayoutParams以外のものではないと言うことができます。
  2. そのため、特定の場合、Parentはnullになります。
  3. LayoutParamsはLinearLayoutになります。
0
Faisal Naseer