Passing user-selected data to a detail activity
Video: Passing user-selected data to a detail activityMy sample application so far is using data that's imported from XML and then stored persistently in an SQLite database, and it's now capable of being displayed in either a rich display as we see here or in a pure text display. I'm going to add some new functionality now, and in fact, in the new version of the project--which is named DetailView-- the application now has a detail view. When the user touches or clicks on an item, that results in opening another activity, and right now that activity is populated with dummy data.
- Retrieving and displaying data
- Filtering and sorting data
Viewers: in countries Watching now:
Take your Android programming skills to the next level with the Android built-in framework that enables local data management in text files and SQLite-based relational databases. This course shows you how to create datacentric apps for Android devices, using SQLite, Java, and the built-in android.database packages. Author David Gassner describes how to define shared preferences, work with JSON and XML files in internal and external data stores, and create new local SQLite databases.
- Exploring local data storage options
- Creating an Android virtual device
- Starting a new project
- Defining preferences with Java and activities
- Creating and reading JSON and XML data files
- Creating a new SQLite database
- Inserting and retrieving data in the database
Passing user-selected data to a detail activity
My sample application so far is using data that's imported from XML and then stored persistently in an SQLite database, and it's now capable of being displayed in either a rich display as we see here or in a pure text display. I'm going to add some new functionality now, and in fact, in the new version of the project--which is named DetailView-- the application now has a detail view. When the user touches or clicks on an item, that results in opening another activity, and right now that activity is populated with dummy data.
I'll show you how I created that detail activity but then go on to the most important part, how to take a data object such as my tour object and prepare it so that it can be passed from one activity to another. First, let's take a look at the elements of this new functionality. In this version of the application, I've added a new layout file called tour_detail.xml. You can find it in the layout folder underneath the resources. This file uses nested LinearLayouts.
There's an ImageView control that's displaying the map and then a nested LinearLayout with an orientation of vertical that contains two text views for the title and the price. Then underneath all that, there's another text view nested within a ScrollView. This is for the description, a long bit of text on a smaller device, the ScrollView will allow the TextView to scroll up and down, so the user can see all of the text. To go along with that layout file, I have a new java class called TourDetailActivity.
This is where I've defined that dummy data. This class extends activity, and it's registered in the manifest file as a new activity. In the onCreate method, I'm populating the tour object with this dummy data and then calling the refreshDisplay method which contains all the code to display the data with the TextViews and the ImageView. This code is very similar to the code that I used to display data in the list items in the rich display. Finally, in the main activity class, I've added a new method called onListItemClick.
This method is called automatically from the list activity when the user selects an item. I'm creating a new intent object, populating it with the TourDetailActivity class and then starting the activity. So now my job is to take the selected data, the actual tour that the user selected, and pass it to the new detail activity. The first step is to get a reference to the selected tour. I can do that by using this position argument that's passed into onListItemClick.
That will tell me the position of the tour that was selected in the data collection. So above the Intent, I'll create a new tour object that I'll name tour, and I'll get its reference by calling tours.get and I'll pass in the position argument. So now I know what data I want to pass in. One way of passing the data would be to call the putExtra method of the Intent object a whole bunch of times. For example, after I create the Intent, I might call intent.putExtra, and then I would pass in a string as the key to the value, and then the actual value and I might call tour.getId, and I would call that method five times here, and then in the TourDetailActivity, I would call the getExtra method five times and put the tour object back together again.
That approach will definitely work, but a cleaner and more elegant approach is to do some work in the Tour class itself so that you can pass the entire object as a single object, and the right way to do this is to use an interface in Android called Parcelable. So let's go to the tour object that I've already defined. I'll shrink down my editor, and in my Package Explorer, I'll go to the model package and I'll open the class Tour.java.
Right now this class is following a classic data transfer object or JavaBean pattern. It has private properties and then getters and setters for each of those properties. And in addition, there's a two-string method that's being used in the pure text presentation to display the tour information. In order to make this tour object something that you can pass from one activity to the next, you need to implement the Parcelable interface. I'll go back up to the class declaration.
I'll add the implements keyword and then type par, press Ctrl+Spacebar, and select Parcelable. When I add that to the Tour declaration, I see an error on the right. I'll click on it and it tells me that I have to add unimplemented methods. I'll select that quick fix, then I'll scroll down to the bottom of the class and I see that two new methods have been created called describeContents and writeToParcel.
The describeContents method will be called by Android whenever it needs to find out what kind of special objects might be a part of this class. My class doesn't have any special objects. It's limited to primitive values and strings. I can return a value of zero and that basically says never mind. The writeToParcel method is a different story. Its job is to describe the different data elements and pass them to what's called the parcel destination and it will be called automatically whenever Android needs to get a flattened version of this object, something it can save while one activity is going away and another activity is coming to the screen.
We're getting to a point now, though, where there's going to be some significant typing. So I've added a typinghelp file to the project. I'll open that up and I'll expand it to Full Screen, and let's take a look at all of the code. First of all, there's a new default no arguments constructor method. The Tour class in its current state didn't have one of these and it's going to need it because there's going to be another version of the constructor method that receives a parcel argument.
This class is going to be called automatically whenever a tour object is being passed into an activity. The class will be instantiated and then values will be passed in, in something called the parcel. We then take those values and pass them to the private properties of the current instance of this class. Notice the order in which I'm calling these methods. That will become important in a moment. Here's that describeContents method again, and I'm only returning zero, which basically means I don't have anything other than simple values.
Here's a full implementation of the writeToParcel method. This method will be called when I'm passing data out of the activity. Remember I said to remember the order in which I was passing data in? You have to pass data in and data out in exactly the same order. So I have my data in the order of ID, title, description, price, and image here, and I'm using the same order in the writeToParcel method. That is absolutely critical. Finally, each class which implements the Parcelable interface must have a static field called CREATOR, the name of the field must be all uppercase, and it has to be of a datatype of Parcelable.Creator.
The data type of the generic declaration should match your data object. I'm using Tour in the generic declaration here and here and also when the methods create from parcel and newArray, both in the data that's being passed back and in the instantiation code within the methods. So all this code taken together will make my tour class something that can be passed from one activity to the next. As I mentioned, it's a lot of code, so I'm just going to copy it and paste it.
I'll select and copy. I'll come back to the Tour class. I'll remove the Override methods that were just created and instead paste in that code. When I paste in the code, I'll see I need to apply a few quick fixes to add import statements. So I'll move to each line that's displaying an error and press Command+1 on Mac or Ctrl+1 on Windows and select the Import quick fixes. I'll scan and make sure I don't have any other errors then I'll save that class.
Now I'll come back to the MainActivity class. I'll still use the putExtra method to pass the data into the Intent object, but now instead of an arbitrary key for the value, I must use the full package name and class name of the data object. Just as with any key value that you pass into putExtra, this will be a string. You can either pass in the entire package name or you can start with the sub-package underneath the default package of the project, and that's what I'll do.
I'll start with .model and then .tour and then I'll pass in the tour object that I just got a reference to up here. That's all I need to do. All of the difficult code is now in the Tour class, and all I have to do is get an object and pass it in and say what class it's an instance of. I'll save those changes, then I'll come to the TourDetailActivity class. I'm going to comment out the code that's creating the dummy tour object, and I'll replace it with the following code. First, I'll create a bundle object.
I'll press Ctrl+Spacebar just to make sure I've added the import for the class, and I'll name the object B and then I'll call getIntent().getExtras. Then to get the data from the bundle, I'll say tour = and then I'll call a method of the bundle object called getParcelable and I'll pass in the same string key, .model.tour, and that's it.
Once again, the code in the receiving activity is incredibly simple, all the hard stuff is in the Tour class and I'm ready to test. I'll run the application in the emulator again, and I'll click into an item and this time I see the actual data of the selected item. Remember, I mentioned that I'd be able to scroll up and down on a smaller device so I could see all of the text in the description. I'll go back a level to the My List Activity.
I'll scroll a bit and I'll select an item with a custom map, the Channel Islands Excursion, and I see that my Detail Activity correctly selects the right image as well. And again, I'll scroll up and down and show that it's all working correctly. So the most important new lesson in this exercise is how to take a class that's designed to contain data and turn it into something that's Parcelable. That is something that can be passed from one activity to the next to make it easy to move data around your Android application.
There are currently no FAQs about Android SDK: Local Data Storage.