Blog Tutorial Android Bagi Pemula

Sunday, February 21, 2021

Aplikasi Movie & TV Show Menggunakan API TMDB (Part 2: Implementasi)

UPDATE ! 10 Juli 2021. Penambahan repositori Github, dapat dilihat di https://github.com/DaltrayNababan/MovieTracker


Tutorial ini adalah bagian ke-2 dari Aplikasi Movie & TV Shows Menggunakan API TMDB. Bagi Anda yang ingin mengikuti tutorial ini namun belum melihat tutorial bagian 1, silahkan untuk terlebih dahulu melihatnya, Klik Disini. Tutorial tersebut mengenai langkah-langkah mendapatkan API Key TMDB.

Sekarang mari kita buat sebuah project baru dengan minimum SDK 5.0 (21) dan layout  empty activity. Pada tutorial ini, aplikasi diberi nama Movie Tracker dengan nama package com.gwnbs.movietracker

Aplikasi ini akan memiliki fitur yakni menampilkan Most Popular Movies, Now Playing Movies, Upcoming Movies, Top Rated Movies, Most Popular TV Shows, Top Rated TV Shows, On The Air TV Shows, rincian dari movie / tv show, dan pencarian movie / tv show. Berikut video singkat penampakan dari aplikasi.



Disini kita menggunakan Java 8 atau 1.8, sehingga pada build.gradle(app) kita perlu melakukan konfigurasi tambahan. Baik, setelah project Anda sudah dibuat, pertama-tama buka file build.gradle(app) untuk melakukan konfigurasi dan menambahkan beberapa library yang dibutuhkan seperti : Retrofit, Picasso dan RoundedImageView. Pastikan file gradle Anda seperti dibawah ini :

build.gradle(:app) :
plugins {
id 'com.android.application'
}

android {
compileSdkVersion 30
buildToolsVersion "29.0.3"

defaultConfig {
applicationId "com.gwnbs.movietracker"
minSdkVersion 21
targetSdkVersion 30
versionCode 1
versionName "1.0"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}

dependencies {

//Library bawaan
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'

//Library tambahan yang dibutuhkan
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.picasso:picasso:2.71828'
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
implementation 'com.makeramen:roundedimageview:2.3.0'
}




Setelah itu lakukan sync gradle untuk mulai mengunduh library. Berikutnya resouce value yaitu strings.xml, colors.xml dan themes.xml. Saya harap Anda sudah menggunakan Android Studio versi terbaru saat ini yaitu 4.1.2 agar terhindar dari kendala-kendala, karena cukup banyak perubahan pada versi terbaru ini.

Untuk versi lama, resource value themes.xml adalah styles.xml. Pada Android Studio terbaru saat Anda membuat sebuah project baru, themes.xml ada 2 tipe yaitu night dan light. Silahkan hapus saja yang tipe night karena disini kita tidak menggunakannya. Berikut ketiga resource value tersebut.

colors.xml :
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="purple_200">#FFBB86FC</color>
<color name="purple_500">#FF6200EE</color>
<color name="purple_700">#FF3700B3</color>
<color name="teal_200">#FF03DAC5</color>
<color name="teal_700">#FF018786</color>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
<color name="color_actionbar">#E6E4E4</color>
</resources>

strings.xml :
<resources>
<string name="app_name">Movie Tracker | gwnbs.com</string>
<string name="movies">Movies</string>
<string name="most_popular_movies">Most Popular Movies</string>
<string name="now_playing_movies">Now Playing Movies</string>
<string name="upcoming_movies">Upcoming Movies</string>
<string name="top_rated_movies">Top Rated Movies</string>
<string name="tv_shows">TV Shows</string>
<string name="most_popular_tv_shows">Most Popular TV Shows</string>
<string name="top_rated_tv_shows">Top Rated TV Shows</string>
<string name="on_the_air_tv_shows">On The Air TV Shows</string>
<string name="text_info">Aplikasi ini sepenuhnya menggunakan API dari TMDB (www.themoviedb.org), namun tidak didukung oleh TMDB.</string>
<string name="enter_keyword">Enter query</string>
<string name="no_results">No results</string>
<string name="something_wrong">Something wrong with connection</string>
<string name="search_movie_or_tv_show">Search Movie or TV Show</string>
</resources>

themes.xml :
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.MovieTracker" parent="Theme.MaterialComponents.Light.NoActionBar">
<!-- Primary brand color. -->
<item name="colorPrimary">#454545</item>
<item name="colorPrimaryVariant">#4A4141</item>
<item name="colorOnPrimary">@color/white</item>
<!-- Secondary brand color. -->
<item name="colorSecondary">@color/teal_200</item>
<item name="colorSecondaryVariant">@color/teal_700</item>
<item name="colorOnSecondary">@color/black</item>
<!-- Status bar color. -->
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
<item name="android:fontFamily">@font/alegreya_medium</item>
<!-- Customize your theme here. -->
</style>
</resources>


Pada themes.xml diatas, untuk item fontFamily, saya mengunduhnya dari fonts.google.com. Silahkan untuk memilih font Anda sendiri, atau Anda bisa menghapus item fontFamily pada themes.xml diatas jika Anda belum mau terlalu ribet.

Berikutnya icon drawable dan background drawable. Untuk background drawable kita memilik 5 files xml dan vector drawable kita memilik 3 icon xml. Berikut file nya.

bg_actionbar.xml :
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">

<solid android:color="@color/color_actionbar"/>

</shape>

bg_item.xml :
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/purple_700">

<item>
<shape android:shape="rectangle">
<solid android:color="#ECEAEA"/>
<corners android:radius="10dp"/>
</shape>
</item>

</ripple>

bg_oval.xml :
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/purple_500">

<item>
<shape android:shape="oval">
<stroke
android:width="1dp"
android:color="@color/black" />
<solid android:color="@color/white" />
</shape>
</item>

</ripple>

bg_text_title.xml :
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">

<solid android:color="#D3D0D0"/>
<corners android:radius="15dp"/>

</shape>

bg_view_gradient.xml :
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">

<gradient android:endColor="#FFFFFF"
android:startColor="@android:color/transparent"/>

</shape>

ic_android.xml :
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="50dp"
android:height="50dp"
android:tint="#1DA238"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M17.6,9.48l1.84,-3.18c0.16,-0.31 0.04,-0.69 -0.26,-0.85c-0.29,-0.15 -0.65,-0.06 -0.83,0.22l-1.88,3.24c-2.86,-1.21 -6.08,-1.21 -8.94,0L5.65,5.67c-0.19,-0.29 -0.58,-0.38 -0.87,-0.2C4.5,5.65 4.41,6.01 4.56,6.3L6.4,9.48C3.3,11.25 1.28,14.44 1,18h22C22.72,14.44 20.7,11.25 17.6,9.48zM7,15.25c-0.69,0 -1.25,-0.56 -1.25,-1.25c0,-0.69 0.56,-1.25 1.25,-1.25S8.25,13.31 8.25,14C8.25,14.69 7.69,15.25 7,15.25zM17,15.25c-0.69,0 -1.25,-0.56 -1.25,-1.25c0,-0.69 0.56,-1.25 1.25,-1.25s1.25,0.56 1.25,1.25C18.25,14.69 17.69,15.25 17,15.25z" />
</vector>

ic_back.xml :
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:autoMirrored="true"
android:tint="#393939"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z" />
</vector>

ic_search.xml :
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="30dp"
android:height="30dp"
android:tint="#393939"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z" />
</vector>


Tahap selanjutnya, buatlah 2 buah files java, sebuah java class dan sebuah java interface yang digunakan untuk mengirim permintaan API dari TMDB. Beri nama ApiClient.java dan ApiService.java. Berikut isinya.

ApiClient.java :
package com.gwnbs.movietracker;

import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class ApiClient {

private static Retrofit retrofit;

public static Retrofit getClient() {
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.baseUrl("https://api.themoviedb.org/3/")
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}

}

ApiService.java :
package com.gwnbs.movietracker;

import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Path;
import retrofit2.http.Query;

public interface ApiService {

//Permintaan untuk menampilkan Most Popular Movies
@GET("movie/popular")
Call<MovieRespon> getPopularMovies(@Query("api_key") String apiKey, @Query("language") String language,
@Query("page") int page);

//Permintaan untuk menampilkan Now Playing Movies
@GET("movie/now_playing")
Call<MovieRespon> getNowPlayingMovies(@Query("api_key") String apiKey, @Query("language") String language,
@Query("page") int page);

//Permintaan untuk menampilkan Upcoming Movies
@GET("movie/upcoming")
Call<MovieRespon> getUpcomingMovies(@Query("api_key") String apiKey, @Query("language") String language,
@Query("page") int page);

//Permintaan untuk menampilkan Top Rated Movies
@GET("movie/top_rated")
Call<MovieRespon> getTopRatedMovies(@Query("api_key") String apiKey, @Query("language") String language,
@Query("page") int page);

//Permintaan untuk pencarian Movie
@GET("search/movie")
Call<MovieRespon> searchMovie(@Query("api_key") String apiKey, @Query("language") String language,
@Query("query") String query, @Query("page") int page);

//Permintaan untuk menampilkan rincian / details movie
@GET("movie/{movie_id}")
Call<MovieDetails> getMovieDetails(@Path("movie_id") String id, @Query("api_key") String apiKey);

//Permintaan untuk menampilkan Most Popular TV Shows
@GET("tv/popular")
Call<TVRespon> getTvPopular(@Query("api_key") String apiKey, @Query("language") String language,
@Query("page") int page);

//Permintaan untuk menampilkan Top Rated TV Shows
@GET("tv/top_rated")
Call<TVRespon> getTvTopRated(@Query("api_key") String apiKey, @Query("language") String language,
@Query("page") int page);

//Permintaan untuk menampilkan On Air TV Shows
@GET("tv/on_the_air")
Call<TVRespon> getTvOnAir(@Query("api_key") String apiKey, @Query("language") String language,
@Query("page") int page);

//Permintaan untuk menampilkan rincian TV Show
@GET("tv/{tv_id}")
Call<TVDetails> getTvDetails(@Path("tv_id") String id, @Query("api_key") String apiKey);

//Permintaan untuk pencarian TV Shows
@GET("search/tv")
Call<TVRespon> searchTv(@Query("api_key") String apiKey, @Query("language") String language,
@Query("query") String query, @Query("page") int page);

}


Metode call-call diatas akan ditandai dengan error karena kita belum membuat class-class nya. Berikutnya buatlah beberapa java class, pertama buatlah sebuah class dengan nama ImageAdapter.java. Class ini berisi 2 metode static, digunakan untuk memuat poster dan backdrop gambar dari database TMDB ke aplikasi.

ImageAdapter.java :
package com.gwnbs.movietracker;

import android.widget.ImageView;

import com.squareup.picasso.Callback;
import com.squareup.picasso.Picasso;

public class ImageAdapter {

public static final String IMAGEBASE_URL = "https://image.tmdb.org/t/p/w220_and_h330_face";
public static final String BACKDROPIMAGE_BASE_URL = "https://image.tmdb.org/t/p/original";

public static void setPosterURL(ImageView imageView, String path) {
try {
imageView.setAlpha(0f);
Picasso.get().load(IMAGEBASE_URL + path).noFade().into(imageView, new Callback() {
@Override
public void onSuccess() {
imageView.animate().setDuration(500).alpha(1f).start();
}
@Override
public void onError(Exception e) {
}
});
} catch (Exception ignored) {
}
}

public static void setBackdropURL(ImageView imageView, String path) {
try {
imageView.setAlpha(0f);
Picasso.get().load(BACKDROPIMAGE_BASE_URL + path).noFade().into(imageView, new Callback() {
@Override
public void onSuccess() {
imageView.animate().setDuration(500).alpha(1f).start();
}
@Override
public void onError(Exception e) {
}
});
} catch (Exception ignored) {
}
}
}


Nah, sekarang buatlah 6 buah java files dengan nama : MovieRespon.java, MovieResult.java, MovieDetails.java, TVRespon.java, TVResult.java dan TVDetails.java. Class-class ini digunakan untuk getter (mendapatkan) nilai-nilai yang kita butuhkan dari database TMDB. Karena kita memuat data dengan streaming maka kita membutuhkan anotasi Serialize.

MovieRespon.java :
package com.gwnbs.movietracker;

import com.google.gson.annotations.SerializedName;

import java.util.ArrayList;
import java.util.List;

public class MovieRespon {

@SerializedName("page")
int page;

@SerializedName("results")
private final List<MovieResult> results = new ArrayList<>();

@SerializedName("total_results")
int totalResults;

@SerializedName("total_pages")
int totalPages;

public int getPage() {
return page;
}

public List<MovieResult> getResults() {
return results;
}

public int getTotalResults() {
return totalResults;
}

public int getTotalPages() {
return totalPages;
}

}

MovieResult.java :
package com.gwnbs.movietracker;

import com.google.gson.annotations.SerializedName;

public class MovieResult {

@SerializedName("id")
int id;

@SerializedName("poster_path")
String posterPath;

@SerializedName("title")
String title;

public int getId() {
return id;
}

public String getPosterPath() {
return posterPath;
}

public String getTitle() {
return title;
}
}

MovieDetails.java :
package com.gwnbs.movietracker;

import androidx.annotation.Keep;

import com.google.gson.annotations.SerializedName;

@Keep
public class MovieDetails {

@SerializedName("title")
String title;

@SerializedName("homepage")
String homepage;

@SerializedName("overview")
String overview;

@SerializedName("runtime")
String runtime;

@SerializedName("poster_path")
String posterPath;

@SerializedName("vote_average")
String voteAverage;

@SerializedName("release_date")
String releaseDate;

@SerializedName("original_language")
String language;

@SerializedName("backdrop_path")
String backdropPath;

@SerializedName("status")
String status;

@SerializedName("budget")
String budget;

@SerializedName("revenue")
String revenue;

@SerializedName("popularity")
String popularity;

@SerializedName("tagline")
String tagline;

@SerializedName("vote_count")
String voteCount;

public String getStatus() {
return status;
}

public String getHomepage() {
return homepage;
}

public String getOverview() {
return overview;
}

public String getRuntime() {
return runtime;
}

public String getPosterPath() {
return posterPath;
}

public String getVoteAverage() {
return voteAverage;
}

public String getBackdropPath() {
return backdropPath;
}

public String getReleaseDate() {
return releaseDate;
}

public String getBudget() {
return budget;
}

public String getRevenue() {
return revenue;
}

public String getPopularity() {
return popularity;
}

public String getTagline() {
return tagline;
}

public String getVoteCount() {
return voteCount;
}

public String getLanguage() {
return language;
}

public String getTitle() {
return title;
}
}

TVRespon.java :
package com.gwnbs.movietracker;

import com.google.gson.annotations.SerializedName;

import java.util.ArrayList;
import java.util.List;

public class TVRespon {

@SerializedName("page")
int page;

@SerializedName("results")
private final List<TVResult> results = new ArrayList<>();

@SerializedName("total_results")
int totalResults;

@SerializedName("total_pages")
int totalPages;

public int getPage() {
return page;
}

public List<TVResult> getResults() {
return results;
}

public int getTotalResults() {
return totalResults;
}

public int getTotalPages() {
return totalPages;
}

}

TVResult.java :
package com.gwnbs.movietracker;

import com.google.gson.annotations.SerializedName;

public class TVResult {

@SerializedName("id")
int id;

@SerializedName("poster_path")
String posterPath;

@SerializedName("original_name")
String name;

public int getId() {
return id;
}

public String getPosterPath() {
return posterPath;
}

public String getName() {
return name;
}
}

TVDetails.java :
package com.gwnbs.movietracker;

import androidx.annotation.Keep;

import com.google.gson.annotations.SerializedName;

@Keep
public class TVDetails {

@SerializedName("name")
String name;

@SerializedName("poster_path")
String posterPath;

@SerializedName("backdrop_path")
String backdropPath;

@SerializedName("episode_run_time")
int[] episodeRuntime;

@SerializedName("first_air_date")
String firstAirDate;

@SerializedName("last_air_date")
String lastAirdate;

@SerializedName("number_of_episodes")
String numberOfEpisodes;

@SerializedName("number_of_seasons")
String numberOfSeasons;

@SerializedName("original_language")
String originalLanguage;

@SerializedName("overview")
String overview;

@SerializedName("popularity")
String popularity;

@SerializedName("status")
String status;

@SerializedName("tagline")
String tagline;

@SerializedName("vote_average")
String voteAverage;

@SerializedName("vote_count")
String voteCount;

@SerializedName("homepage")
String homepage;

public int[] getEpisodeRuntime() {
return episodeRuntime;
}

public String getBackdropPath() {
return backdropPath;
}

public String getFirstAirDate() {
return firstAirDate;
}

public String getLastAirdate() {
return lastAirdate;
}

public String getName() {
return name;
}

public String getNumberOfEpisodes() {
return numberOfEpisodes;
}

public String getNumberOfSeasons() {
return numberOfSeasons;
}

public String getOriginalLanguage() {
return originalLanguage;
}

public String getOverview() {
return overview;
}

public String getPopularity() {
return popularity;
}

public String getPosterPath() {
return posterPath;
}

public String getStatus() {
return status;
}

public String getTagline() {
return tagline;
}

public String getVoteAverage() {
return voteAverage;
}

public String getVoteCount() {
return voteCount;
}

public String getHomepage() {
return homepage;
}
}


Tentu disini kita akan menggunakan RecyclerView untuk memuat item-item yang kita request dalam daftar. Disini kita akan membutuhkan 4 buah java class adapter yaitu untuk daftar movie, daftar tv show, pencarian movie dan pencarian tv show. Namun sebelumnya buatlah terlebih dahulu 2 buah layout resouce file sebagai container dari item. Beri nama item_container.xml dan item_container_search.xml. Berikut kedua files ini.

item_container.xml :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:background="@drawable/bg_item"
android:gravity="center"
android:orientation="vertical"
android:padding="3dp">

<com.makeramen.roundedimageview.RoundedImageView
android:id="@+id/imageItemPoster"
android:layout_width="85dp"
android:layout_height="120dp"
android:layout_marginTop="3dp"
android:scaleType="centerCrop"
app:riv_corner_radius="10dp" />

<TextView
android:id="@+id/textItemName"
android:layout_width="85dp"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:alpha="0.6"
android:gravity="center"
android:lines="2"
android:textColor="@color/black"
android:textSize="10sp"
tools:ignore="SmallSp" />

</LinearLayout>

item_container_search.xml :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:layout_margin="5dp"
android:gravity="center"
android:orientation="vertical"
android:padding="3dp">

<com.makeramen.roundedimageview.RoundedImageView
android:id="@+id/imageItemPoster"
android:layout_width="100dp"
android:layout_height="150dp"
android:layout_marginTop="3dp"
android:background="@drawable/bg_item"
android:scaleType="centerCrop"
app:riv_corner_radius="10dp" />

<TextView
android:id="@+id/textItemName"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:alpha="0.6"
android:gravity="center"
android:lines="2"
android:textColor="@color/black"
android:textSize="10sp"
tools:ignore="SmallSp" />

</LinearLayout>


Sekarang 4 buah file java adapter. Beri nama MovieAdapter.java, MovieSearchAdapter.java, TVAdapter.java dan TVSearchAdapter.java. Ke-4 adapter ini adalah adapter RecyclerView karena memang disini kita hanya memakai RecyclerView. Untuk class list, RecyclerView adalah yang terbaik saat ini.

MovieAdapter.java :
package com.gwnbs.movietracker;

import android.content.Context;
import android.content.Intent;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import com.makeramen.roundedimageview.RoundedImageView;

import java.util.List;

public class MovieAdapter extends RecyclerView.Adapter<MovieAdapter.MovieViewHolder> {

private final List<MovieResult> movieResults;
private final Context context;

public MovieAdapter(List<MovieResult> movieResults, Context context) {
this.movieResults = movieResults;
this.context = context;
}

@NonNull
@Override
public MovieAdapter.MovieViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new MovieViewHolder(LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_container, parent, false));
}

@Override
public void onBindViewHolder(@NonNull MovieAdapter.MovieViewHolder holder, int position) {
holder.bindItem(movieResults.get(position), context);
}

@Override
public int getItemCount() {
return movieResults.size();
}

static class MovieViewHolder extends RecyclerView.ViewHolder {

private final RoundedImageView imageItemPoster;
private final TextView textItemName;

MovieViewHolder(@NonNull View itemView) {
super(itemView);

imageItemPoster = itemView.findViewById(R.id.imageItemPoster);
textItemName = itemView.findViewById(R.id.textItemName);
}

void bindItem(MovieResult movieResult, Context context) {
if (!TextUtils.isEmpty(movieResult.getPosterPath())) {
ImageAdapter.setPosterURL(imageItemPoster, movieResult.getPosterPath());
} else {
imageItemPoster.setImageResource(R.drawable.ic_android);
}
textItemName.setText(movieResult.getTitle());

itemView.setOnClickListener(v -> {
Intent i = new Intent(context, DetailsActivity.class);
i.putExtra("tipe", "movie");
i.putExtra("id", movieResult.getId());
context.startActivity(i);
});
}
}
}

MovieSearchAdapter.java :
package com.gwnbs.movietracker;

import android.content.Context;
import android.content.Intent;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import com.makeramen.roundedimageview.RoundedImageView;

import java.util.List;

public class MovieSearchAdapter extends RecyclerView.Adapter<MovieSearchAdapter.MovieViewHolder> {

private final List<MovieResult> movieResults;
private final Context context;

public MovieSearchAdapter(List<MovieResult> movieResults, Context context) {
this.movieResults = movieResults;
this.context = context;
}

@NonNull
@Override
public MovieSearchAdapter.MovieViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new MovieViewHolder(LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_container_search, parent, false));
}

@Override
public void onBindViewHolder(@NonNull MovieSearchAdapter.MovieViewHolder holder, int position) {
holder.bindItem(movieResults.get(position), context);
}

@Override
public int getItemCount() {
return movieResults.size();
}

static class MovieViewHolder extends RecyclerView.ViewHolder {

private final RoundedImageView imageItemPoster;
private final TextView textItemName;

MovieViewHolder(@NonNull View itemView) {
super(itemView);

imageItemPoster = itemView.findViewById(R.id.imageItemPoster);
textItemName = itemView.findViewById(R.id.textItemName);
}

void bindItem(MovieResult movieResult, Context context) {
if (!TextUtils.isEmpty(movieResult.getPosterPath())) {
ImageAdapter.setPosterURL(imageItemPoster, movieResult.getPosterPath());
} else {
imageItemPoster.setImageResource(R.drawable.ic_android);
}
textItemName.setText(movieResult.getTitle());

itemView.setOnClickListener(v -> {
Intent i = new Intent(context, DetailsActivity.class);
i.putExtra("tipe", "movie");
i.putExtra("id", movieResult.getId());
context.startActivity(i);
});
}
}
}

TVAdapter.java :
package com.gwnbs.movietracker;

import android.content.Context;
import android.content.Intent;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import com.makeramen.roundedimageview.RoundedImageView;

import java.util.List;

public class TVAdapter extends RecyclerView.Adapter<TVAdapter.TvViewHolder> {

private final List<TVResult> TVResults;
private final Context context;

public TVAdapter(List<TVResult> TVResults, Context context) {
this.TVResults = TVResults;
this.context = context;
}

@NonNull
@Override
public TvViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new TvViewHolder(LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_container, parent, false));
}

@Override
public void onBindViewHolder(@NonNull TvViewHolder holder, int position) {
holder.bindItem(TVResults.get(position), context);
}

@Override
public int getItemCount() {
return TVResults.size();
}

static class TvViewHolder extends RecyclerView.ViewHolder {

private final RoundedImageView imageItemPoster;
private final TextView textItemName;

TvViewHolder(@NonNull View itemView) {
super(itemView);

imageItemPoster = itemView.findViewById(R.id.imageItemPoster);
textItemName = itemView.findViewById(R.id.textItemName);
}

void bindItem(TVResult tvResult, Context context) {
if (!TextUtils.isEmpty(tvResult.getPosterPath())) {
ImageAdapter.setPosterURL(imageItemPoster, tvResult.getPosterPath());
} else {
imageItemPoster.setImageResource(R.drawable.ic_android);
}
textItemName.setText(tvResult.getName());

itemView.setOnClickListener(v -> {
Intent i = new Intent(context, DetailsActivity.class);
i.putExtra("tipe", "tv");
i.putExtra("id", tvResult.getId());
context.startActivity(i);
});
}
}
}

TVSearchAdapter.java :
package com.gwnbs.movietracker;

import android.content.Context;
import android.content.Intent;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import com.makeramen.roundedimageview.RoundedImageView;

import java.util.List;

public class TVSearchAdapter extends RecyclerView.Adapter<TVSearchAdapter.TvViewHolder> {

private final List<TVResult> TVResults;
private final Context context;

public TVSearchAdapter(List<TVResult> TVResults, Context context) {
this.TVResults = TVResults;
this.context = context;
}

@NonNull
@Override
public TvViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new TvViewHolder(LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_container_search, parent, false));
}

@Override
public void onBindViewHolder(@NonNull TvViewHolder holder, int position) {
holder.bindItem(TVResults.get(position), context);
}

@Override
public int getItemCount() {
return TVResults.size();
}

static class TvViewHolder extends RecyclerView.ViewHolder {

private final RoundedImageView imageItemPoster;
private final TextView textItemName;

TvViewHolder(@NonNull View itemView) {
super(itemView);

imageItemPoster = itemView.findViewById(R.id.imageItemPoster);
textItemName = itemView.findViewById(R.id.textItemName);
}

void bindItem(TVResult tvResult, Context context) {
if (!TextUtils.isEmpty(tvResult.getPosterPath())) {
ImageAdapter.setPosterURL(imageItemPoster, tvResult.getPosterPath());
} else {
imageItemPoster.setImageResource(R.drawable.ic_android);
}
textItemName.setText(tvResult.getName());

itemView.setOnClickListener(v -> {
Intent i = new Intent(context, DetailsActivity.class);
i.putExtra("tipe", "tv");
i.putExtra("id", tvResult.getId());
context.startActivity(i);
});
}
}
}


Anda akan mendapatkan error untuk class DetailsActivity.java karena activity nya belum kita buat, silahkan diabaikan saja. Selanjutnya kita akan implementasi di MainActivity, tentunya pertama-tama dengan mendesain layout activity_main.xml terlebih dahulu. Supaya view di activity_main.xml tidak terlalu banyak, xml untuk movie dan tv kita pisahkan dalam resource berbeda yang nantinya kita include ke activity_main.xml.

Baik, buatlah terlebih dahulu 2 layout resource files, berikan nama layout_movie.xml dan layout_tv.xml dan berikut isinya :

layout_movie.xml :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@drawable/bg_text_title"
android:paddingStart="10dp"
android:paddingTop="5dp"
android:paddingEnd="10dp"
android:paddingBottom="5dp"
android:text="@string/movies"
android:textColor="@color/black"
android:textSize="22sp"
android:textStyle="bold" />

<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp">

<TextView
android:id="@+id/textMoviePopular"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/most_popular_movies"
android:textColor="@color/black"
android:textSize="20sp"
app:layout_constraintBottom_toTopOf="@id/rvMoviePopular"
app:layout_constraintTop_toTopOf="parent" />

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvMoviePopular"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:clipToPadding="false"
android:orientation="horizontal"
android:overScrollMode="never"
android:paddingStart="0dp"
android:paddingEnd="40dp"
android:scrollbars="horizontal"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/textMoviePopular" />

<ProgressBar
android:id="@+id/loadingMoviePopular"
android:layout_width="25dp"
android:layout_height="25dp"
android:layout_marginEnd="10dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/rvMoviePopular"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/rvMoviePopular" />

<ProgressBar
android:id="@+id/loadingMainMoviePopular"
android:layout_width="50dp"
android:layout_height="100dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/rvMoviePopular"
app:layout_constraintEnd_toEndOf="@id/rvMoviePopular"
app:layout_constraintStart_toStartOf="@id/rvMoviePopular"
app:layout_constraintTop_toTopOf="@id/rvMoviePopular" />

</androidx.constraintlayout.widget.ConstraintLayout>

<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp">

<TextView
android:id="@+id/textMovieNowPlaying"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/now_playing_movies"
android:textColor="@color/black"
android:textSize="20sp"
app:layout_constraintBottom_toTopOf="@id/rvMovieNowPlaying"
app:layout_constraintTop_toTopOf="parent" />

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvMovieNowPlaying"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:clipToPadding="false"
android:orientation="horizontal"
android:overScrollMode="never"
android:paddingStart="0dp"
android:paddingEnd="40dp"
android:scrollbars="horizontal"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/textMovieNowPlaying" />

<ProgressBar
android:id="@+id/loadingMovieNowPlaying"
android:layout_width="25dp"
android:layout_height="25dp"
android:layout_marginEnd="10dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/rvMovieNowPlaying"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/rvMovieNowPlaying" />

<ProgressBar
android:id="@+id/loadingMainMovieNowPlaying"
android:layout_width="50dp"
android:layout_height="100dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/rvMovieNowPlaying"
app:layout_constraintEnd_toEndOf="@id/rvMovieNowPlaying"
app:layout_constraintStart_toStartOf="@id/rvMovieNowPlaying"
app:layout_constraintTop_toTopOf="@id/rvMovieNowPlaying" />

</androidx.constraintlayout.widget.ConstraintLayout>

<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp">

<TextView
android:id="@+id/textUpcomingMovie"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/upcoming_movies"
android:textColor="@color/black"
android:textSize="20sp"
app:layout_constraintBottom_toTopOf="@id/rvUpcomingMovie"
app:layout_constraintTop_toTopOf="parent" />

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvUpcomingMovie"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:clipToPadding="false"
android:orientation="horizontal"
android:overScrollMode="never"
android:paddingStart="0dp"
android:paddingEnd="40dp"
android:scrollbars="horizontal"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/textUpcomingMovie" />

<ProgressBar
android:id="@+id/loadingMovieUpcoming"
android:layout_width="25dp"
android:layout_height="25dp"
android:layout_marginEnd="10dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/rvUpcomingMovie"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/rvUpcomingMovie" />

<ProgressBar
android:id="@+id/loadingMainMovieUpcoming"
android:layout_width="50dp"
android:layout_height="100dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/rvUpcomingMovie"
app:layout_constraintEnd_toEndOf="@id/rvUpcomingMovie"
app:layout_constraintStart_toStartOf="@id/rvUpcomingMovie"
app:layout_constraintTop_toTopOf="@id/rvUpcomingMovie" />

</androidx.constraintlayout.widget.ConstraintLayout>

<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp">

<TextView
android:id="@+id/textMovieTopRated"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/top_rated_movies"
android:textColor="@color/black"
android:textSize="20sp"
app:layout_constraintBottom_toTopOf="@id/rvMovieTopRated"
app:layout_constraintTop_toTopOf="parent" />

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvMovieTopRated"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:clipToPadding="false"
android:orientation="horizontal"
android:overScrollMode="never"
android:paddingStart="0dp"
android:paddingEnd="40dp"
android:scrollbars="horizontal"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/textMovieTopRated" />

<ProgressBar
android:id="@+id/loadingMovieTopRated"
android:layout_width="25dp"
android:layout_height="25dp"
android:layout_marginEnd="10dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/rvMovieTopRated"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/rvMovieTopRated" />

<ProgressBar
android:id="@+id/loadingMainMovieTopRated"
android:layout_width="50dp"
android:layout_height="100dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/rvMovieTopRated"
app:layout_constraintEnd_toEndOf="@id/rvMovieTopRated"
app:layout_constraintStart_toStartOf="@id/rvMovieTopRated"
app:layout_constraintTop_toTopOf="@id/rvMovieTopRated" />

</androidx.constraintlayout.widget.ConstraintLayout>

</LinearLayout>

layout_tv.xml :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@drawable/bg_text_title"
android:paddingStart="10dp"
android:paddingTop="5dp"
android:paddingEnd="10dp"
android:paddingBottom="5dp"
android:text="@string/tv_shows"
android:textColor="@color/black"
android:textSize="22sp"
android:textStyle="bold" />

<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp">

<TextView
android:id="@+id/textTvPopular"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/most_popular_tv_shows"
android:textColor="@color/black"
android:textSize="20sp"
app:layout_constraintBottom_toTopOf="@id/rvTvPopular"
app:layout_constraintTop_toTopOf="parent" />

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvTvPopular"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:clipToPadding="false"
android:orientation="horizontal"
android:overScrollMode="never"
android:paddingStart="0dp"
android:paddingEnd="40dp"
android:scrollbars="horizontal"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/textTvPopular" />

<ProgressBar
android:id="@+id/loadingTvPopular"
android:layout_width="25dp"
android:layout_height="25dp"
android:layout_marginEnd="10dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/rvTvPopular"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/rvTvPopular" />

<ProgressBar
android:id="@+id/loadingMainTvPopular"
android:layout_width="50dp"
android:layout_height="100dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/rvTvPopular"
app:layout_constraintEnd_toEndOf="@id/rvTvPopular"
app:layout_constraintStart_toStartOf="@id/rvTvPopular"
app:layout_constraintTop_toTopOf="@id/rvTvPopular" />

</androidx.constraintlayout.widget.ConstraintLayout>

<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp">

<TextView
android:id="@+id/textTvTopRated"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/top_rated_tv_shows"
android:textColor="@color/black"
android:textSize="20sp"
app:layout_constraintBottom_toTopOf="@id/rvTvTopRated"
app:layout_constraintTop_toTopOf="parent" />

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvTvTopRated"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:clipToPadding="false"
android:orientation="horizontal"
android:overScrollMode="never"
android:paddingStart="0dp"
android:paddingEnd="40dp"
android:scrollbars="horizontal"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/textTvTopRated" />

<ProgressBar
android:id="@+id/loadingTvTopRated"
android:layout_width="25dp"
android:layout_height="25dp"
android:layout_marginEnd="10dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/rvTvTopRated"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/rvTvTopRated" />

<ProgressBar
android:id="@+id/loadingMainTvTopRated"
android:layout_width="50dp"
android:layout_height="100dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/rvTvTopRated"
app:layout_constraintEnd_toEndOf="@id/rvTvTopRated"
app:layout_constraintStart_toStartOf="@id/rvTvTopRated"
app:layout_constraintTop_toTopOf="@id/rvTvTopRated" />

</androidx.constraintlayout.widget.ConstraintLayout>

<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp">

<TextView
android:id="@+id/textTvOnAir"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/on_the_air_tv_shows"
android:textColor="@color/black"
android:textSize="20sp"
app:layout_constraintBottom_toTopOf="@id/rvTvOnAir"
app:layout_constraintTop_toTopOf="parent" />

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvTvOnAir"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:clipToPadding="false"
android:orientation="horizontal"
android:overScrollMode="never"
android:paddingStart="0dp"
android:paddingEnd="40dp"
android:scrollbars="horizontal"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/textTvOnAir" />

<ProgressBar
android:id="@+id/loadingTvOnAir"
android:layout_width="25dp"
android:layout_height="25dp"
android:layout_marginEnd="10dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/rvTvOnAir"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/rvTvOnAir" />

<ProgressBar
android:id="@+id/loadingMainTvOnAir"
android:layout_width="50dp"
android:layout_height="100dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/rvTvOnAir"
app:layout_constraintEnd_toEndOf="@id/rvTvOnAir"
app:layout_constraintStart_toStartOf="@id/rvTvOnAir"
app:layout_constraintTop_toTopOf="@id/rvTvOnAir" />

</androidx.constraintlayout.widget.ConstraintLayout>

</LinearLayout>


Nampaknya postingan ini terlalu panjang, sebaiknya kita lanjutkan pada postingan berbeda. Silahkan KLIK DISINI.
Share:

0 comments:

Post a Comment

Hubungi Saya

Name

Email *

Message *

Terlaris 30 Hari Terakhir