We have learned a lot in researching each of these topics. We are eager to share with you this information on custom components, presented in this and the next two chapters. We believe custom components hold the key to unlocking the full potential of the Android SDK.
We start this chapter by covering the custom views. This chapter also forms the basis for the next two chapters: Compound views/controls and Custom layouts.
Planning a Custom View
Before we explain the implementation of a custom view like the CircleView , let us show you the expected look, feel, and behavior of the CircleView . This, we believe, will make it easier for you to follow the subsequent explanation and code.
Lets begin by examining the CircleView in Figure , the CircleView is between two text views in a linear layout. The width of the view is set to match_parent . The height of the CircleView is set to wrap_content .
Figure 1-1.
Custom CircleView with wrap_content
When we design this CircleView , we make the circle stroke color and width configurable in the layout file using custom attributes. To test responding to events, we use click events to expand the circle and redraw. Figure shows what the CircleView would look like after a couple of clicks. Each click expands the circle by 20 percent.
Figure 1-2.
Custom CircleView expanded with clicks
We then implement state management to the CircleView so that when we flip the device to landscape, the view retains its magnification. Figure shows the rotated device with CircleView maintaining its expansion.
Figure 1-3.
Custom CircleView retaining state after rotation
Lets get started and cover all the essential things (there are a lot of them) about custom views so that you can design and code the CircleView that is shown in Figures .
Nature of Drawing in Android
To understand how to draw in Android, you have to understand the architecture of the following classes:
View
ViewParent (interface)
ViewGroup (extends View and implements ViewParent)
ViewRoot (implements ViewParent)
View is the fundamental class that all of the visible components in Android are derived from. It defines a number of callbacks to customize its behavior, like the ability to define size, draw, and save state.
A ViewParent defines the protocol for any object (including another view) that wants to play the role of a parent to other views. There are two important view parents. Of those, ViewGroup is the key one. In addition to being a ViewParent , a ViewGroup also defines the protocol for a collection of child views. All layouts like the FrameLayout and LinearLayout in the Android SDK extend this class ViewGroup . ViewGroup plays a central role in defining these layouts in XML files and in placing the controls (views) at the right place. A ViewGroup also controls the background and animation of its child views.
The other key ViewParent , the ViewRoot is implementation centric and is not a public API. In some releases it is called ViewRoot , and in some implementations it is called ViewRootImplementation and it may even be changed in the future to something else. However, this class is important for understanding how drawing is done in Android.
We advise you to keep tabs on the source code of these three classes (View, ViewGroup, ViewParent) to refer back to, should you have questions that were not answered anywhere else. For instance, if you want to look up the source code for View.java , Google that name and you will see a number of places on the Web that has this source code. The source code may not match the latest release, but for understanding what this class does, it is sufficient. I tend to download the latest android.jar source code and keep it in eclipse, then quickly locate a file in the source using CTRL-SHIFT-R ( R stands for resource).
Being a root parent of all views in the activity, the ViewRoot schedules traversals of all the views in order to first lay them out at the right place with the right size; this is called the layout phase. The ViewRoot then traverses the view hierarchy to draw them; this phase is called the drawing phase. We will talk about each of these phases now.
Layout Phase: Measurement and Layout
The goal of the layout phase is to know the position and size of each view in the view hierarchy owned by a parent such as the ViewRoot . To calculate the position and size of each view, the ViewRoot initiates a layout phase. However, in the layout phase, the view root does a traversal of only those views that reported or requested a layout change. This conditional measurement is to save resources and improve response time.
The trigger to initiate the layout phase may come from multiple events. One trigger may be the very first time everything is being drawn. Or one of the views, while reacting to an event like a click or touch, could report that its size has changed. In such an event, the view that got clicked on calls the method requestLayout ( ). This call walks up the chain and gets to the root view ( ViewRoot ). The root view then schedules a layout traversal message on the main threads queue.