Rotten Tomatoes Walkthrough
Goals for Today
- Creating a new project in Android Studio
- Basics of building views and layouts
- RelativeLayout vs. LinearLayout
- Setup click events on views
- Displaying items in a list
- Handling clicks on items within a list
- Navigating between screen
Scoping the App
- Display stream/detail view of movies.
- Fetch from the network a list of movies
- Clicking on any item in the list will show the more detailed info.
Create the Layout
- UI vs XML Editor
- RelativeLayout vs. LinearLayout
Creating List
- Create a BoxOfficeMovie class (title, poster URL, critics score)
- Create an ArrayList of Movies
- Get a handle to a ListView
- Attach adapter to ListView
Create a BoxOfficeMovie Class
public class BoxOfficeMovie {
public String title;
public String posterUrl;
public float criticsScore;
public String getScore() {
return String.valueOf(criticsScore) + "%";
}
}
Build a fake list of movies
public static ArrayList<BoxOfficeMovie> getFakeMovies() {
ArrayList<BoxOfficeMovie> movies = new ArrayList<>();
movies.add(new BoxOfficeMovie("Mission: Impossible", "http://bit.ly/1eWVWvz", 93.0f));
movies.add(new BoxOfficeMovie("Ant-Man", "http://bit.ly/1NdWEjS", 80.0f));
movies.add(new BoxOfficeMovie("Minions", "http://bit.ly/1J0HaSF", 54.0f));
}
(We'll deal with networking later!)
Create Adapter
public class BoxOfficeMoviesAdapter extends ArrayAdapter<BoxOfficeMovie> {
// ...
// Translates a particular `BoxOfficeMovie` given a position
// into a relevant row within an AdapterView
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// Get the data item for this position
BoxOfficeMovie movie = getItem(position);
// Check if an existing view is being reused, otherwise inflate the view
if (convertView == null) {
LayoutInflater inflater = LayoutInflater.from(getContext());
convertView = inflater.inflate(R.layout.item_box_office_movie, parent, false);
}
// Lookup views within item layout
TextView tvTitle = (TextView) convertView.findViewById(R.id.tvTitle);
TextView tvCriticsScore = (TextView) convertView.findViewById(R.id.tvCriticsScore);
// Return the completed view to render on screen
return convertView;
}
// ...
}
Connect everything up
public class BoxOfficeActivity extends Activity {
private ListView lvMovies;
private BoxOfficeMoviesAdapter adapterMovies;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_box_office);
lvMovies = (ListView) findViewById(R.id.lvMovies);
ArrayList<BoxOfficeMovie> aMovies = new ArrayList<BoxOfficeMovie>();
adapterMovies = new BoxOfficeMoviesAdapter(this, aMovies);
lvMovies.setAdapter(adapterMovies);
}
}
Add Networking
<uses-permission android:name="android.permission.INTERNET" />
repositories {
jcenter()
}
dependencies {
// ...
compile 'com.squareup.picasso:picasso:2.5.2'
compile 'com.loopj.android:android-async-http:1.4.6'
}
Load remote image
public class BoxOfficeMoviesAdapter extends ArrayAdapter<BoxOfficeMovie> {
// ...
// Translates a particular `BoxOfficeMovie` given a position
// into a relevant row within an AdapterView
@Override
public View getView(int position, View convertView, ViewGroup parent) {
.
.
.
ImageView ivPosterImage = (ImageView) convertView.findViewById(R.id.ivPosterImage);
Picasso.with(getContext()).load(movie.getPosterUrl()).into(ivPosterImage);
// Return the completed view to render on screen
return convertView;
}
// ...
}
Create networking client
public class RottenTomatoesClient {
private final String API_KEY = "...getkey...";
private final String API_BASE_URL = "http://api.rottentomatoes.com/api/public/v1.0/";
private AsyncHttpClient client;
public RottenTomatoesClient() {
this.client = new AsyncHttpClient();
}
private String getApiUrl(String relativeUrl) {
return API_BASE_URL + relativeUrl;
}
}
http://api.rottentomatoes.com/api/public/v1.0/lists/movies/box_office.json?apikey=[API key]
i.e. API key = 9htuhtcb4ymusd73d4z6jxcj
Add API call
public void getBoxOfficeMovies(JsonHttpResponseHandler handler) {
String url = getApiUrl("lists/movies/box_office.json");
RequestParams params = new RequestParams("apikey", API_KEY);
client.get(url, params, handler);
}
http://api.rottentomatoes.com/api/public/v1.0/lists/movies/box_office.json?apikey=[API key]
i.e. API key = 9htuhtcb4ymusd73d4z6jxcj
Process movie data
public class BoxOfficeMovie {
// ...
// Returns a BoxOfficeMovie given the expected JSON
// BoxOfficeMovie.fromJson(movieJsonDictionary)
// Stores the `title`, `year`, `synopsis`, `poster` and `criticsScore`
public static BoxOfficeMovie fromJson(JSONObject jsonObject) {
BoxOfficeMovie b = new BoxOfficeMovie();
try {
// Deserialize json into object fields
b.setTitle(jsonObject.getString("title"));
b.setPictureUrl(jsonObject.getJSONObject("posters").getString("thumbnail"));
b.setCriticsScore(jsonObject.getJSONObject("ratings").getInt("critics_score"));
} catch (JSONException e) {
e.printStackTrace();
return null;
}
// Return new object
return b;
}
// ...
}
Make API call
public class BoxOfficeActivity extends Activity {
RottenTomatoesClient client;
protected void onCreate(Bundle savedInstanceState) {
// ...
// Fetch the data remotely
fetchBoxOfficeMovies();
}
// Executes an API call to the box office endpoint, parses the results
// Converts them into an array of movie objects and adds them to the adapter
private void fetchBoxOfficeMovies() {
client = new RottenTomatoesClient();
client.getBoxOfficeMovies(new JsonHttpResponseHandler() {
@Override
public void onSuccess(int statusCode, Header[] headers, JSONObject responseBody) {
JSONArray items = null;
try {
// Get the movies json array
items = responseBody.getJSONArray("movies");
// Parse json array into array of model objects
for (int i = 0; i < items.length(); i++) {
BoxOfficeMovie movie = BoxOfficeMovie.fromJson(items.getJSONObject(i));
mBoxOfficeMovies.add(movie);
}
// Load model objects into the adapter
for (BoxOfficeMovie movie : movies) {
adapterMovies.add(movie); // add movie through the adapter
}
adapterMovies.notifyDataSetChanged();
} catch (JSONException e) {
e.printStackTrace();
}
}
});
}
}
Navigation Flows
Serializable
public class BoxOfficeMovie implements Serializable {
// ...
}
Passing data
public class BoxOfficeActivity extends Activity {
// ...
public static final String MOVIE_DETAIL_KEY = "movie";
@Override
protected void onCreate(Bundle savedInstanceState) {
// ...
setupMovieSelectedListener();
}
// ...
public void setupMovieSelectedListener() {
lvMovies.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View item, int position, long rowId) {
// Launch the detail view passing movie as an extra
Intent i = new Intent(BoxOfficeActivity.this, BoxOfficeDetailActivity.class);
i.putExtra(MOVIE_DETAIL_KEY, adapterMovies.getItem(position));
startActivity(i);
}
});
}
}
Detailed View Layout
Styling/Theming
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">#673AB7</item>
<item name="colorPrimaryDark">#512DA8</item>
<item name="colorAccent">#FF4081</item>
</style>
What's left
- RecyclerView
- Fragments
- Drawables
- Animations/Gestures
- Services
- Third-Party Libraries
Help us keep http://guides.codepath.com/android updated!
Rotten Tomatoes Walkthrough
By Roger Hu
Rotten Tomatoes Walkthrough
- 5,210