This tutorial we are learning how to create an android application that will be showing Latest movies, Favorites movies and Most Popular movies in a gridview using Movie DB APIs.
The App will enable users to stream these movies trailers from Youtube.
Users can change settings(using SharedPreferences) to enable the app display either favorites movie(which users have saved), Highest rated movies from movie DB API and or Most Popular movies.
When users click on specific movie displayed in the gridview they will be taken to details screen where they will see details about the movie and also there is add to favorites button, and full image of the movie and at the bottom will show all the trailers a movie has in youtube
The following are the end result of this app.
Firstly we need to get API key from the movie DB website. You can visit the site register an account and apply for API key.
This API key is used to access the content from their database.
We are going to load data using android Retrofit Library which renders JSON data to the android device, Glide for rendering images and caching.
Lets get started,Create a new project “MovieApp” using latest android studio IDE and add the following libraries in build.gradle(Module:app)
1
|
compile 'com.squareup.retrofit2:retrofit:2.1.0'
|
1
|
compile 'com.github.bumptech.glide:glide:3.7.0'
|
1
|
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
|
1
|
compile 'com.android.support:recyclerview-v7:25.3.1'
|
1
|
compile 'com.android.support:cardview-v7:25.3.1'
|
1
|
compile 'com.google.firebase:firebase-ads:10.0.1'
|
For security reasons, we will have our API key stored in gradle.properties(Project Properties) as shown below snippet
1
|
MovieDBApiToken="xxxxxxxxxxxxxxxxxxxxxxxxx"
|
We will call the API key in build.gradle(Module:app) file which will look as follows
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
apply plugin: 'com.android.application'
android {
compileSdkVersion 25
buildToolsVersion "26.0.1"
defaultConfig {
applicationId "ke.kazutech.movieapp.movieapp"
minSdkVersion 14
targetSdkVersion 25
versionCode 2
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
multiDexEnabled true
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
buildTypes.each{
it.buildConfigField 'String','THE_MOVIE_DB_API_TOKEN',MovieDBApiToken
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
// Required -- JUnit 4 framework
testCompile 'junit:junit:4.12'
// Optional -- Mockito framework
testCompile 'org.mockito:mockito-core:1.10.19'
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.google.firebase:firebase-ads:10.0.1'
compile 'com.android.support:appcompat-v7:25.3.1'
compile 'com.android.support:design:25.3.1'
compile 'com.android.support:recyclerview-v7:25.3.1'
compile 'com.android.support:cardview-v7:25.3.1'
compile 'com.github.bumptech.glide:glide:3.7.0'
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
compile 'com.github.ivbaranov:materialfavoritebutton:0.1.4'
androidTestCompile 'com.android.support:support-annotations:25.3.1'
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
androidTestCompile 'com.android.support.test.espresso:espresso-intents:2.2.2'
androidTestCompile 'com.android.support.test:rules:0.5'
androidTestCompile 'com.android.support.test:runner:0.5'
androidTestCompile("com.android.support.test.espresso:espresso-contrib:2.2.2") {
exclude module: 'espresso-core'
exclude module: 'support-v4'
exclude module: 'recyclerview-v7'
exclude module: 'appcompat-v7'
exclude module: 'support-annotations'
exclude module: 'design'
}
}
apply plugin: 'com.google.gms.google-services'
|
Having configured properly you can add the following for activity_main.xml files.
1
2
3
4
5
6
7
8
9
10
|
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.SwipeRefreshLayout 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"
android:background="@color/white"
android:id="@+id/main_content">
<include layout="@layout/content_main"/>
</android.support.v4.widget.SwipeRefreshLayout>
|
create content_main.xml file which will contain content of activity_main.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:ads="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:background="@color/viewBg"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="ke.kazutech.movieapp.movieapp.MainActivity"
tools:showIn="@layout/activity_main"
android:layout_marginTop="@dimen/activity_vertical_margin">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="false"
android:scrollbars="vertical" />
</RelativeLayout>
|
Under Java create a new package called adapter under it create “MoviesAdapter” class and have the following codes in this class.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
|
public class MoviesAdapter extends RecyclerView.Adapter{
private Context mContext;
private List movieList;
private static final int DEFAULT_VIEW_TYPE = 1;
public int lastPosition = -1;
public MoviesAdapter(Context mContext,List movieList){
this.mContext=mContext;
this.movieList=movieList;
}
@Override
public int getItemViewType(int position) {
if (position>1 && position % 4 == 0) {
return NATIVE_AD_VIEW_TYPE;
}
return DEFAULT_VIEW_TYPE;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup,int i){
View view;
switch (i) {
default:
view = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.movie_card, viewGroup, false);
return new MyViewHolder(view);
}
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position){
if (!(holder instanceof MyViewHolder)) {
return;
}
MyViewHolder viewHolder=(MyViewHolder) holder;
Movie currentItem=movieList.get(position);
viewHolder.title.setText(currentItem.getOriginalTitle());
String vote=Double.toString(currentItem.getVoteAverage());
viewHolder.userrating.setText(vote);
String poster = "https://image.tmdb.org/t/p/w500" + currentItem.getPosterPath();
Glide.with(mContext)
.load(poster)
.placeholder(R.drawable.load)
.into(viewHolder.thumbnail);
setAnimation(viewHolder.itemView, position);
/*
RecyclerView.Adapter
final MoviesAdapter.MyViewHolder viewHolder,int i
viewHolder.title.setText(movieList.get(i).getOriginalTitle());
String vote=Double.toString(movieList.get(i).getVoteAverage());
viewHolder.userrating.setText(vote);
String poster = "https://image.tmdb.org/t/p/w500" + movieList.get(i).getPosterPath();
Glide.with(mContext)
.load(poster)
.placeholder(R.drawable.load)
.into(viewHolder.thumbnail);
setAnimation(viewHolder.itemView, i);*/
}
@Override
public int getItemCount(){
return movieList.size();
}
public void setFilter(ArrayList newsModels) {
movieList = new ArrayList<>();
movieList.addAll(newsModels);
notifyDataSetChanged();
}
private void setAnimation(View viewToAnimate, int position) {
// If the bound view wasn't previously displayed on the screen, it's animated
if (position > lastPosition) {
ScaleAnimation anim = new ScaleAnimation(0.0f, 1.0f, 0.0f, 1.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
anim.setDuration(new Random().nextInt(501));//to make duration random number between [0,501)
viewToAnimate.startAnimation(anim);
// lastPosition = position;
}
}
public class MyViewHolder extends RecyclerView.ViewHolder{
public TextView title,userrating;
public ImageView thumbnail;
public MyViewHolder(View view){
super(view);
title=(TextView) view.findViewById(R.id.title);
userrating=(TextView) view.findViewById(R.id.userrating);
thumbnail=(ImageView) view.findViewById(R.id.thumbnail);
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int pos=getAdapterPosition();
if(pos!=RecyclerView.NO_POSITION){
Movie clickedDataItem=movieList.get(pos);
Intent intent=new Intent(mContext, DetailActivity.class);
intent.putExtra("movies", clickedDataItem );
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent);
Toast.makeText(v.getContext(),"You Clicked "+clickedDataItem.getOriginalTitle(),Toast.LENGTH_LONG).show();
}
}
});
}
}
}
|
Create another package called model and in it create Movie class which implements Parcelable interface which allows serialization of objects rather fast.The following are object/String parsed under the Movie class.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
|
public class Movie implements Parcelable {
@SerializedName("poster_path")
private String posterPath;
@SerializedName("adult")
private boolean adult;
@SerializedName("overview")
private String overview;
@SerializedName("release_date")
private String releaseDate;
@SerializedName("genre_ids")
private List genreIds = new ArrayList();
@SerializedName("id")
private Integer id;
@SerializedName("original_title")
private String originalTitle;
@SerializedName("original_language")
private String originalLanguage;
@SerializedName("title")
private String title;
@SerializedName("backdrop_path")
private String backdropPath;
@SerializedName("popularity")
private Double popularity;
@SerializedName("vote_count")
private Integer voteCount;
@SerializedName("video")
private Boolean video;
@SerializedName("vote_average")
private Double voteAverage;
public Movie(String posterPath, boolean adult, String overview, String releaseDate, List genreIds, Integer id,
String originalTitle, String originalLanguage, String title, String backdropPath, Double popularity,
Integer voteCount, Boolean video, Double voteAverage) {
this.posterPath = posterPath;
this.adult = adult;
this.overview = overview;
this.releaseDate = releaseDate;
this.genreIds = genreIds;
this.id = id;
this.originalTitle = originalTitle;
this.originalLanguage = originalLanguage;
this.title = title;
this.backdropPath = backdropPath;
this.popularity = popularity;
this.voteCount = voteCount;
this.video = video;
this.voteAverage = voteAverage;
}
public Movie(){
}
public static final Comparator BY_NAME_ALPHABETICAL = new Comparator() {
@Override
public int compare(Movie movie, Movie t1) {
return movie.originalTitle.compareTo(t1.originalTitle);
}
};
public String getPosterPath() {
return posterPath;
}
public void setPosterPath(String posterPath) {
this.posterPath = posterPath;
}
public boolean isAdult() {
return adult;
}
public void setAdult(boolean adult) {
this.adult = adult;
}
public String getOverview() {
return overview;
}
public void setOverview(String overview) {
this.overview = overview;
}
public String getReleaseDate() {
return releaseDate;
}
public void setReleaseDate(String releaseDate) {
this.releaseDate = releaseDate;
}
public List getGenreIds() {
return genreIds;
}
public void setGenreIds(List genreIds) {
this.genreIds = genreIds;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getOriginalTitle() {
return originalTitle;
}
public void setOriginalTitle(String originalTitle) {
this.originalTitle = originalTitle;
}
public String getOriginalLanguage() {
return originalLanguage;
}
public void setOriginalLanguage(String originalLanguage) {
this.originalLanguage = originalLanguage;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getBackdropPath() {
return backdropPath;
}
public void setBackdropPath(String backdropPath) {
this.backdropPath = backdropPath;
}
public Double getPopularity() {
return popularity;
}
public void setPopularity(Double popularity) {
this.popularity = popularity;
}
public Integer getVoteCount() {
return voteCount;
}
public void setVoteCount(Integer voteCount) {
this.voteCount = voteCount;
}
public Boolean getVideo() {
return video;
}
public void setVideo(Boolean video) {
this.video = video;
}
public Double getVoteAverage() {
return voteAverage;
}
public void setVoteAverage(Double voteAverage) {
this.voteAverage = voteAverage;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.posterPath);
dest.writeByte(this.adult ? (byte) 1 : (byte) 0);
dest.writeString(this.overview);
dest.writeString(this.releaseDate);
dest.writeList(this.genreIds);
dest.writeValue(this.id);
dest.writeString(this.originalTitle);
dest.writeString(this.originalLanguage);
dest.writeString(this.title);
dest.writeString(this.backdropPath);
dest.writeValue(this.popularity);
dest.writeValue(this.voteCount);
dest.writeValue(this.video);
dest.writeValue(this.voteAverage);
}
protected Movie(Parcel in) {
this.posterPath = in.readString();
this.adult = in.readByte() != 0;
this.overview = in.readString();
this.releaseDate = in.readString();
this.genreIds = new ArrayList();
in.readList(this.genreIds, Integer.class.getClassLoader());
this.id = (Integer) in.readValue(Integer.class.getClassLoader());
this.originalTitle = in.readString();
this.originalLanguage = in.readString();
this.title = in.readString();
this.backdropPath = in.readString();
this.popularity = (Double) in.readValue(Double.class.getClassLoader());
this.voteCount = (Integer) in.readValue(Integer.class.getClassLoader());
this.video = (Boolean) in.readValue(Boolean.class.getClassLoader());
this.voteAverage = (Double) in.readValue(Double.class.getClassLoader());
}
public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
@Override
public Movie createFromParcel(Parcel source) {
return new Movie(source);
}
@Override
public Movie[] newArray(int size) {
return new Movie[size];
}
};
}
|
Create api package and under it create Client class and Service interface this are use to fetch API keys and api url.
1
2
3
4
5
6
7
8
9
10
|
public class Client {
public static final String BASE_URL="https://api.themoviedb.org/3/";
public static Retrofit retrofit=null;
public static Retrofit getClient() {
if (retrofit == null) {
retrofit=new Retrofit.Builder().baseUrl(BASE_URL).addConverterFactory(GsonConverterFactory.create()).build();
}
return retrofit;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
|
public interface Service {
@GET("movie/popular")
Call getPopularMovies(@Query("api_key") String apiKey);
@GET("movie/top_rated")
Call getTopRatedMovies(@Query("api_key") String apiKey);
@GET("movie/{movie_id}/videos")
Call getMovieTrailer(@Path("movie_id") int id, @Query("api_key") String apiKey);
}
|
To get full source codes which include Admob Adds both interstitial and banner Add download from the link below
@ $5
sound like you know what you?re talking about! Thanks https://yong79.com/
I like the helpful info you provide in your articles. I will bookmark your weblog and check again here regularly. https://yong79.com/
With thanks! Valuable information!
With thanks! Valuable information!
With thanks! Valuable information!
I got what you mean,saved to fav, very decent site. https://yong79.com/
It?s hard to come by knowledgeable people on this subject, however, you https://yong79.com/
With thanks! Valuable information!
great