Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does the LayoutInflater attachToRoot parameter mean?

People also ask

What is LayoutInflater explain the inflate method?

LayoutInflater creates View objects based on layouts defined in XML. There are several different ways to use LayoutInflater, including creating custom Views, inflating Fragment views into Activity views, creating Dialogs, or simply inflating a layout file View into an Activity.

What is LayoutInflater in Android example?

android.view.LayoutInflater. Instantiates a layout XML file into its corresponding View objects. It is never used directly. Instead, use Activity.

What does attach to root mean?

attachToRoot: attaches the views to their parent (includes them in the parent hierarchy), so any touch event that the views recieve will also be transfered to parent view. Now it's upto the parent whether it wants to entertain those events or ignore them.

What is the use of LayoutInflater?

Using the getLayoutInflater method we get LayoutInflater, use it to get View element from text. xml and get LayoutParams from the view we have just created. Take a look at the parameters we used to call the inflate method.


NOW OR NOT NOW

The main difference between the "third" parameter attachToRoot being true or false is this.

When you put attachToRoot

true : add the child view to parent RIGHT NOW
false: add the child view to parent NOT NOW.
Add it later. `

When is that later?

That later is when you use for eg parent.addView(childView)

A common misconception is, if attachToRoot parameter is false then the child view will not be added to parent. WRONG
In both cases, child view will be added to parentView. It is just the matter of time.

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

is equivalent to

inflater.inflate(child,parent,true);

A BIG NO-NO
You should never pass attachToRoot as true when you are not responsible for adding the child view to parent.
Eg When adding Fragment

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

if you pass third parameter as true you will get IllegalStateException because of this guy.

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

Since you have already added the child fragment in onCreateView() by mistake. Calling add will tell you that child view is already added to parent Hence IllegalStateException.
Here you are not responsible for adding childView, FragmentManager is responsible. So always pass false in this case.

NOTE: I have also read that parentView will not get childView touchEvents if attachToRoot is false. But I have not tested it though.


If set to true then when your layout is inflated it will be automatically added to the view hierarchy of the ViewGroup specified in the 2nd parameter as a child. For example if the root parameter was a LinearLayout then your inflated view will be automatically added as a child of that view.

If it is set to false then your layout will be inflated but won't be attached to any other layout (so it won't be drawn, receive touch events etc).


Seems like a lot of text in the responses but no code, that's why I decided to revive this old question with a code example, in several responses people mentioned:

If set to true then when your layout is inflated it will be automatically added to the view hierarchy of the ViewGroup specified in the 2nd parameter as a child.

What that actually means in code(what most programmers understand) is:

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

Notice that previous code is adding the layout R.layout.child_view as child of MyCustomLayout because of attachToRoot param is true and assigns the layout params of the parent exactly in the same way as if I would be using addView programmatically, or as if I did this in xml:

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

The following code explains the scenario when passing attachRoot as false:

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

In the previous code you specify that you wanted myView to be it's own root object and do not attach it to any parent, later on we added it as part of the LinearLayout but for a moment it was a stand-alone (no parent) view.

Same thing happens with Fragments, you could add them to an already existing group and be part of it, or just pass the parameters:

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

To specify that it will be it's own root.


The documentation and the two previous answers should be enough, just some thoughts from me.

The inflate method is used to inflate layout files. With those inflated layouts you have to possibility to attach them directly to a parent ViewGroup or just inflate the view hierarchy from that layout file and work with it outside of the normal view hierarchy.

In the first case the attachToRoot parameter will have to be set to true(or much simple use the inflate method that takes a layout file and a parent root ViewGroup(non null)). In this case the View returned is simply the ViewGroup that was passed in the method, the ViewGroup to which the inflated view hierarchy will be added.

For the second option the returned View is the root ViewGroup from the layout file. If you remember our last discussion from the include-merge pair question this is one of the reasons for the merge's limitation(when a layout file with merge as root is inflated, you must supply a parent and attachedToRoot must be set to true). If you had a layout file with the root a merge tag and attachedToRoot was set to false then the inflate method will have nothing to return as merge doesn't have an equivalent. Also, as the documentation says, the inflate version with attachToRoot set to false is important because you can create the view hierarchy with the correct LayoutParams from the parent. This is important in some cases, most notable with the children of AdapterView, a subclass of ViewGroup, for which the addView() methods set is not supported. I'm sure you recall using this line in the getView() method:

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

This line ensures that the inflated R.layout.row_layout file has the correct LayoutParams from the AdapterView subclass set on its root ViewGroup. If you wouldn't be doing this you could have some problems with the layout file if the root was a RelativeLayout. The TableLayout/TableRow also have some special and important LayoutParams and you should make sure the views in them have the correct LayoutParams.


I myself was also confused about what was the real purpose of attachToRoot in inflate method. After a bit of UI study, I finally got the answer:

parent:

in this case is the widget/layout that is surrounding the view objects that you want to inflate using findViewById().

attachToRoot:

attaches the views to their parent (includes them in the parent hierarchy), so any touch event that the views recieve will also be transfered to parent view. Now it's upto the parent whether it wants to entertain those events or ignore them. if set to false, they are not added as direct children of the parent and the parent doesn't recieve any touch events from the views.

Hope this clears the confusion


I wrote this answer because even after going through several StackOverflow pages I wasn't able to clearly grasp what attachToRoot meant. Below is inflate() method in the LayoutInflater class.

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

Take a look at activity_main.xml file, button.xml layout and the MainActivity.java file I created.

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

When we run the code, we won't see the button in the layout. This is because our button layout is not added into the main activity layout since attachToRoot is set to false.

LinearLayout has an addView(View view) method which can be used to add Views to LinearLayout. This will add the button layout into the main activity layout, and make the button visible when you run the code.

root.addView(view);

Let's remove the previous line, and see what happens when we set attachToRoot as true.

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

Again we see that the button layout is visible. This is because attachToRoot directly attaches the inflated layout to the parent specified. Which in this case is root LinearLayout. Here we don't have to add the views manually like we did in the previous case with addView(View view) method.

Why are people getting IllegalStateException when setting attachToRoot as true for a Fragment.

This is because for a fragment you have already specified where to place your fragment layout in your activity file.

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

The add(int parent, Fragment fragment) adds the fragment which has it's layout to the parent layout. If we set attachToRoot as true, you will get IllegalStateException: The specified child already has a parent. Since fragment layout is already added to the parent layout in the add() method.

You should always pass false for attachToRoot when you're inflating Fragments. It is the FragmentManager’s job to add, remove and replace Fragments.

Back to my example. What if we do both.

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

In the first line, LayoutInflater attaches the button layout to the root layout and returns a View object which holds the same button layout. In the second line, we add the same View object to the parent root layout. This results in the same IllegalStateException we saw with Fragments (The specified child already has a parent).

Keep in mind that there is another overloaded inflate() method, which sets attachToRoot as true by default.

View inflate (int resource, ViewGroup root)