Creating Compound Views on Android

When building complex applications, you'll often want to reuse the same group of views in various places of the application. One way to solve this problem is by creating a view that encapsulates the logic and layout of a group of views so you can reuse them without duplicating code in various places of the project. In this tutorial, you'll learn how to use compound views to create custom views that are easily reusable.

1. Introduction

On Android, a view composed of a group of views is called a compound view or a compound component. In this tutorial, you'll build a control to select a value from a list that scrolls from side to side. We'll name the compound a side spinner since the default view of the Android SDK to pick a value from a list is called a spinner. The following screenshot illustrates what we'll be creating in this tutorial.

2. Project Setup

To get started, you must create a new Android project with Android 4.0 as the minimum required SDK level. This project should only contain a blank activity called MainActivity. The Activity does nothing more than initializing the layout as you can see in the following code snippet.

The layout for MainActivity is located in the /res/layout/activity_main.xml file and it should only only contain an empty RelativeLayout in which the compound view will be displayed later.

3. Create a Compound View

To create a compound view, you must create a new class that manages the views in the compound view. For the side spinner, you need two Button views for the arrows and a TextView view to display the selected value.

To get started, create the /res/layout/sidespinner_view.xml layout file that we'll use for the side spinner class, making sure to wrap the three views in a <merge> tag.

Next, we need to create the SideSpinner class that inflates this layout and sets the arrows as the background images for the buttons. At this point, the compound view doesn't do anything since there is nothing to show yet.

You'll notice that the compound view extends the LinearLayout view group. This means that any layout using the compound view has access to the attributes of the linear layout. As a result, the layout for the compound view is a bit different than usual, the root tag is a <merge> tag instead of the tag for a view group like <LinearLayout> or <RelativeLayout>.

When you add the compound view to the layout of MainActivity, the tag for the compound view will act as a <LinearLayout> tag. A compound view class can derive from any class deriving from ViewGroup, but in this case the linear layout is the most appropriate since the views are laid out horizontally.

4. Add the Compound View to a Layout

At this point, the project compiles but nothing is visible since the compound view is not in the layout of MainActivity. The side spinner view must be added to the layout of the activity like any other view. The name of the tag is the full name of the SideSpinner class, including the namespace.

To add the side spinner to MainActivity, add the following to the relative layout in the /res/layout/activity_main.xml file.

The attributes available in the <SideSpinner> tag are attributes of the linear layout since the SideSpinner class we created extends the LinearLayout class. If you launch the project, the side spinner should be visible, but it doesn't contain any values yet.

5. Add Methods to the Compound View

There are still a few things missing if we want to actually use the side spinner. We should be able to add new values to the spinner, select a value, and get the selected value.

The easiest way to add new behaviors to a compound view is to add new public methods to the SideSpinner class. These methods can be used by any Activity that has a reference to the view.

The onFinishInflate method of the compound view is called when all the views in the layout are inflated and ready to use. This is the place to add your code if you need to modify views in the compound view.

With the methods you just added to the SideSpinner class, behavior for the buttons selecting the previous and next value can now be added. Replace the existing code in the onFinishInflate method with the following:

With the newly created setValues and setSelectedIndex methods, we can now initialize the side spinner from our code. As with any other view, you need to find the side spinner view in the layout with the findViewById method. We can then call any public method on the view from the object returned, including the ones we just created.

The following code snippet shows how to update the onCreate method of the MainActivity class to show a list of values in the side spinner, using the setValues method. We can also select the second value in the list by default by invoking the setSelectedIndex method.

If you launch the application the side spinner should work as expected. The list of values is shown and the value Orange is selected by default.

6. Add Layout Attributes to the Compound View

The views available in the Android SDK can be modified through code, but some attributes can also be set directly in the corresponding layout. Let's add an attribute to the side spinner that sets the values the side spinner needs to display.

To create a custom attribute for the compound view, we first need to define the attribute in the /res/values/attr.xml file. Every attribute of the compound view should be grouped in a styleable with a <declare-styleable> tag. For the side spinner, the name of the class is used as shown below.

In the <attr> tag, the name attribute contains the identifier used to refer to the new attribute in the layout and the format attribute contains the type of the new attribute.

For the list of values, the reference type is used since the attribute will refer to a list of strings defined as a resource. The value types that are normally used in layouts can be used for your custom attributes, including booleancolordimensionenumintegerfloat and string.

Here is how to define the resource for a list of strings that the values attribute of the side spinner will refer to. It must be added to the /res/values/strings.xml file as shown below.

To test the new values attribute, create a side spinner view in the MainActivity layout below the existing side spinner. The attribute must be prefixed with a namespace added to the RelativeLayout, such as xmlns:sidespinner="http://schemas.android.com/apk/res-auto". This is what the final layout in /res/layout/activity_main.xml should look like.

Finally, the SideSpinner class need to be modified to read the values attribute. The value of every attribute of the view is available in the AttributeSet object that is passed in as a parameter of the view's constructor.

To get the value of your custom values attribute, we first call the obtainStyledAttributes method of the AttributeSet object with the name of the styleable containing the attribute. This returns the list of attributes for that styleable as a TypedArray object.

We then call the getter method of the TypedArray object that has the right type for the attribute you want, passing the identifier of the attribute as a parameter. The following code block shows how to modify the constructor of the side spinner to get the list of values and set them in the side spinner.

If you launch the application, you should see two side spinners that work independently from one another.

7. Save and Restore State

The last step we need to complete is saving and restoring the state of the compound view. When an activity is destroyed and recreated, for example, when the device is rotated, the values of native views with a unique identifier are automatically saved and restored. This currently isn't true for the side spinner.

The state of the views isn't saved. The identifiers of the views in the SideSpinner class are not unique since it can be reused many times. This means that we are responsible for saving and restoring the values of the views in the compound view. We do this by implementing the onSaveInstanceState, onRestoreInstanceState, and dispatchSaveInstanceState methods. The following code block shows how to do this for the side spinner.

Conclusion

The side spinner is now complete. Both side spinners work as expected and their values are restored if the activity is destroyed and recreated. You can now apply what you've learned to reuse any group of views in an Android application by using compound views.

Tags:

Comments

Related Articles