Creating an Activity Class

Article from

Unlocking Android
A Developer's Guide W. Frank Ableson, Charlie Collins, and Robi Sen MEAP Release: March 2008 Softbound print: March 2009 (est.) | 435 pages ISBN: 1933988673

This article is taken from the forthcoming book Unlocking Android: A Developer’s Guide from Manning Publications. A big part of the Android platform revolves around the UI and the concepts of activities and views. This article explains those concepts and provides practical examples to show how they are created. For the book’s table of contents, the Author Forum, and other resources, go to http://manning.com/ableson/. An Activity is basically analogous to a screen. So to create a screen we will be extending the android.app.Activity base class and implementing several key methods it defines. Listing 1 shows the first portion of the ReviewCriteria class, which is used to collect the user’s impression of a restaurant.

Listing 1 The first half of the ReviewCriteria Activity class
public class ReviewCriteria extends Activity { #1

private static final int MENU_GET_REVIEWS = Menu.FIRST; private EditText location; private Spinner cuisine; private Button getReviews; #|2 #|2 #|2 #3

@Override public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.setContentView(R.layout.review_criteria); #4

this.location = (EditText) this.findViewById(R.id.location); #|5 this.cuisine = (Spinner) this.findViewById(R.id.cuisine); #|5 this.getReviews = (Button) this.findViewById(R.id.get_reviews_button); #|5 ArrayAdapter<String> cuisines = new ArrayAdapter<String>(this, R.layout.spinner_view, this.getResources().getStringArray(R.array.cuisines)); cuisines.setDropDownViewResource(R.layout.spinner_view_dropdown);

#6 #7

this.cuisine.setAdapter(cuisines); getReviews.setOnClickListener(new OnClickListener() { public void onClick(View v) { ReviewCriteria.this.handleGetReviews(); } });

#8 #9

} 1. 2. 3. 4. 5. 6. 7. 8. 9.

Extend android.app.Activity Define Views to use within Activity Override the Activity onCreate() method Define the layout with setContentView Inflate views from XML Define ArrayAdapter instances to bind data, with Context, View, and Array Set the View for the dropdown Associate View and Data using Adapter Add an OnClickListener for our Button

The ReviewCriteria class extends android.app.Activity #1, which does a number of very important things: first, it gives our application a “context,” because Activity itself extends android.app.ApplicationContext; second it brings the Android life-cycle methods into play; third it gives the framework a hook to start and run your application; and lastly it provides a container into which View elements can be placed. Because an Activity represents an interaction with the user, in the form of a screen, it needs to provide components on the screen. This is where views come into play. In our ReviewCriteria class we have referenced three views in the code: location, rating, and getReviews #2. Location is a type of View known as an

EditText, a basic text entry component. Next, rating is a fancy select list component, known in Android terms as a Spinner. And, getReviews is a Button. View elements such as these are placed within an Activity using a particular layout to create a screen.
Layout and views can be defined directly in code, or in a layout XML “resource” file. We will learn more about views as we progress through this article.

Location as an EditText View
You may be wondering why were are using an EditText View for the “location” field in the ReviewCriteria Activity when Android includes technology that could be used to derive this value from the current physical location of the device (or allow the user to select it using a Map, rather than type it in). Good eye, but we are doing this intentionally here. We want this early example to be complete, and non trivial, but not too complicated. We will learn more about using the location support Android provides, and MapViews.

After an Activity, complete with necessary views, is started, the life-cycle takes over and the onCreate() method is invoked #3. This is one of a series of important life-cycle methods the Activity class provides access to. Every Activity will override onCreate(), where component initialization steps are invoked, though not every Activity will need to override other life-cycle methods. Once inside the onCreate() method, the setContentView() method is where you will normally associate an XML layout view file #4. We say normally, because you do not have to use an XML file at all, you can instead define all of your layout and View configuration in code, as Java objects. Nevertheless, if is often easier, and better practice by de-coupling, to use an XML layout resource for each Activity. An XML layout file defines View objects, which are laid out in a tree, and can then be “set” into the Activity for use.

At this point, all you need to know about views is that they are typically defined in XML, and then are set into the Activity and “inflated.” Views that need some run time manipulation, such as binding to data, can then be referenced in code and cast to their respective sub-types #5. Views that are static, i.e. those you don't need to interact with or update at run time, like labels, do not need to be referenced in code at all (they show up on the screen, because they are part of the View tree as defined in the XML, but need no explicit setup in code). In Figure 1, you will notice that the ReviewCriteria screen has two labels, as well as the three inputs we have already discussed. These labels are not present in the code, they are defined in the “review_criteria.xml” file that we will see coming up when we discuss XML-defined resources.

Figure 1. High level diagram of Activity, View, resources, and manifest relationship showing that activities are made up of views, and views use

The next area of our ReviewCriteria Activity is where we bind data to our select list views, the Spinner objects. Android employs a handy “adapter” concept to link views that contain collections with data. Basically an

Adapter is a collection handler that returns each item in the collection as a View. Android provides many basic adapters: ListAdapter, ArrayAdapter, GalleryAdapter, CursorAdapter, and more. And, you can also

easily create your own, but that is beyond the scope of this article. Here we are using an ArrayAdapter that is populated with our Context (this), a View element defined in an XML resource file, and an array representing the data (also defined as a resource in XML) #6. When we create the ArrayAdapter we define the View to be used for the element shown in the Spinner before it is selected. After it is selected it uses the View defined in the “dropdown” #7. After our Adapter, and its View elements are defined, we then set it into the Spinner object #8. The last thing this initial Activity demonstrates is our first explicit use of event handling. UI elements in general support many types of events. In this case we are using an OnClickListener with our Button, in order to respond when the button is clicked #9. After the onCreate() method is complete, with the binding of data to our Spinner views, we next have some menu buttons (which are different than on-screen Button views, as we shall see) and associated actions, we see how these are implemented in the last part of ReviewCriteria in listing 2.

Listing 2 The second half of the ReviewCriteria Activity class
. . . @Override public boolean onCreateOptionsMenu(final Menu menu) { #1 super.onCreateOptionsMenu(menu); menu.add(0, ReviewCriteria.MENU_GET_REVIEWS, 0, R.string.menu_get_reviews). setIcon(android.R.drawable.ic_menu_more); return true; } @Override public boolean onMenuItemSelected(final int featureId, final MenuItem item) { switch (item.getItemId()) { case MENU_GET_REVIEWS: this.handleGetReviews(); return true; } return super.onMenuItemSelected(featureId, item); } private void handleGetReviews() { if (!this.validate()) { return; } #3 #2

RestaurantFinderApplication application = (RestaurantFinderApplication) this.getApplication(); #4 application.setReviewCriteriaCuisine(this.cuisine.getSelectedItem().toString()); application.setReviewCriteriaLocation(this.location.getText().toString()); Intent intent = new Intent(Constants.INTENT_ACTION_VIEW_LIST); this.startActivity(intent); #6 #5

}

private boolean validate() { boolean valid = true; StringBuffer validationText = new StringBuffer(); if ((this.location.getText() == null) || this.location.getText().toString().equals("")) { validationText.append(this.getResources().getString(R.string.location_not_supplied _message)); valid = false; } if (!valid) { new AlertDialog.Builder(this).setTitle(this.getResources() .getString(R.string.alert_label)).setMessage( validationText.toString()).setPositiveButton("Continue", #7 new android.content.DialogInterface.OnClickListener() { #8 public void onClick(final DialogInterface dialog, final int arg1) { } }).show();

} } 1. 2. 3. 4. 5. 6. 7. 8.

validationText = null; } return valid;

Create options menu Respond when menu item is selected Create method to process getting reviews and moving to next screen Use the android.app.Application object for state Create an Intent Start an Activity Use an AlertDialog – again notice builder pattern Respond to button click with anonymous ClickListener

The menu items seen at the bottom of the Activity screens in figure 2 are all created using the onCreateOptionsMenu() method #1. Here we are using the Menu class add() method to create a single MenuItem element #1. We are passing a group ID, an ID, order, and a text resource reference to create the menu item. We are also then assigning an icon to the menu item via the setIcon method. The text and the image are externalized from the code, again using Android's concept of resources. The MenuItem we have added duplicates the on-screen Button with the same label for the “Get reviews” purpose.

Figure 3.2 RestaurantFinder application screen shots, showing three Activities: ReviewCriteria, ReviewList, and ReviewDetail.

Using the Menu versus on screen Buttons
We have chosen to use the Menu here, in addition to the on screen Button Views. Though either (or both) can work in many scenarios, you need to consider whether the menu, which is invoked by pressing the menu button on the device and then tapping a selection (button and a tap) is appropriate for what you are doing, or if an onscreen Button (single tap) is a better choice. Generally on-screen Buttons should be tied to UI elements (a search button for a search form input, for example), and menu items should be used for screen wide actions (submitting a form, performing an action like “create,” “save,” “edit,” or “delete”). Yet, because all rules need an exception, if you have the screen real estate, it may be more convenient for users to have on-screen Buttons for actions as well, as we have done here. The most important thing to keep in mind with these type UI decisions is to be consistent. If you do it one way on one screen, use that same approach on other screens within your application.

In addition to creating the menu item, we also add support to react and perform an action when the item is selected. This is done in the onMenuItemSelected() event method #2, where we parse the ID of the available menu items with a case/switch statement. When the MENU_GET_REVIEWS item is determined to have been selected, we then call the handleGetReviews method #3. This method puts the logic to save the user’s selection state in the Application object #4, and then sets up to call the next screen. We have moved this logic into its own method because we are using it from multiple places, from our on-screen Button, and again from our MenuItem. The Application object is used internally by Android for many purposes, and can be extended, as we have done with RestaurantFinderApplication (which simply includes few member variables in JavaBean style), to store global state information. We will reference this object again from other Activities to retrieve the information we are storing here. There are several ways to pass objects back and forth between Activities, using Application is one of them. You can also use Android Preferences to pass objects between activities within an application, but again these are limited to primitive and String types. Additionally, you can use the provided SQLite database, or you can implement your own ContentProvider and store data there. Lastly, an Intent can be passed The important thing to between Activities to share data – particularly data that does not need to be long-lived.

take away here is that we are using the Application object to pass state between activites. are asking another Activity to respond startActivity(Intent intent) #6. to the user's selection of a

After we store the criteria state we then fire off an action in the form of an Android Intent #5; basically we menu item by calling

Using startActivity versus startActivityForResult

The most common way to invoke an Activity is by using the simple startActivity() method, but there is also another method you will see used in specific instances – startActivityForResult(). Both of these pass control to a different Activity. The difference with regard to “For Result” is that it returns a value to the current Activity when the Activity being invoked is complete. It in effect allows you to chain Activities and expect callback responses. The result of a called Activity is obtained by a “callback” method you must implement named onActivityResult.
Also of note within the ReviewCriteria example is that we are using an AlertDialog #7. Before we allow the next Activity to be invoked, we call a simple validate() method that we have created, where we display a pop-up style alert dialog to the user if the location has not been specified. Along with generally demonstrating the use of AlertDialog, this also again demonstrates how a button can be made to respond to a click event with an

OnClickListener() #8.

The Builder Pattern
You may have noticed the usage of the Builder pattern when we added parameters to the created in the

AlertDialog we ReviewCriteria class. If you are not familiar with this approach, basically each of the methods invoked, such as AlertDialog.setMessage(), and AlertDialog.setTitle() returns a reference to itself (this), which means we can continue chaining method calls. This avoids either an extra long constructor
with many parameters, or the repetition of the class reference throughout the code. Intents make use of this handy pattern too; it is something you will see time and time again in Android.

We have covered a good deal of material and have completed our first Activity – ReviewCriteria! This class is now fully implemented.

Sign up to vote on this title
UsefulNot useful