So far the principles of the graphic system are simple enough.
Every control or widget corresponds to a View object and you can build a UI by creating view objects in code and adding them to Layouts. An Activity will draw its View hierarchy to the screen when it needs to.
OK, this is how to create a UI in code but so far we have been building a UI using the Designer.
How does this relate to the View hierarchy?
The Designer creates an XML file which describes the View hierarchy that you want to create. The way that this works is fairly obvious. Each tag in the XML file corresponds to a View object that you would like to create an instance of.
would create an instance of a LinearLayout object.
Nesting tags within a layout indicates that the object created need to be added to the layout as child Views. For example:
<LinearLayout> <Button /> </LinearLayout>
would create a LinearLayout object and a Button object and then add the Button object to the LinearLayout using its addView method.
You can see that the XML captures the idea of the View hierarchy perfectly.
To set object properties all you have to do is is use the corresponding attributes in the XML. For example to set the button's text you would use
<Button android:text="New Button" />
That is really all there is to it. The XML defines a hierarchy of objects and their properties and the system reads the file and creates the objects. This use of XML as an object instantiation system is not an uncommon one.
Of course, the XML created by the designer looks a lot more complicated than the examples above but this is mainly because of the number of attributes it defines. The basic idea is still the same.
The final big question to be answered is how does the XML get converted into a real object hierarchy?
The answer to this is to use an "inflater". To inflate a layout is Android jargon for instantiating the objects defined by an XML file.
You normally don't have to call an inflater because the system does it for you behind the scenes, but you can if you want to.
For example, to inflate a layout you would use an instance of the LayoutInflater. Normally you wouldn't create a fresh instance, but opt to borrow ths system's using getLayoutInflater. Once you have the LayoutInflater you can use one of its many inflate methods to create a View object hierarchy as specified by the XML. Which method you use depends on where the XML is stored. You can simply supply a resource id for an XML file included in the res directory.
For example to inflate the usual activity_main.xml layout you would use:
The second parameter of inflate can be used to provide a View object to act as the root container for the inflated View hierarchy.
Of course this is entirely equivalent to the usual:
which calls the LayoutInflater and sets the view in one instruction.
The only reason that you would manually inflate an XML layout is if you wanted to do something clever such as put one layout together with another or in some way manipulate the View hierarchy.
Notice that there are other types of inflater objects - e.g. the Menu inflater but it does the same job of converting XML to instantiated objects with the given properties. There is also a version of the inflate method:
which will inflate the XML resource using root as its container for the purposes of layout if the last parameter is false and it will add the inflated View to the root if the last parameter is true.
Finding View objects
One problem we have to solve if you want to work with the View hierarchy created by an inflater is finding View objects in the hierarchy.
In the example where we built the View hierarchy in code it was easy to keep track of a button or a textView by simply keeping a reference to when it was created. An inflater simply returns the View hierarchy without an easy way to get at a particular object, a button say.
One way of solving the problem would be to "walk" the View tree. A ViewGroup object e.g. a layout not only has an addView method but a range of methods that allow you to access objects it contains. Each child object is assigned an integer index - think of it like an array. The method:
will return the child object at index i.
You can also use:
to find out how many child objects are stored in the container.
Using these methods you can search the hierarchy for the View object you want but how do you know which one it is?
The answer to this one is that all View objects have an id property which should identify them uniquely. There are getid and setid methods but usually the id property is set as part of the XML file.
To avoid you having to work out an id value the standard way of setting an id is to define a resource:
When the XML file is inflated the @+ symbol is interpreted as "create a resource". An integer id is generated using the generateViewId method and this is used to both create the id property and to add a my_button property to the R.id object.
There is a lot more to say about resources, but for the moment this is enough to understand what is going on.
What all this means is that not only do you get an autogenerated id value, but also a way to get this value into running code. You could use the getChildAt methods to step through all of the View objects in the hierarchy, but it is much easier to use:
which returns the object in one instruction.
How To Build A UI?
You now have two, at least, approaches to building a UI. You can do the whole job in code or you can create an XML layout.
In practice it is usually easier to use the Android Studio designer to generate the XML file for you. You can however mix the two approaches and change a UI "on the fly". For example you can load a UI by implicitly or explicitly inflating an XML file and then writing code to create and add other widgets or even remove View objects from the layout. To remove View object you simply use the removeView or removeViewAt methods of the ViewGroup object.
What more is there to learn?
In the next few chapters we need to find out about fragments, dialog boxes and 2D graphics.