CHAPTER THIRTEEN: XML and Content Apps

Chapter opener image: © Fon_nongkran/Shutterstock

Introduction

Smartphones and tablets include a browser so users can surf the Internet. However, a regular web page displayed on a small screen can be overwhelming. Content apps are apps that display selected content from a website so that the amount of information is manageable and the user experience is optimal. Such content apps exist for many sites, particularly social media websites and news sites. Often, websites offer frequently updated, consistently formatted information available to the outside world in general and app developers in particular. One such formatted information is called a Really Simple Syndication (RSS) feed. A typical RSS feed, not only is formatted in XML, but also often uses generic elements such as item, title, link, description, or pubDate. In order to build a content app for a particular site, we need to understand the formatting of that website’s RSS feed, and we need to be able to parse the XML file that is exposed by that website so that we can display its contents.

13.1 Parsing XML, DOM, and SAX Parsers, Web Content App, Version 0

An XML document is typically accompanied by a Document Type Declaration (DTD) document: it contains the rules that an XML document should adhere to. For example, it can specify that an rss element can contain 0 or more item elements. It could specify that an item element must contain exactly one title element and one link element. It could also specify that a title element must contain plain text. In this chapter, we focus on the XML only, and we do not take into account the DTD. We assume that any XML document that we parse is properly formed and complies with the rules of its DTD.

There are two types of parsers to parse an XML document:

  • DOM parsers

  • SAX parsers

A Document Object Model (DOM) parser converts an XML file into a tree. Once the XML has been converted into a tree, we can navigate the tree to modify elements, add elements, delete elements, or search for elements or values. This is similar to what a browser does with an HTML document: it builds a tree, and once that tree data structure is in memory, it can be accessed, searched, or modified using JavaScript.

Using a DOM parser is a good option if we use an XML document as a replacement for a database and we are interested in accessing and modifying that database. That is often not the case for content apps. Content apps typically convert the XML document into a list and allow the user to interact with the list. Often, that list contains links to URLs.

In order to convert an XML document into a list, we use a Simple API for XML (SAX) parser. A SAX parser builds a list as it reads the XML document. The elements of that list are objects. It is generally thought that a SAX parser is faster than a DOM parser.

For example, let’s assume that we want to parse the XML document shown in EXAMPLE 13.1.

EXAMPLE 13.1 A simple XML document

A DOM parser would convert that XML document to a tree similar to the tree shown in FIGURE 13.1. There are actually a few more branches in the tree than Figure 13.1 shows because empty strings between elements are also branches of the tree.

Instead of looking at that XML document as a tree, we can also look at it as a list of three items. In this document, each item has a title and a link. We can code an Item class with two String instance variables, title and link. A SAX parser would convert that XML document to a list of items containing three elements as shown in FIGURE 13.2.

FIGURE 13.1 A tree for the XML document in Example 13.1

FIGURE 13.2 A list of items for the XML document in Example 13.1

TABLE 13.1 The EntityResolver, DTDHandler, ContentHandler, and ErrorHandler interfaces

Interface Receives Notifications For Method Names
EntityResolver Entity-related events resolveEntity
DTDHandler DTD-related events notationDcl, unparsedEntityDcl
ContentHandler Logic-related events startDocument, endDocument, startElement, endElement, characters, and other methods
ErrorHandler Errors and warnings error, fatalError, warning

In Version 0 of our app, we concentrate on the process of XML parsing using a SAX parser. We use an existing SAX parser to parse the XML document in Example 13.1 and output feedback on the various steps of the parsing process to Logcat. In order to do this, we build two classes:

  • ▸ The SAXHandler class processes the various elements of the XML document, such as start tags, end tags, and text between tags, as they are encountered by the SAX parser.

  • ▸ The MainActivity class.

We will create a raw directory within the res directory and place a file named test.xml containing the XML document in Example 13.1 in it. Later in the chapter, we read an XML document dynamically from a URL. At this point, we do not create an Item class yet. We do that in Version 1.

The DefaultHandler class is the base class for SAX level 2 event handlers. The Default-Handler class implements four interfaces: EntityResolver, DTDHandler, ContentHandler, and ErrorHandler. It implements all the methods that it inherits from these four interfaces as do-nothing methods. TABLE 13.1 shows these four interfaces and their methods.

When parsing an XML file using a SAX parser, there are many events that can happen: for example, whenever the parser encounters a start tag, an end tag, or text, it is an event. The DefaultHandler class includes a number of methods that are automatically called when parsing a document and an event occurs. TABLE 13.2 shows some of these methods. For example, when the parser encounters a start tag, the startElement method is called. When the parser encounters an end tag, the endElement method is called. When the parser encounters text, the characters method is called. These methods are implemented as do-nothing methods. In order to handle these events, we need to subclass the DefaultHandler class and override the methods that we are interested in.

An XML document can contain entity references. An entity reference uses the special syntax &nameOfTheEntity;. For example, &quot; is an entity reference that represents a double quote ("), &lt; represents the less than sign (<), and &amp; represents the ampersand character (&). These are some of several predefined entity references. We can also add user-defined entity references in the DTD. To keep things simple, we assume that the XML documents we parse in this chapter do not contain entity references.

TABLE 13.2 Selected methods of the DefaultHandler class

Method Description
void startElement( String uri, String localName, String startElement, Attributes attributes ) Called when a start tag is encountered by the parser; uri is the namespace uri and localName its local name; startElement is the name of the start tag; attributes contains a list of (name, value) pairs that are found inside the startElement tag.
void endElement( String uri, String localName, String endElement ) Called when an end tag is encountered by the parser.
void characters( char [ ] ch, int start, int length ) Called when character data is encountered by the parser; ch stores the characters, start is the starting index, and length is the number of characters to read from ch.

TABLE 13.3 Selected parse methods of the SAXParser class

Method Description
void parse( InputStream is, DefaultHandler dh ) Parses the content of the XML input stream is and uses dh to handle events.
void parse( File f, DefaultHandler dh ) Parses the content of the XML file f and uses dh to handle events.
void parse( String uri, DefaultHandler dh ) Parses the content of the XML file located at uri and uses dh to handle events.

To parse an XML document, we call one of the parse methods of the SAXParser class. TABLE 13.3 lists some of them. They specify a DefaultHandler parameter that is used to handle the events encountered during parsing. When we override the DefaultHandler class, we need to override its methods so that we build our list of items inside these methods. EXAMPLE 13.2 shows our SAXHandler class, Version 0. We override the startElement, endElement, and characters methods, which all throw a SAXException. Inside them, we output the values of some of their parameters. The SAX related classes are in the org.xml.sax and org.xml.sax.helpers packages. These classes are imported at lines 3–5.

As shown in Table 13.1, the DefaultHandler class includes other methods, such as error or fatalError that are called when an error in the XML document is encountered. In order to keep things simple, we assume that there is no error of any kind in the XML and the DTD documents, so we do not deal with XML errors.

EXAMPLE 13.2 The SAXHandler class, Version 0

TABLE 13.4 Selected methods of the SAXParserFactory class

Method Description
SAXParserFactory newInstance( ) Static method that creates and returns a SAXParserFactory.
SAXParser newSAXParser( ) Creates and returns a SAXParser.

The SAXParser class is abstract so we cannot instantiate an object from it. The SAXParserFactory class, also abstract, provides methods to create a SAXParser object. TABLE 13.4 lists two of its methods. The static newInstance method creates a SAXParserFactory object and returns a reference to it, and the newSAXParser method creates a SAXParser object and returns a reference to it.

TABLE 13.5 The openRawResource method of the Resources class

Method Description
InputStream openRawResource( int id ) Opens and returns an InputStream to read the data of the resource whose id is id.

To create a SAXParser object, we can use the following sequence:

SAXParserFactory factory = SAXParserFactory.newInstance( );
SAXParser saxParser = factory.newSAXParser( );

In Version 0, we store a hard coded XML document in a file located in the raw directory of the res directory. Thus, if the name of our file is test.xml, we can access it using the resource R.raw.test. We can convert that resource to an input stream and use the first parse method listed in Table 13.3.

The getResources method of the Context class, inherited by the Activity class, returns a Resources reference to our application package. With that Resources reference, we can call the openRawResource method, listed in TABLE 13.5, in order to open and get a reference to an InputStream so that we can read the data in that resource.

We can chain method calls to getResources and openRawResource to open an input stream to read data from the file test.xml as follows:

InputStream is = getResources( ).openRawResource( R.raw.test );

In order to parse an InputStream named is containing an XML document with a SAXParser named saxParser that uses a DefaultHandler reference named handler to handle the events, we call the parse method as follows:

saxParser.parse( is, handler );

EXAMPLE 13.3 shows the MainActivity class. The SAXParser and SAXParserFactory classes, both part of the javax.xml.parsers package, are imported at lines 7–8. Many exceptions can be thrown by the various methods that we use:

newSAXParser throws a ParserConfigurationException and a SAXException.

openRawResource throws a Resources.NotFoundException.

parse throws an IllegalArgumentException, an IOException, and a SAXException.

Some of these exceptions are checked and some others are unchecked. To keep things simple, we use one try block at lines 14–20 and catch a generic Exception at line 20. Inside the try block, we create a SAXParser at lines 15–16 and declare and instantiate a SAXHandler named handler at line 17. We open an input stream to read the data in the test.xml file located in the raw directory of the res directory at line 18, and start parsing that input stream with handler at line 19. The parsing triggers a number of calls to the various methods inherited from DefaultHandler by SAXHandler, in particular the three methods that we overrode in Example 13.2.

EXAMPLE 13.3 The MainActivity class, Version 0

FIGURE 13.3 shows the output inside Logcat when we run the app. We observe the following:

  • ▸ Whenever a start tag is encountered, we execute inside the startElement method, and the value of the startElement parameter is the name of the tag.

  • ▸ Whenever an end tag is encountered, we execute inside the endElement method, and the value of the endElement parameter is the name of the tag.

  • ▸ Between the start and end tags of the same element, we execute inside the characters method and the array ch stores the characters between the start and end tags (also called the element contents).

  • ▸ Between the end tag of an element and the start tag of another element, we execute inside the characters method and the text only contains white space characters.

FIGURE 13.3 Logcat output of the Web Content app, Version 0

13.2 Parsing XML into a List, Web Content App, Version 1

In Version 1, we parse the test.xml file and convert it to a list. We code the Item class as part of our Model. The Item class encapsulates an item in the XML document: it has two String instance variables, title and link. In the SAXHandler class, we build an ArrayList of Item objects that reflect what we find in the XML document.

EXAMPLE 13.4 shows the Item class. It includes an overloaded constructor at lines 7–10, mutators at lines 12–13, and accessors at lines 14–15. We add a toString method at line 16, mostly to check the correctness of the parsing.

EXAMPLE 13.4 The Item class, Web Content app, Version 1

In the SAXHandler class, Version 1, shown in EXAMPLE 13.5, we build an ArrayList of Item objects. When the startElement method is called, if the tag is item, we need to instantiate a new Item object. If the tag is title or link, the text that we will read inside the characters method is the value for the title or link instance variable of the current Item object. When the endElement method is called, if the tag is item, we are done processing the current Item object and we need to add it to the list.

We need to be careful that inside the characters method, we process the correct data. There are white spaces that are typically present between tags, for example between an end tag and a start tag, in an XML document. We must be careful not to process them. The character data that we want to process is found between a start tag and the corresponding end tag. Thus, we use a state variable, validText, to assess if the character data that we process inside characters is the data that we want to process or not. When we encounter a start tag, we set validText to true. When we encounter an end tag, we set validText to false. We only process the character data in the characters method if validText is true.

EXAMPLE 13.5 The SAXHandler class, Web Content app, Version 1

We declare four instance variables at lines 9–12:

  • validText, a boolean state variable. If its value is true, we process character data inside the characters method, otherwise we do not.

  • element, a String, stores the current element (xml, rss, item, title, or link in this example).

  • currentItem, an Item, stores the current Item object.

  • items, an ArrayList, stores the list of Item objects that we build.

The constructor, at lines 14–17, initializes validText to false at line 15 and instantiates items at line 16. We code an accessor to items at line 19 so we can access it from an outside class.

In the startElement method (lines 21–28), we set validText to true at line 24 and assign startElement, the text inside the start tag, to element at line 25. If the start tag is equal to item (line 26), we instantiate a new Item object and assign its reference to currentItem at line 27.

If there is an end tag following the start tag, the characters method is called next. If currentItem is not null and validText is true and the value of element is either title or link (lines 39 and 41), we are in the middle of building the current Item object. If element is equal to title or link, we set the value of the title or link instance variable of the current Item object to the characters read (lines 40 and 42).

After the startElement and characters methods are called, the endElement method (lines 30–35) is called. We assign false to validText at line 32. In this way, any text found after an end tag and before a start tag is not processed by the characters method. If the value of element is item (line 33), we are done processing the current Item object, and we add it to items at line 34.

EXAMPLE 13.6 shows the MainActivity class, Version 1. At line 22, we retrieve the list of items that we parsed and assign it to the ArrayList items. At lines 23–24, we loop through it and output it to Logcat in order to check the correctness of the parsing.

FIGURE 13.4 shows the output inside Logcat when we run the app, Version 1. We can check that the ArrayList items contains the three Item objects that are listed in the test.xml file.

EXAMPLE 13.6 The MainActivity class, Web Content app, Version 1

FIGURE 13.4 Logcat output of the Web Content app, Version 1

13.3 Parsing a Remote XML Document, Web Content App, Version 2

In Version 2, we parse an XML document from a remote website. Instead of reading data from the test.xml file, we pull data from the rss feed for the computer science blog of Jones & Bartlett Learning. The URL is http://www.blogs.jblearning.com/computer-science/feed/. We access the Internet, open that URL, and read the data at that URL. Starting with Version 3.0, Android does not allow an app to open a URL in its main thread. A thread is a sequence of code that executes inside an existing process. The existing process is often called the main thread. For an Android app, it is also called the user-interface thread. There can be several threads executing inside the same process. Threads can share resources, such as memory.

When we start the app, our code executes in the main thread, so we need to open that URL in a different thread. Furthermore, we need the XML parsing in the secondary thread to finish before trying to fill the list with data in the main thread because we use the data read in the secondary thread for the list. The AsyncTask class, from the android.os package, allows us to perform a background operation and then access the main thread to communicate its results. It is designed to execute short tasks, lasting a few seconds or less, and should not be used to start a thread that runs for a long time. AsyncTask is abstract so we must subclass it and instantiate an object of the subclass in order to use it. Appendix D explains the AsyncTask in detail, and it also includes a very simple app using AsyncTask.

We create the ParseTask class, a subclass of the AsyncTask class, in order to parse an rss feed from a remote server. We perform the following steps:

  • ▸ We create ParseTask, a subclass of AsyncTask.

  • ▸ We override the doInBackground method so that it parses the XML document we are interested in.

  • ▸ Inside ParseTask, we include an instance variable that is a reference to the activity that starts the task. This enables us to call methods of the activity class from ParseTask.

  • ▸ We override the onPostExecute method and call methods of the activity class to update the activity with the results of the task that we just completed.

  • ▸ Inside the activity, we create and instantiate an object of ParseTask.

  • ▸ We pass a reference to this activity to the ParseTask object.

  • ▸ We call the execute method of AsyncTask, passing a URL, to start executing the task.

The AsyncTask class uses three generic types that we must specify when extending it. The class header of a subclass is as follows:

AccessModifier ClassName extends AsyncTask<Params, Progress, Result>

where Params, Progress, and Result are placeholders for actual class names and have the following meanings:

  • Params is the data type of the array that is passed to the execute method of AsyncTask when we call it.

  • Progress is the data type used for progress units as the task executes in the background. If we choose to report progress, that data type is often Integer or Long.

  • Result is the data type of the value returned upon execution of the task.

In this app, we want to be able to pass an array of Strings (representing URLs) as the argument of the execute method, we do not care about reporting progress, and the task returns an ArrayList of Item objects. Thus, we use the following class header:

public ParseTask extends AsyncTask<String, Void, ArrayList<Item>>

TABLE 13.6 Selected methods of the AsyncTask class

Method Description
AsyncTask execute( Params. . . params ) params represent an array of values of type Params, a generic type.
Result doInBackground( Params. . . params ) params is the argument passed to execute when we call it.
void onPostExecute( Result result ) Automatically called after doInBackground finishes. Result is the value returned by doInBackground.

In order to launch a task using the AsyncTask class, we do the following:

  • ▸ Create a subclass of AsyncTask.

  • ▸ Declare and instantiate an object of that subclass.

  • ▸ With that object, call the execute method to start the task.

After we call the execute method with a ParseTask reference, the doInBackground method executes and its argument is the same as the one we passed to execute. Its return value becomes the argument of onPostExecute, which is called automatically after doInBackground finishes. TABLE 13.6 shows these three methods. The execute and doInBackground method headers show that they both accept a variable number of arguments.

When we call the execute method, the following methods of the AsyncTask class execute in this order: onPreExecute, doInBackground, and onPostExecute.

Before the task executes, if we want to perform some initialization, we can place that code inside the onPreExecute method. We place the code for the task we want to execute in the doInBackground method. That method is abstract and must be overridden. The argument that is automatically passed to the doInBackground method is the same argument that we passed to the execute method when we called it. Inside doInBackground, as the task is executing, if we want to report progress to the main thread, we can call the publishProgress method. That in turn triggers a call to the onProgressUpdate method, which we can override: for example, we can update a progress bar in the user-interface thread. When the doInBackground method finishes executing, the onPostExecute method is called. We can override it to update the user interface with the results of the task that just completed. The argument that is automatically passed to the onPostExecute method is the value that is returned by the doInBackground method.

To summarize the flow of data, the argument that we pass to execute is passed to doInBackground and the returned value of doInBackground is passed as the argument of onPostExecute.

In our ParseTask class, the Params data type is String and the Result data type is ArrayList. Thus, the doInBackground method has the following method header:

protected ArrayList<Item> doInBackground( String... urls )

EXAMPLE 13.7 The ParseTask class, Web Content app, Version 2

EXAMPLE 13.7 shows the ParseTask class. In the class header (line 9), we specify String as the Params data type, Void as the Progress data type since we do not report progress in this app, and ArrayList as the Result data type.

At line 10, we declare the activity instance variable, a reference to MainActivity. The constructor (lines 12–4), accepts a MainActivity parameter and assigns it to activity. When we call that constructor from the MainActivity class, we pass this as the argument.

We override the doInBackground method at lines 16–27. The method header specifies that it returns an ArrayList of Item objects (the Result data type) and that it accepts Strings as parameters (the Params data type).

The code to parse the XML is essentially the same as in Example 13.6 except that we use a parse method whose first parameter is a String representing a URL at line 21, as opposed to an InputStream before. We expect that only one argument is passed to that method so we only process the first argument, which we access using the expression urls[0]. We do not expect that urls will be null at that point but even if it is, we catch that exception at line 23. At line 22, we return the ArrayList of Item objects generated when we parse the XML document.

We override the onPostExecute method at lines 29–31. When it is called automatically after the doInBackground method finishes execution, the value of its parameter returnedItems is the ArrayList of Item objects returned by doInBackground. We call the displayList method of the MainActivity class with the activity instance variable, passing returnedItems at line 30. Inside the MainActivity class, we need to code the displayList method.

Since our app accesses the Internet, we need to add the appropriate permission code inside the manifest element of the AndroidManifest.xml file as follows:

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

EXAMPLE 13.8 shows the MainActivity class. We declare a String constant named URL at lines 9–10 to store the URL of the RSS feed. At line 15, we declare and instantiate a ParseTask object and call the execute method at line 16, passing URL. Since the execute method accepts a variable number of arguments, we can pass 0, 1, or more arguments. Alternatively, we could have passed an array of Strings containing only one element, URL, as follows:

task.execute(new String[ ] { URL } );

EXAMPLE 13.8 The MainActivity class, Web Content app, Version 2

FIGURE 13.5 Logcat output of the Web Content app, Version 2

At lines 19–24, we code the displayList method. At this point, it outputs the contents of its parameter items to Logcat. Items could be null so we test for it at line 20 before the for loop.

Note that the sequencing of our code is important when we use an AsyncTask. After the call to execute at line 16, execution would continue inside the onCreate method if there were statements after line 16. In that case, there would be interleaved execution between those statements and the statements executing inside the ParseTask class. If we attempted to retrieve the ArrayList of Item objects right after line 16 inside the onCreate method, it would very likely be null. The correct way to process the result of a task is to do it inside the onPostExecute method.

FIGURE 13.5 shows a partial output inside Logcat when we run the app, Version 2. Note that this is a live blog, therefore contents may change daily and may not match the output in the figure.

13.4 Displaying the Results in a ListView, Web Content App, Version 3

In Versions 0, 1, and 2, we do not have any user interface. In Version 3, we display a list of all the titles retrieved from the XML document on the screen.

In order to keep this example simple, we only allow our app to run in vertical position. Thus, we add the following to the AndroidManifest.xml file inside the activity element:

android:screenOrientation="portrait"

To display the titles as a list on the screen, we use a ListView element in the activity_main.xml file. A ListView contains an arbitrary number of Strings. We do not have to specify how many elements when we define the ListView. Thus, we can build an ArrayList of Item objects dynamically and place the corresponding list of titles inside the ListView programmatically.

EXAMPLE 13.9 shows the activity_main.xml file, Version 3. We replace the TextView that displays Hello World! in the skeleton file with a ListView element at lines 12–16. We give it an id at line 12 so that we can access it by code using the findViewById method and populate it with data. We use the android:divider and the android:dividerHeight attributes at lines 15–16 to add a red, 2 pixels thick dividing line between the items displayed.

EXAMPLE 13.9 The activity_main.xml file, Web Content app, Version 3

EXAMPLE 13.10 shows the ParseTask class, Version 3. At lines 24–25, we replace the output statement to Logcat at line 24 of Example 13.7 with a Toast statement. Along with the statement importing Toast at line 4, these are the only changes.

EXAMPLE 13.11 shows the MainActivity class, Version 3. We include a ListView instance variable, listView, at line 13.

Inside the displayList method (lines 23–36), we populate listView with all the titles from the ArrayList items. We generate titles, an ArrayList of Strings containing the titles in items at lines 25–28. At lines 30–31, we create an ArrayAdapter containing titles. We use the constructor listed in TABLE 13.7, passing three arguments. The first argument is this, a reference to this activity (Activity inherits from Context). The second argument is android.R.layout.simple_list_item_1, a constant of the R.layout class. It is a simple layout that we can use to display some text. The third argument is titles, an ArrayList and therefore a List.

EXAMPLE 13.10 The ParseTask class, Web Content app, Version 3

EXAMPLE 13.11 The MainActivity class, Web Content app, Version 3

TABLE 13-7 Selected constructor of the ArrayAdapter class

Constructor Description
ArrayAdapter( Context context, int textViewResourceId, List<T> objects ) Context is the current context; textViewResourceId is the id of a layout resource containing a TextView; objects is a list of objects of type T.

We retrieve the ListView defined in activity_main.xml using its id at line 18 and assign it to listView. At line 32, we assign the list of titles as the list to display inside listView, calling the setAdapter method of the ListView class, shown in TABLE 13.8. The adapter reference is actually an ArrayAdapter and therefore a ListAdapter because ArrayAdapter inherits from the ListAdapter interface. ListAdapter is a bridge between a ListView and its list of data.

TABLE 13.8 The setAdapter method of the ListView class

Method Description
void setAdapter( ListAdapter adapter ) Sets the ListAdapter of this ListView to adapter; adapter stores the list of data backing this ListView and produces a View for each item in the list of data.

At this point, we do not set up any list event handling; we do that in Version 4. If items is null, we show a Toast at lines 34–35.

FIGURE 13.6 shows the app, Version 3, running inside the emulator.

FIGURE 13.6 The Web Content app, Version 3, running inside the emulator

13.5 Opening a Web Browser Inside the App, Web Content App, Version 4

In Version 4, we open the browser at the URL chosen by the user from the list. In order to do that, we do the following:

  • ▸ Implement event handling when the user selects an item from the list.

  • ▸ Retrieve the link associated with that item.

  • ▸ Open the browser at the URL contained in the link.

In Version 3, our list shows the titles of the ArrayList of Item objects that we generated parsing the XML document. In Version 4, we actually need to access the link values. In order to retrieve the link for a given Item object, we add an instance variable to the MainActivity class that represents the list of Item objects. When the user selects an item from the list, we retrieve its index, retrieve the Item object from the ArrayList at that index, and retrieve the value of the link instance variable of that Item.

EXAMPLE 13.12 shows the MainActivity class, Version 4. At line 18, we declare the listItems instance variable, an ArrayList of Item objects. Inside displayList, at line 29, we assign its items parameter to listItems.

EXAMPLE 13.12 The MainActivity class, Web Content app, Version 4

OnItemClickListener is a public inner interface of the AdapterView class. It includes one abstract callback method, onItemClick. That method is automatically called when the user selects an item from a list associated with an AdapterView, provided that an OnItemClickListener object is listening for events on that list. TABLE 13.9 shows the onItemClick method.

In order to implement list event handling, we do the following:

  • ▸ We code a private handler class that extends OnItemClickListener at lines 47–56, and override its only abstract method, onItemClick, at lines 49–55.

  • ▸ We declare and instantiate an object of that class at line 40.

  • ▸ We register that handler on the ListView at line 41 so that it listens to list events.

TABLE 13.9 The OnItemClickListener interface

Method Description
void onItemClick( AdapterView<?> parent, View view, int position, int id ) This method is called when an item is selected in a list: parent is the AdapterView; view is the view within the AdapterView that was selected; position is the position of view within the AdapterView; id is the row index of the item selected.

TABLE 13.10 The parse method of the Uri class

Method Description
static Uri parse( String uriString ) Creates a Uri that parses uriString and returns a reference to it.

The parameter position of the onItemClick method stores the index of the item in the list that the user selected. We use it to retrieve the Item object in listItems at that index at line 51. At line 52, we use the static parse method of the Uri class, shown in TABLE 13.10, to create a Uri object with the String stored in the link instance variable of that Item object. At line 53, we create an Intent to open the web browser at that Uri. We pass the ACTION_VIEW constant of the Intent class and the Uri object to the Intent constructor. At line 54, we start a new activity with that Intent. Since the activity is to open the browser, there is no layout file and class associated with that activity.

FIGURE 13.7 shows the app, Version 4, running inside the emulator, after the user selects an item from the list. If we click on the back button of the emulator, we go back to the list of links, which then becomes the activity at the top of the activity stack.

FIGURE 13.7 The Web Content app, Version 4, running inside the emulator, after the user selects an item from the list

Chapter Summary

  • There are two types of XML parsers: DOM and SAX.

  • A DOM parser converts an XML document into a tree. A SAX parser reads the XML document sequentially and reports what it finds. We can use it to convert the XML document to a list of objects.

  • We can use the SAXParserFactory class to get a SAXParser reference.

  • The SAXParser class contains several methods to parse an XML document.

  • We can extend the DefaultHandler class to parse an XML document using a SAX parser.

  • The DefaultHandler class includes callback methods that are automatically called when parsing an XML document. For example, the startElement method is called when the parser encounters a start tag.

  • The latest versions of Android do not allow us to open a URL on the app’s main thread. We need to do that on a separate thread.

  • We can extend the AsyncTask class to provide threading functionality.

  • A call to the execute method of the AsyncTask class triggers execution of its preExecute, doInBackground, and postExecute methods, in that order.

  • The argument passed to execute is in turn passed to doInBackground, and the return value of doInBackground is then passed as the argument of postExecute.

  • In order to access the Internet from an app, we need to include a uses-permission element for it in the AndroidManifest.xml file.

  • We can use a ListView to display a list of items.

  • To perform event handling on a ListView, we can implement the OnItemClickListener inner interface of the AdapterView class and override its onItemClick method.

  • The Uri class encapsulates a Uniform Resource Identifier.

  • We can create an intent to open the browser using the ACTION_VIEW constant of the Intent class and an Uri object.

Exercises, Problems, and Projects

Multiple-Choice Exercises

  1. What does DOM stand for?

    • Direct Object Model

    • Document Object Model

    • Document Oriented Model

    • Direct Oriented Model

  2. What does SAX stand for?

    • Super Adapter XML

    • Simple API eXtension

    • Simple API for XML

    • Simple Android for XML

  3. What method of the DefaultHandler class is called when the parser encounters a start tag?

    • startElement

    • startTag

    • start

    • tag

  4. What method of the DefaultHandler class is called when the parser encounters an end tag?

    • endElement

    • endTag

    • end

    • tag

  5. What method of the DefaultHandler class is called when the parser encounters character data?

    • data

    • characterData

    • foundCharacters

    • characters

  6. What method of the AsyncTask class do we call to start the task?

    • start

    • run

    • execute

    • startTask

  7. The argument of the method in question 7 becomes the argument of which method?

    • preExecute

    • doInBackground

    • postExecute

    • publishProgress

  8. The return value of the doInBackground method becomes the argument of which method?

    • preExecute

    • doInBackground

    • postExecute

    • publishProgress

  9. What constant of the Intent class can we use to create an intent to open the browser?

    • ACTION_BROWSER

    • ACTION_VIEW

    • WEB_VIEW

    • WEB_BROWSER

Fill in the Code

  1. This code starts parsing a file named myFile.xml located in the raw directory of the res directory, using the DefaultHandler myHandler (assume that SAXHandler extends DefaultHandler).

    SAXParserFactory factory = SAXParserFactory.newInstance( );
    SAXParser saxParser = factory.newSAXParser( );
    SAXHandler myHandler = new SAXHandler( );
    // Your code goes here
    
  2. Inside the characters method of the DefaultHandler class, assign to a String named myString the characters read by the parser.

    public void characters( char ch [ ], int start, int length )
          throws SAXException {
      String myString;
      // Your code goes here
    }
    
  3. We are coding a class named MyTask, extending AsyncTask. We expect to pass an array of Integers to the execute method, and we expect the doInBackground method to return an ArrayList of Doubles. We do not care about reporting progress. Write the class header of MyTask.

  4. We are coding a class named MyTask2, extending AsyncTask. We expect to pass a Double to the execute method, and we expect the doInBackground method to return a String. We want to report progress as an Integer. Write the class header of MyTask2.

  5. We have already coded a class named MyTask, extending AsyncTask. The doInBackground accepts a variable number of Integers as parameters. Start executing the task, passing one or more values.

    // We are inside an activity class
    MyTask myTask = new MyTask( this );
    // Your code goes here ( one statement only )?
    
  6. Write a private class that implements the OnItemClickListener interface and override its method(s) as do-nothing method(s).

  7. Inside an activity, write the code to open the browser at http://www.google.com.

Write an App

  1. Write an app similar to the app in the chapter: use a different URL and display the published date along with the title.

  2. Write an app similar to the app in the chapter: use a different URL and display only the titles that have been published within the last 30 days.

  3. Write an app similar to the app in the chapter: use a different URL and add a text field to let users specify how recent they want the data to be. For example, if a user enters 45, then only display results with a published date that is within the last 45 days.

  4. Write an app similar to the app in the chapter: use a different URL and add a front end activity with a text field to let the user specify a search term so that the titles displayed include that search term.

  5. Write an app that presents the user with a list of websites of your choice in a ListView. When the user clicks on one, the browser opens that website.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.139.81.143