Setup Project
Add Libraries
Create Movie model
Create Movie list item
Create MovieAdapter
Setup MovieService and Retrofit
Show Movies in RecyclerView
Create MovieDetailActivity
Open MovieDetailActivity and show details when movie is tapped
Open IMDB page when globe is tapped
Create a new project
Minimum SDK 19
Empty Activity (Backwards Compatible)
Add libraries to app/build.gradle
:
compile ' com.android.support:appcompat-v7:25.3.1'
compile ' com.android.support.constraint:constraint-layout:1.0.2'
compile ' com.android.support:recyclerview-v7:25.3.1'
compile ' com.android.support:design:25.3.1'
compile ' com.squareup.retrofit2:retrofit:2.2.0'
compile ' com.squareup.retrofit2:converter-gson:2.2.0'
compile ' com.squareup.picasso:picasso:2.5.2'
public class Movie implements Parcelable {
public String title ;
public String year ;
public String plot ;
public String simplePlot ; // Don't show the whole plot in the list
public String rating ;
public String runtime ;
public String urlIMDB ; // Link to IMDB page
public String urlPoster ;
public String urlPosterPreview ; // Smaller icon for the list
public Movie (String title , String year , String plot , String simplePlot , String rating , String runtime , String urlIMDB , String urlPoster , String urlPosterPreview ) {
this .title = title ;
this .year = year ;
this .plot = plot ;
this .simplePlot = simplePlot ;
this .rating = rating ;
this .runtime = runtime ;
this .urlIMDB = urlIMDB ;
this .urlPoster = urlPoster ;
this .urlPosterPreview = urlPosterPreview ;
}
@ Override
public int describeContents () {
return 0 ;
}
@ Override
public void writeToParcel (Parcel dest , int flags ) {
dest .writeString (this .title );
dest .writeString (this .year );
dest .writeString (this .plot );
dest .writeString (this .simplePlot );
dest .writeString (this .rating );
dest .writeString (this .runtime );
dest .writeString (this .urlIMDB );
dest .writeString (this .urlPoster );
dest .writeString (this .urlPosterPreview );
}
protected Movie (Parcel in ) {
this .title = in .readString ();
this .year = in .readString ();
this .plot = in .readString ();
this .simplePlot = in .readString ();
this .rating = in .readString ();
this .runtime = in .readString ();
this .urlIMDB = in .readString ();
this .urlPoster = in .readString ();
this .urlPosterPreview = in .readString ();
}
public static final Parcelable .Creator <Movie > CREATOR = new Parcelable .Creator <Movie >() {
@ Override
public Movie createFromParcel (Parcel source ) {
return new Movie (source );
}
@ Override
public Movie [] newArray (int size ) {
return new Movie [size ];
}
};
}
Create list_item_movie.xml
in res/layouts
folder:
<?xml version =" 1.0" encoding =" utf-8" ?>
<android .support.constraint.ConstraintLayout xmlns : android =" http://schemas.android.com/apk/res/android"
xmlns : app =" http://schemas.android.com/apk/res-auto"
xmlns : tools =" http://schemas.android.com/tools"
android : layout_width =" match_parent"
android : layout_height =" wrap_content" >
<ImageView
android : id =" @+id/moviePoster"
android : layout_width =" 64dp"
android : layout_height =" 64dp"
android : layout_marginBottom =" 8dp"
android : layout_marginLeft =" 8dp"
android : layout_marginTop =" 8dp"
app : layout_constraintBottom_toBottomOf =" parent"
app : layout_constraintLeft_toLeftOf =" parent"
app : layout_constraintTop_toTopOf =" parent"
app : srcCompat =" @mipmap/ic_launcher" />
<TextView
android : id =" @+id/movieTitle"
android : layout_width =" 0dp"
android : layout_height =" wrap_content"
android : layout_marginLeft =" 8dp"
android : layout_marginRight =" 8dp"
android : layout_marginTop =" 8dp"
android : textSize =" 18sp"
android : textStyle =" bold"
app : layout_constraintLeft_toRightOf =" @+id/moviePoster"
app : layout_constraintRight_toRightOf =" parent"
app : layout_constraintTop_toTopOf =" parent"
tools : text =" Movie Title (Year)" />
<TextView
android : id =" @+id/moviePlot"
android : layout_width =" 0dp"
android : layout_height =" wrap_content"
android : layout_marginBottom =" 8dp"
android : layout_marginLeft =" 8dp"
android : layout_marginRight =" 8dp"
android : layout_marginTop =" 0dp"
app : layout_constraintBottom_toBottomOf =" parent"
app : layout_constraintLeft_toRightOf =" @+id/moviePoster"
app : layout_constraintRight_toRightOf =" parent"
app : layout_constraintTop_toBottomOf =" @+id/movieTitle"
app : layout_constraintVertical_bias =" 0.0"
tools : text =" Movie Plot" />
</android .support.constraint.ConstraintLayout>
Add a RecyclerView
to activity_main.xml
:
<?xml version =" 1.0" encoding =" utf-8" ?>
<android .support.constraint.ConstraintLayout xmlns : android =" http://schemas.android.com/apk/res/android"
xmlns : app =" http://schemas.android.com/apk/res-auto"
xmlns : tools =" http://schemas.android.com/tools"
android : layout_width =" match_parent"
android : layout_height =" match_parent"
tools : context =" org.equithon.equithondemo.MainActivity" >
<android .support.v7.widget.RecyclerView
android : id =" @+id/recyclerView"
android : layout_width =" 0dp"
android : layout_height =" 0dp"
android : layout_marginBottom =" 0dp"
android : layout_marginLeft =" 0dp"
android : layout_marginRight =" 0dp"
android : layout_marginTop =" 0dp"
app : layout_constraintBottom_toBottomOf =" parent"
app : layout_constraintLeft_toLeftOf =" parent"
app : layout_constraintRight_toRightOf =" parent"
app : layout_constraintTop_toTopOf =" parent"
tools : listitem =" @layout/list_item_movie" />
</android .support.constraint.ConstraintLayout>
Create MovieAdapter.java
:
package org .equithon .equithondemo ;
import android .support .v7 .widget .RecyclerView ;
import android .view .LayoutInflater ;
import android .view .View ;
import android .view .ViewGroup ;
import android .widget .ImageView ;
import android .widget .TextView ;
import com .squareup .picasso .Picasso ;
import java .util .List ;
public class MovieAdapter extends RecyclerView .Adapter <MovieAdapter .MovieHolder > {
private List <Movie > movies ;
public MovieAdapter (List <Movie > movies ) {
this .movies = movies ;
}
@ Override
public MovieHolder onCreateViewHolder (ViewGroup parent , int viewType ) {
View itemView = LayoutInflater .from (parent .getContext ()).inflate (R .layout .list_item_movie , parent , false );
return new MovieHolder (itemView );
}
@ Override
public void onBindViewHolder (MovieHolder holder , int position ) {
Movie movie = movies .get (position );
holder .bind (movie );
}
@ Override
public int getItemCount () {
return movies .size ();
}
class MovieHolder extends RecyclerView .ViewHolder {
private ImageView moviePoster ;
private TextView movieTitle ;
private TextView moviePlot ;
public MovieHolder (View itemView ) {
super (itemView );
moviePoster = (ImageView ) itemView .findViewById (R .id .moviePoster );
movieTitle = (TextView ) itemView .findViewById (R .id .movieTitle );
moviePlot = (TextView ) itemView .findViewById (R .id .moviePlot );
}
public void bind (Movie movie ) {
movieTitle .setText (movie .title + " (" + movie .year + ")" );
moviePlot .setText (movie .simplePlot );
Picasso .with (itemView .getContext ())
.load (movie .urlPosterPreview )
.into (moviePoster );
}
}
}
Create MovieService.java
:
package org .equithon .equithondemo ;
import java .util .List ;
import retrofit2 .Call ;
import retrofit2 .http .GET ;
public interface MovieService {
@ GET ("https://gist.githubusercontent.com/drampelt/a83916c6d3c32d0732651d656ce0184d/raw/c5ca4d73034bfa9f2b2ccbf8accf6353f45d4661/movies.json" )
Call <List <Movie >> getPopularMovies ();
}
Retrofit retrofit = new Retrofit .Builder ()
.baseUrl ("https://gist.githubusercontent.com" )
.addConverterFactory (GsonConverterFactory .create ())
.build ();
MovieService movieService = retrofit .create (MovieService .class );
movieService .getPopularMovies ().enqueue (new Callback <List <Movie >>() {
@ Override
public void onResponse (Call <List <Movie >> call , Response <List <Movie >> response ) {
List <Movie > movies = response .body ();
}
@ Override
public void onFailure (Call <List <Movie >> call , Throwable t ) {
Toast .makeText (MainActivity .this , "Could not get movies!" , Toast .LENGTH_SHORT ).show ();
}
});
<uses-permission android : name =" android.permission.INTERNET" />
// Above onCreate
private RecyclerView recyclerView ;
// Below setContentView
recyclerView = (RecyclerView ) findViewById (R .id .recyclerView );
recyclerView .setLayoutManager (new LinearLayoutManager (this ));
// In onResponse
MovieAdapter movieAdapter = new MovieAdapter (movies );
recyclerView .setAdapter (movieAdapter );
Create MovieDetailActivity
with Right Click -> New -> Activity -> BasicActivity (View Changes )
Design content_movie_detail.xml
:
<?xml version =" 1.0" encoding =" utf-8" ?>
<android .support.constraint.ConstraintLayout xmlns : android =" http://schemas.android.com/apk/res/android"
xmlns : app =" http://schemas.android.com/apk/res-auto"
xmlns : tools =" http://schemas.android.com/tools"
android : layout_width =" match_parent"
android : layout_height =" match_parent"
app : layout_behavior =" @string/appbar_scrolling_view_behavior"
tools : context =" org.equithon.equithondemo.MovieDetailActivity"
tools : showIn =" @layout/activity_movie_detail" >
<ImageView
android : id =" @+id/moviePoster"
android : layout_width =" 128dp"
android : layout_height =" 128dp"
android : layout_marginLeft =" 16dp"
android : layout_marginTop =" 16dp"
app : layout_constraintLeft_toLeftOf =" parent"
app : layout_constraintTop_toTopOf =" parent"
app : srcCompat =" @mipmap/ic_launcher" />
<TextView
android : id =" @+id/movieTitle"
android : layout_width =" 0dp"
android : layout_height =" wrap_content"
android : layout_marginLeft =" 16dp"
android : layout_marginRight =" 16dp"
android : layout_marginTop =" 16dp"
android : textSize =" 18sp"
android : textStyle =" bold"
app : layout_constraintLeft_toRightOf =" @+id/moviePoster"
app : layout_constraintRight_toRightOf =" parent"
app : layout_constraintTop_toTopOf =" parent"
tools : text =" Movie Title" />
<TextView
android : id =" @+id/movieYear"
android : layout_width =" 0dp"
android : layout_height =" wrap_content"
android : layout_marginLeft =" 16dp"
android : layout_marginRight =" 16dp"
android : layout_marginTop =" 8dp"
app : layout_constraintLeft_toRightOf =" @+id/moviePoster"
app : layout_constraintRight_toRightOf =" parent"
app : layout_constraintTop_toBottomOf =" @+id/movieTitle"
tools : text =" Release Year: 2017"
app : layout_constraintHorizontal_bias =" 0.0" />
<TextView
android : id =" @+id/movieDuration"
android : layout_width =" 0dp"
android : layout_height =" wrap_content"
android : layout_marginLeft =" 16dp"
android : layout_marginRight =" 16dp"
android : layout_marginTop =" 8dp"
app : layout_constraintLeft_toRightOf =" @+id/moviePoster"
app : layout_constraintRight_toRightOf =" parent"
app : layout_constraintTop_toBottomOf =" @+id/movieYear"
tools : text =" Duration: 120m"
app : layout_constraintHorizontal_bias =" 0.0" />
<TextView
android : id =" @+id/movieRating"
android : layout_width =" 0dp"
android : layout_height =" wrap_content"
android : layout_marginLeft =" 16dp"
android : layout_marginRight =" 16dp"
android : layout_marginTop =" 8dp"
app : layout_constraintLeft_toRightOf =" @+id/moviePoster"
app : layout_constraintRight_toRightOf =" parent"
app : layout_constraintTop_toBottomOf =" @+id/movieDuration"
tools : text =" Rating: 8/10" />
<TextView
android : id =" @+id/moviePlot"
android : layout_width =" 0dp"
android : layout_height =" 0dp"
android : layout_marginBottom =" 16dp"
android : layout_marginLeft =" 16dp"
android : layout_marginRight =" 16dp"
android : layout_marginTop =" 16dp"
app : layout_constraintBottom_toBottomOf =" parent"
app : layout_constraintLeft_toLeftOf =" parent"
app : layout_constraintRight_toRightOf =" parent"
app : layout_constraintTop_toBottomOf =" @+id/moviePoster"
tools : text =" Movie Plot" />
</android .support.constraint.ConstraintLayout>
Step 9 - Open MovieDetailActivity when movie is tapped (View Changes )
// Below Picasso.with()...
itemView .setOnClickListener (new View .OnClickListener () {
@ Override
public void onClick (View v ) {
Intent intent = new Intent (itemView .getContext (), MovieDetailActivity .class );
intent .putExtra ("movie" , movie );
itemView .getContext ().startActivity (intent );
}
});
// Before onCreate
private ImageView moviePoster ;
private TextView movieTitle ;
private TextView movieYear ;
private TextView movieDuration ;
private TextView movieRating ;
private TextView moviePlot ;
// Below setSupportActionBar in onCreate
moviePoster = (ImageView ) findViewById (R .id .moviePoster );
movieTitle = (TextView ) findViewById (R .id .movieTitle );
movieYear = (TextView ) findViewById (R .id .movieYear );
movieDuration = (TextView ) findViewById (R .id .movieDuration );
movieRating = (TextView ) findViewById (R .id .movieRating );
moviePlot = (TextView ) findViewById (R .id .moviePlot );
Movie movie = getIntent ().getParcelableExtra ("movie" );
movieTitle .setText (movie .title );
movieYear .setText ("Release Year: " + movie .year );
movieDuration .setText ("Duration: " + movie .runtime );
movieRating .setText ("Rating: " + movie .rating + "/10" );
moviePlot .setText (movie .plot );
Picasso .with (this )
.load (movie .urlPoster )
.into (moviePoster );
Step 10 - Open IMDB page when globe icon is tapped (View Changes )
Add globe icon from Right Click -> New -> Vector Asset, search for 'public', name it ic_globe.xml
Edit icon, change color to #FFFFFFFF
<vector android : height =" 32dp" android : viewportHeight =" 24.0"
android : viewportWidth =" 24.0" android : width =" 32dp" xmlns : android =" http://schemas.android.com/apk/res/android" >
<path android : fillColor =" #FFFFFFFF" android : pathData =" M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM11,19.93c-3.95,-0.49 -7,-3.85 -7,-7.93 0,-0.62 0.08,-1.21 0.21,-1.79L9,15v1c0,1.1 0.9,2 2,2v1.93zM17.9,17.39c-0.26,-0.81 -1,-1.39 -1.9,-1.39h-1v-3c0,-0.55 -0.45,-1 -1,-1L8,12v-2h2c0.55,0 1,-0.45 1,-1L11,7h2c1.1,0 2,-0.9 2,-2v-0.41c2.93,1.19 5,4.06 5,7.41 0,2.08 -0.8,3.97 -2.1,5.39z" />
</vector >
<android .support.design.widget.FloatingActionButton
android : id =" @+id/fab"
android : layout_width =" wrap_content"
android : layout_height =" wrap_content"
android : layout_gravity =" bottom|end"
android : layout_margin =" @dimen/fab_margin"
app : srcCompat =" @drawable/ic_globe" />
// Below testInstrumentationRunner "..."
vectorDrawables. useSupportLibrary = true
// Replace onClick content with this
Intent intent = new Intent (Intent .ACTION_VIEW );
intent .setData (Uri .parse (movie .urlIMDB ));
startActivity (intent );