Make Your First Android App: Part 3/3
Build upon the foundations of the first two part on how to make your first Android app and create a book search application using web APIs. By Matt Luedke.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Make Your First Android App: Part 3/3
50 mins
- Getting Started
- Networking Considerations
- A Glance at Gradle
- JSON Basics
- Creating a Query
- Making the API Call
- Creating the List Rows
- Adapting JSON for a ListView
- Putting Together the Insta-Row
- Connecting the List to the Adapter
- Updating the List Data
- Showing Progress
- The Detail Activity
- The Up and Back Buttons
- An Intent to Show the Detail Activity
- Sharing the Image
- Where to Go From Here?
JSON Basics
Great! Now it’s time to meet your datasource: the Open Library API. It’s a constantly-updated database of books, searchable by author and title. The wealth of data is enormous!
Try this query as an example: http://openlibrary.org/search.json?q=hunger+games+suzanne+collins
This is a relatively simple URL to understand; whatever is typed in after the ?q=
is the query string that will be used to search the database. Feel free to change it to a different author name/title and compare results, remembering to use +
to separate words.
The result is in the form of a large JSON response. Take a minute to look around at the sort of data the response includes.
If you’re not familiar with the JSON format, I suggest a quick glance through the JSON page. The basics are that there are two ways data can be arranged in JSON: arrays and objects.
JSONArrays
list objects of the same type. For example, a list of books might look like this:
["The Hunger Games", "Harry Potter and the Sorcerer's Stone", "A Game Of Thrones"]
["The Hunger Games", "Harry Potter and the Sorcerer's Stone", "A Game Of Thrones"]
JSONObjects
are a collection of key-value pairs. For example, a very simple object describing a book might be:
{"title" : "The Hunger Games", "author_name" : "Suzanne Collins"}
{"title" : "The Hunger Games", "author_name" : "Suzanne Collins"}
The results from your queries to the Open Library API are really just expansions on those two basic structures. They are larger, and nested into several levels, but the ideas remain the same.
Creating a Query
Now that you know roughly what to expect from the datasource, it’s time to set up the code to go make a sample query!
Add the following variable at the top of MainActivity.java:
private static final String QUERY_URL = "http://openlibrary.org/search.json?q=";
The above is simply a reference to the URL you’ll be calling. It’s much better to hold this URL as a static String
, so you don’t have to go searching through your code for it. Remember that you’re going to append the search string after the ?q=
.
Next, add this new method:
private void queryBooks(String searchString) {
// Prepare your search string to be put in a URL
// It might have reserved characters or something
String urlString = "";
try {
urlString = URLEncoder.encode(searchString, "UTF-8");
} catch (UnsupportedEncodingException e) {
// if this fails for some reason, let the user know why
e.printStackTrace();
Toast.makeText(this, "Error: " + e.getMessage(), Toast.LENGTH_LONG).show();
}
// Create a client to perform networking
AsyncHttpClient client = new AsyncHttpClient();
// Have the client get a JSONArray of data
// and define how to respond
client.get(QUERY_URL + urlString,
new JsonHttpResponseHandler() {
@Override
public void onSuccess(JSONObject jsonObject) {}
@Override
public void onFailure(int statusCode, Throwable throwable, JSONObject error) {}
});
}
queryBooks
handles the network call to the API. It encodes the input searchString
into URL format, then appends that to the base URL you specified at the top of the class.
Calling new AsyncHttpClient()
simply creates an instance of the HTTP client. It’s got a lot of great methods built-in, but the only one you need here is get(String url, ResponseHandlerInterface responseHandler)
.
The get
method takes in two parameters:
-
String url
is simply the URL from which you’d like to fetch data. You supply an input made of the base URL defined at the top of the class, plus the search string. -
JsonHttpResponseHandler
, which you define now, even though you don’t know whether the network call will succeed or fail, or how long it will take either way. It contains methods calledonSuccess
andonFailure
to respond to the two cases once the handler does get a response from the server.
You might have noticed that onSuccess
and onFailure
are currently method stubs with no code. Fix that by fleshing out the onSuccess
to match the following:
@Override
public void onSuccess(JSONObject jsonObject) {
// Display a "Toast" message
// to announce your success
Toast.makeText(getApplicationContext(), "Success!", Toast.LENGTH_LONG).show();
// 8. For now, just log results
Log.d("omg android", jsonObject.toString());
}
Next, implement onFailure
as follows:
@Override
public void onFailure(int statusCode, Throwable throwable, JSONObject error) {
// Display a "Toast" message
// to announce the failure
Toast.makeText(getApplicationContext(), "Error: " + statusCode + " " + throwable.getMessage(), Toast.LENGTH_LONG).show();
// Log error message
// to help solve any problems
Log.e("omg android", statusCode + " " + throwable.getMessage());
}
In both cases, you simply present a Toast
and log the results. Soon, though, the success case will get a lot more exciting.
Making the API Call
queryBooks
is a great method and all, but it still needs to be hooked up to the EditText
and Button
to complete the search capability.
Fortunately, that’s pretty simple. In MainActivity.java, find onClick
and replace the current implementation with this:
// 9. Take what was typed into the EditText and use in search
queryBooks(mainEditText.getText().toString());
Now, every time the user taps the button, this method takes the user’s input in the EditText
control and queries the Open Library for books and authors matching that string. Run your app and try it out to see what happens!
NoClassDefFoundError
upon querying. If so, not to worry — simply select Build > Rebuild Project and try again!Remember that you haven’t yet hooked up the results to anything that will display them on the screen. But open LogCat and you can see the resulting JSON spilled out whenever the API call finishes.
This is already exciting and clearly has the potential to be something cool very soon! But it’s still just a jumble of data. Your next challenge, then, is to display this data on the screen in a more organized manner.
Creating the List Rows
First, you need to set up the layout for each of the rows in your list. A simple row of text won’t cut it anymore — you need space for a thumbnail image on the left and then two rows of text for the title and author, respectively. A layout which would look something like this:
Right-click on the res/layout folder in the Studio left pane, and select New > Layout resource file.
Name your file row_book.xml with a root element of RelativeLayout
.
The new file will open in Design mode. So, switch to Text mode, as before. Now change the layout_height
attribute from this:
android:layout_height="match_parent"
To this:
android:layout_height="75dp"
You simply set the layout to have a specific height instead of matching the height of the parent container.
Next, you’re going to add three views inside the RelativeLayout
, and then you’ll witness a few of the capabilities of this type of layout in action. First, add the thumbnail view:
<ImageView
android:id="@+id/img_thumbnail"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginLeft="25dp"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:scaleType="centerInside"/>
The ImageView
has the width and height set and there’s a little margin to the left of the picture. This is stuff you’ve seen before.
Then it gets interesting with layout_alignParentLeft
and layout_centerVertical
. These attributes are available since this ImageView
is a child of a RelativeLayout
. Because of these two attributes, the ImageView
stays tight to the left of the cell with that margin intact, and centers vertically.
The last attribute, scaleType
, specifies how you’d like the image to display within the amount of space it’s given. Especially given the unpredictable list of screen sizes you’d have to support, it’s often important to set this beforehand.
Using centerInside
means that you’ll preserve the aspect ratio of the image and that both dimensions will fit inside the given space. You might, however, have some blank space above and below the image if it’s too short, or space on the sides if it’s too thin. If you’re interested, you can read up on the various ScaleType
options.
Next, add a TextView
for the book’s title:
<TextView
android:id="@+id/text_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="25dp"
android:layout_toRightOf="@+id/img_thumbnail"
android:layout_alignTop="@+id/img_thumbnail"/>
Read through the XML first and see if you can tell what’s going on. The commands should be starting to make a bit of sense by now. The only thing that might give you pause might be the @+id/img_thumbnail
bit – but that’s just a reference to another control by ID. In this case, the ID refers to the ImageView
you added previously.
Basically, the title will sit to the right of the thumbnail, such that the top of the title will be at the same height as the top of the thumbnail. There’s some space in between the two controls, as well.
Finally, add the TextView
for the author’s name:
<TextView
android:id="@+id/text_author"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/text_title"
android:layout_alignLeft="@+id/text_title"/>
By now, these attributes should all make sense. The author’s name will be below the title, with its left side aligned to that of the title.
RelativeLayout
s often reference other children using IDs, as you saw just now. Just make sure that the references don’t get circular (two attributes that depend on each other), or else your XML won’t inflate!