Android Tutorial for Beginners: Part 3
An Android Tutorial that shows you how to make your first app app step-by-step, using Android Studio! By Darryl Bayliss.
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
Android Tutorial for Beginners: Part 3
55 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?
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 everything inside the Method 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. You will still see something happening though, 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
, then click OK.
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. Make sure that the references don’t get circular (two attributes that depend on each other), or else your XML won’t inflate!Adapting JSON for a ListView
In Part Two, you made a ListView
, at which point I mentioned that ListView
s are a bit picky. They don’t want to deal with the data directly — you can hardly blame them after seeing that confusing JSON response that popped into LogCat earlier. The simple, built-in adapter you used in Part Two won’t cut it here; you need a custom one.
Right-click on the com.example.omgandroid folder (or whatever package name you used when creating the project) and select New > Java Class.
Then type in JSONAdapter as the new class name, make sure the Kind is set to Class and then hit OK.
Once you have your new class open in the editor, add the following code so the class looks like this:
public class JSONAdapter {
private static final String IMAGE_URL_BASE = "http://covers.openlibrary.org/b/id/";
Context mContext;
LayoutInflater mInflater;
JSONArray mJsonArray;
public JSONAdapter(Context context, LayoutInflater inflater) {
mContext = context;
mInflater = inflater;
mJsonArray = new JSONArray();
}
}
This is still just a basic class, beginning with the first part of the URL you’ll use to download images — more on that when you implement the image download code.
Next, there are three simple variables:
- A
Context
. Your old friend Context. Remember Context is the object that lets other objects know what is happening in your App. Here Context is going to help Picasso, the image downloader Library, display the images you download in your App - A
LayoutInflater
. You need this to inflate aView
out of that list item XML you just wrote. - A
JSONArray
. This is the datasource that will be coming in from the server in response to your query!
The JSONAdapter
method is the class constructor – that’s what you call when you create a new instance of JSONAdapter
. So, anyone who wants to ask JSONAdapter
to do anything has got to create an instance of it first, which in turn requires submitting the Context
and LayoutInflater
via the constructor.
The constructor currently simply saves the passed in references and creates an empty JSONArray
. You’ll pass the real data to the class after the search results are in.
Now you need to convert this class into an actual Adapter
class. This is quite easy in an object-oriented programming language like Java – simply change the top line of the class from:
public class JSONAdapter {
To:
public class JSONAdapter extends BaseAdapter {
JSONAdapter
is going to build on the basics provided by the BaseAdapter
class.Right away, Android Studio will underline the line you just modified in red to let you know that you need to add more to JSONAdapter
before it accurately extends BaseAdapter
. Studio isn’t just a naysayer, though — it can help, too! Click the underlined line, then click the red light bulb that pops up next to it, and then select Implement Methods from the menu.
When asked to select methods to implement, make sure all four methods are highlighted and click OK.
Magically, Android Studio creates four methods for you and the red underlining disappears. This means that you’ve satisfactorily extended BaseAdapter
.
But… all the methods are empty. It’s time to go through each one in turn and make them do what you want.
So, first replace the current implementation for getCount
with the following:
@Override
public int getCount() {
return mJsonArray.length();
}
getCount
answers the question: How long does your ListView
need to be? In this example, the answer is simply the length of your JSONArray
. Each entry in that array represents a book and so each one gets a row in the ListView
.
Now replace getItem
with this version:
@Override
public JSONObject getItem(int position) {
return mJsonArray.optJSONObject(position);
}
getItem
returns the book for a given position, counting up from 0. A single book is represented by a JSONObject
. They all just happen to be stored in a JSONArray
. So all you have to do is complete a lookup on the array for the JSONObject
at the given position.
Next, replace the stub for getItemId
with this code:
@Override
public long getItemId(int position) {
// your particular dataset uses String IDs
// but you have to put something in this method
return position;
}
This can be a very helpful method in some situations, but in this case, you don’t really need it. So, you just set it to position
. Imagine a situation where you have a list of books, as a subset of a larger database, and each book has an ID in the larger database. If you needed to go back and query for more information based on a certain item’s ID number, this method would be helpful for you.