Artikel ini sudah tidak relevan lagi dikarenakan service dari ibacor.com sudah mati. Jadi saya mengharapkan para pembaca untuk memakluminya jika aplikasi tidak bisa dijalankan. Untuk tutorial yang baru bisa dilihat di sini http://wimsonevel.blogspot.co.id/2017/07/re-tutorial-android-http-client-on.html
Retrofit merupakan library HTTP Client untuk Android dan Java. Library ini sangat populer dan banyak digunakan oleh developer khususnya Android. Retrofit sangat mudah digunakan, simpel, extensibility dan memiliki performance yang sangat bagus jika dibandingkan dengan library yang lain. Dengan menggunakan Retrofit, kita melakukan request ke REST Webservice dengan mudah dengan berbagai macam method-method yang disediakan. Library Retrofit dikembangkan oleh Square. Saya sendiri suka dengan library ini dan juga sering menggunakannya di setiap project-project yang saya kerjakan. :D
Pada postingan kali ini saya akan membahas tentang penggunaan library Retrofit di Android.
Hal yang perlu disiapkan terlebih dahulu adalah API service. Di sini saya menggunakan layanan Free API dari ibacor. Selengkapnya bisa dilihat di sini http://ibacor.com/api. API yang akan kita gunakan yaitu Jadwal Bioskop 21.
Contoh requestnya sebagai berikut :
Daftar Kota :
GET http://ibacor.com/api/jadwal-bioskop?k=....
Parameter :
k = API Key yang didapat dari ibacor
Response :
{ "status": "success", "data": [ { "id": "32", "kota": "AMBON" }, { "id": "6", "kota": "BALIKPAPAN" }, ….. ] }
Semua Jadwal Berdasarkan Id Kota
GET http://ibacor.com/api/jadwal-bioskop?k=...&id=10
Parameter :
k = API Key yang didapat dari ibacor
id = ID Kota
Response :
{ "status": "success", "kota": "JAKARTA", "date": "22\/07\/16", "data": [ { "movie": "GHOSTBUSTERS", "poster": "http:\/\/image.tmdb.org\/t\/p\/w300\/4qnJ1hsMADxzwnOmnwjZTNp0rKT.jpg", "genre": "Action, Comedy, Sci-fi", "duration": "116 minute", "jadwal": [ { "bioskop": "ANGGREK XXI", "jam": [ "12:15", "14:35", "15:25", "16:55", "19:15", "20:15", "21:35" ], "harga": "Rp.50,000" }, …... ] ….. } ] }
Kita akan membuat aplikasi untuk mengetahui informasi jadwal bioskop 21 di seluruh kota di Indonesia.
Buat project baru.
Pertama, tambahkan library retrofit di gradle. Retrofit yang akan digunakan adalah Retrofit 2. Latest version bisa dilihat di http://square.github.io/retrofit/
compile 'com.squareup.retrofit2:retrofit:2.1.0'Dan tambahkan juga converter gson, karena json hasil request akan langsung di convert ke gson object.
compile 'com.squareup.retrofit2:converter-gson:2.1.0'Tambahkan juga beberapa library pendukung yang diperlukan selengkapnya seperti ini :
dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:23.3.0' compile 'com.android.support:design:23.3.0' compile 'com.squareup.retrofit2:retrofit:2.1.0' compile 'com.squareup.retrofit2:converter-gson:2.1.0' compile 'com.squareup.picasso:picasso:2.5.2' }
Inisialisasi BASE_URL dan API_KEY sebagai di gradle.properties
BASE_URL = "http://ibacor.com" API_KEY = "YOUR API KEY"
Kemudian letakkan configurasi BASE_URL dan API_KEY di gradle.
... defaultConfig { applicationId "example.wim.androidretrofit" minSdkVersion 15 targetSdkVersion 23 versionCode 1 versionName "1.0" } buildTypes.each { it.buildConfigField 'String', 'BASE_URL', BASE_URL it.buildConfigField 'String', 'API_KEY', API_KEY } ...Kita akan mulai menambahkan resource yang diperlukan.
values/colors.xml
<?xml version="1.0" encoding="utf-8"?> <resources> <color name="colorPrimary">#795548</color> <color name="colorPrimaryDark">#4E342E</color> <color name="colorAccent">#FF4081</color> <color name="colorWhite">#FFFFFF</color> <color name="colorGrey">#E5E5E5</color> </resources>
values/string.xml
<resources> <string name="app_name">Cinema XXI</string> <string name="genre">Genre : %s</string> <string name="duration">Duration : %s</string> </resources>
drawable/bg_rounded.xml
<?xml version="1.0" encoding="UTF-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android"> <solid android:color="@color/colorPrimary"/> <stroke android:width="1dip" android:color="@color/colorPrimary" /> <corners android:radius="3dip"/> <padding android:left="0dip" android:top="0dip" android:right="0dip" android:bottom="0dip" /> </shape>
Buat beberapa layout berikut :
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="example.wim.androidretrofit.MainActivity"> <android.support.v4.widget.SwipeRefreshLayout android:id="@+id/refresh" android:layout_width="match_parent" android:layout_height="wrap_content"> <android.support.v7.widget.RecyclerView android:id="@+id/rv_city" android:layout_width="match_parent" android:layout_height="match_parent" android:scrollbars="vertical"/> </android.support.v4.widget.SwipeRefreshLayout> </RelativeLayout>
activity_movie.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.v4.widget.SwipeRefreshLayout android:id="@+id/refresh" android:layout_width="match_parent" android:layout_height="wrap_content"> <android.support.v7.widget.RecyclerView android:id="@+id/rv_movie" android:layout_width="match_parent" android:layout_height="match_parent" android:scrollbars="vertical"/> </android.support.v4.widget.SwipeRefreshLayout> </LinearLayout>
activity_showtime.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.v7.widget.RecyclerView android:id="@+id/rv_showtime" android:layout_width="match_parent" android:layout_height="match_parent" android:scrollbars="vertical"/> </LinearLayout>
list_item_city.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content" android:clickable="true" android:focusable="true" android:background="?android:attr/selectableItemBackground" android:padding="16dp"> <TextView android:id="@+id/city" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="16sp" android:text="Jakarta" /> </LinearLayout>
list_item_movie.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content" android:clickable="true" android:focusable="true" android:background="?android:attr/selectableItemBackground" android:padding="@dimen/margin_8dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <ImageView android:id="@+id/poster" android:layout_width="80dp" android:layout_height="100dp" android:src="@drawable/star_trek" android:scaleType="centerCrop"/> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="8dp"> <TextView android:id="@+id/title" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="STAR TREK BEYOND" android:textSize="17sp" android:textStyle="bold"/> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:layout_marginTop="8dp"> <TextView android:id="@+id/genre" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/genre" android:textSize="16sp"/> <TextView android:id="@+id/duration" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/duration" android:textSize="16sp"/> </LinearLayout> </LinearLayout> </LinearLayout> </LinearLayout>
list_item_showtime.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="@dimen/margin_8dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="@dimen/margin_8dp" android:background="@color/colorGrey"> <TextView android:id="@+id/theater" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="ATRIUM XXI" android:textSize="17sp" android:textStyle="bold"/> <example.wim.androidretrofit.util.FlowLayout android:id="@+id/lyTime" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="4dp" android:orientation="vertical"/> <TextView android:id="@+id/price" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="@dimen/margin_8dp" android:text="Rp. 30.000" android:textSize="16sp"/> </LinearLayout> </LinearLayout>
list_item_time.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="4dp" android:orientation="vertical"> <TextView android:id="@+id/time" android:layout_width="64dp" android:layout_height="wrap_content" android:background="@drawable/bg_rounded" android:gravity="center" android:padding="4dp" android:text="00:00" android:textColor="@color/colorWhite"/> </LinearLayout>
Selanjutnya kita akan melakukan deklarasi API dengan Retrofit.
Deklarasi API di Retrofit menggunakan Annotations di dalam method Interface dan parameter-parameter yang diperlukan untuk melakukan request. Setiap method harus mempunyai HTTP Annotation seperti GET, POST, PUT, DELETE dan HEAD. Setiap URL resource ditentukan oleh Annotation.
Contohnya :
@GET(“users/list”)
@POST(“users/new”)
Selain itu, parameter-parameter di method berupa annotations juga seperti @Query, @Path, @Body dan @Header.
@Path : variabel substitusi untuk endpoint API.
Contohnya : user id bisa disisipi {id} di URLnya.
@Query : menentukan parameter berupa key dan value.
@Body : mengirimkan request body berupa object melalui POST.
@Header : menentukan header.
Selanjutnya membuat interface yang berisi method-method tadi. Di method tersebut akan digunakan untuk mengirim request berupa daftar kota dan jadwal bioskop.
package example.wim.androidretrofit.service; import example.wim.androidretrofit.model.City; import example.wim.androidretrofit.model.Movie; import retrofit2.Call; import retrofit2.http.GET; import retrofit2.http.Query; /** * Created by Wim on 7/19/16. */ public interface ApiInterface { @GET("api/jadwal-bioskop") Call<City> getCity(); @GET("api/jadwal-bioskop") Call<Movie> getMovie( @Query("id") String id); }
Kemudian buat kelas service dengan nama ApiService. Di sini kita akan membuat konfigurasi dari Retrofit.
package example.wim.androidretrofit.service; import java.io.IOException; import java.util.concurrent.TimeUnit; import example.wim.androidretrofit.BuildConfig; import okhttp3.HttpUrl; import okhttp3.Interceptor; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; import retrofit2.Callback; import retrofit2.Retrofit; import retrofit2.converter.gson.GsonConverterFactory; /** * Created by Wim on 7/19/16. */ public class ApiService { private ApiInterface apiInterface; public ApiService(){ Retrofit retrofit = new Retrofit.Builder() .baseUrl(BuildConfig.BASE_URL) .client(builder()) .addConverterFactory(GsonConverterFactory.create()) .build(); apiInterface = retrofit.create(ApiInterface.class); } private OkHttpClient builder() { OkHttpClient.Builder okHttpClient = new OkHttpClient().newBuilder(); okHttpClient.connectTimeout(20, TimeUnit.SECONDS); okHttpClient.writeTimeout(20, TimeUnit.SECONDS); okHttpClient.readTimeout(90, TimeUnit.SECONDS); okHttpClient.addInterceptor(new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); HttpUrl url = request.url() .newBuilder() .addQueryParameter("k", BuildConfig.API_KEY) .build(); request = request.newBuilder().url(url).build(); return chain.proceed(request); } }); return okHttpClient.build(); } public void getCityList(Callback callback){ apiInterface.getCity().enqueue(callback); } public void getMovieList(String id, Callback callback){ apiInterface.getMovie(id).enqueue(callback); } }
Sampai di sini kita sudah membuat kelas untuk melakukan request ke API. Selanjutnya adalah membuat kelas model berdasarkan data di JSON. Berikut kelas-kelasnya :
City.java
package example.wim.androidretrofit.model; import java.util.List; /** * Created by Wim on 7/19/16. */ public class City { private String status; private String message; private List<CityData> data; public String getStatus() { return status; } public void setStatus(String status) { this.status = status; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public List<CityData> getData() { return data; } public void setData(List<CityData> data) { this.data = data; } }
CityData.java
package example.wim.androidretrofit.model; import android.os.Parcel; import android.os.Parcelable; /** * Created by Wim on 7/19/16. */ public class CityData implements Parcelable { private String id; private String kota; public CityData() { } protected CityData(Parcel in) { this.id = in.readString(); this.kota = in.readString(); } public static final Parcelable.Creator<CityData> CREATOR = new Parcelable.Creator<CityData>() { @Override public CityData createFromParcel(Parcel source) { return new CityData(source); } @Override public CityData[] newArray(int size) { return new CityData[size]; } }; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getKota() { return kota; } public void setKota(String kota) { this.kota = kota; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(this.id); dest.writeString(this.kota); } }
Movie.java
package example.wim.androidretrofit.model; import java.util.List; /** * Created by Wim on 7/19/16. */ public class Movie { private String status; private String message; private String kota; private String date; private List<MovieData> data; public String getStatus() { return status; } public void setStatus(String status) { this.status = status; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public String getKota() { return kota; } public void setKota(String kota) { this.kota = kota; } public String getDate() { return date; } public void setDate(String date) { this.date = date; } public List<MovieData> getData() { return data; } public void setData(List<MovieData> data) { this.data = data; } }
MovieData.java
package example.wim.androidretrofit.model; import android.os.Parcel; import android.os.Parcelable; import java.util.ArrayList; import java.util.List; /** * Created by Wim on 7/19/16. */ public class MovieData implements Parcelable { private String movie; private String poster; private String genre; private String duration; private List<Showtime> jadwal; public MovieData() { } protected MovieData(Parcel in) { this.movie = in.readString(); this.poster = in.readString(); this.genre = in.readString(); this.duration = in.readString(); this.jadwal = new ArrayList<Showtime>(); in.readList(this.jadwal, Showtime.class.getClassLoader()); } public static final Parcelable.Creator<MovieData> CREATOR = new Parcelable.Creator<MovieData>() { @Override public MovieData createFromParcel(Parcel source) { return new MovieData(source); } @Override public MovieData[] newArray(int size) { return new MovieData[size]; } }; public String getMovie() { return movie; } public void setMovie(String movie) { this.movie = movie; } public String getPoster() { return poster; } public void setPoster(String poster) { this.poster = poster; } public String getGenre() { return genre; } public void setGenre(String genre) { this.genre = genre; } public String getDuration() { return duration; } public void setDuration(String duration) { this.duration = duration; } public List<Showtime> getJadwal() { return jadwal; } public void setJadwal(List<Showtime> jadwal) { this.jadwal = jadwal; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(this.movie); dest.writeString(this.poster); dest.writeString(this.genre); dest.writeString(this.duration); dest.writeList(this.jadwal); } }
Showtime.java
package example.wim.androidretrofit.model; import android.os.Parcel; import android.os.Parcelable; import java.util.List; /** * Created by Wim on 7/19/16. */ public class Showtime implements Parcelable { private String bioskop; private List<String> jam; private String harga; public Showtime() { } protected Showtime(Parcel in) { this.bioskop = in.readString(); this.jam = in.createStringArrayList(); this.harga = in.readString(); } public static final Parcelable.Creator<Showtime> CREATOR = new Parcelable.Creator<Showtime>() { @Override public Showtime createFromParcel(Parcel source) { return new Showtime(source); } @Override public Showtime[] newArray(int size) { return new Showtime[size]; } }; public String getBioskop() { return bioskop; } public void setBioskop(String bioskop) { this.bioskop = bioskop; } public List<String> getJam() { return jam; } public void setJam(List<String> jam) { this.jam = jam; } public String getHarga() { return harga; } public void setHarga(String harga) { this.harga = harga; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(this.bioskop); dest.writeStringList(this.jam); dest.writeString(this.harga); } }
Setelah kelas model dibuat, berikutnya membuat kelas-kelas utility.
DividerItemDecoration.java
package example.wim.androidretrofit.util; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.View; /** * Created by Wim on 7/18/16. */ public class DividerItemDecoration extends RecyclerView.ItemDecoration { private static final int[] ATTRS = new int[]{ android.R.attr.listDivider }; public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL; public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL; private Drawable mDivider; private int mOrientation; public DividerItemDecoration(Context context, int orientation) { final TypedArray a = context.obtainStyledAttributes(ATTRS); mDivider = a.getDrawable(0); a.recycle(); setOrientation(orientation); } public void setOrientation(int orientation) { if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) { throw new IllegalArgumentException("invalid orientation"); } mOrientation = orientation; } @Override public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) { if (mOrientation == VERTICAL_LIST) { drawVertical(c, parent); } else { drawHorizontal(c, parent); } } public void drawVertical(Canvas c, RecyclerView parent) { final int left = parent.getPaddingLeft(); final int right = parent.getWidth() - parent.getPaddingRight(); final int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { final View child = parent.getChildAt(i); final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child .getLayoutParams(); final int top = child.getBottom() + params.bottomMargin; final int bottom = top + mDivider.getIntrinsicHeight(); mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); } } public void drawHorizontal(Canvas c, RecyclerView parent) { final int top = parent.getPaddingTop(); final int bottom = parent.getHeight() - parent.getPaddingBottom(); final int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { final View child = parent.getChildAt(i); final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child .getLayoutParams(); final int left = child.getRight() + params.rightMargin; final int right = left + mDivider.getIntrinsicHeight(); mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); } } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { if (mOrientation == VERTICAL_LIST) { outRect.set(0, 0, 0, mDivider.getIntrinsicHeight()); } else { outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0); } } }
FlowLayout.java
package example.wim.androidretrofit.util; import android.content.Context; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; import example.wim.androidretrofit.R; /** * Created by Wim on 7/21/16. */ public class FlowLayout extends ViewGroup { private int paddingHorizontal; private int paddingVertical; public FlowLayout(Context context) { super(context); init(); } public FlowLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public FlowLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } private void init() { paddingHorizontal = getResources().getDimensionPixelSize(R.dimen.margin_8dp); paddingVertical = getResources().getDimensionPixelSize(R.dimen.margin_8dp); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int childLeft = getPaddingLeft(); int childTop = getPaddingTop(); int lineHeight = 0; // 100 is a dummy number, widthMeasureSpec should always be EXACTLY for FlowLayout int myWidth = resolveSize(100, widthMeasureSpec); int wantedHeight = 0; for (int i = 0; i < getChildCount(); i++) { final View child = getChildAt(i); if (child.getVisibility() == View.GONE) { continue; } // let the child measure itself child.measure( getChildMeasureSpec(widthMeasureSpec, 0, child.getLayoutParams().width), getChildMeasureSpec(heightMeasureSpec, 0, child.getLayoutParams().height)); int childWidth = child.getMeasuredWidth(); int childHeight = child.getMeasuredHeight(); // lineheight is the height of current line, should be the height of the heightest view lineHeight = Math.max(childHeight, lineHeight); if (childWidth + childLeft + getPaddingRight() > myWidth) { // wrap this line childLeft = getPaddingLeft(); childTop += paddingVertical + lineHeight; lineHeight = childHeight; } childLeft += childWidth + paddingHorizontal; } wantedHeight += childTop + lineHeight + getPaddingBottom(); setMeasuredDimension(myWidth, resolveSize(wantedHeight, heightMeasureSpec)); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { int childLeft = getPaddingLeft(); int childTop = getPaddingTop(); int lineHeight = 0; int myWidth = right – left; for (int i = 0; i < getChildCount(); i++) { final View child = getChildAt(i); if (child.getVisibility() == View.GONE) { continue; } int childWidth = child.getMeasuredWidth(); int childHeight = child.getMeasuredHeight(); lineHeight = Math.max(childHeight, lineHeight); if (childWidth + childLeft + getPaddingRight() > myWidth) { childLeft = getPaddingLeft(); childTop += paddingVertical + lineHeight; lineHeight = childHeight; } child.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight); childLeft += childWidth + paddingHorizontal; } } }
Buat beberapa kelas adapter berikut :
CityListAdapter.java
package example.wim.androidretrofit.adapter; import android.content.Context; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import java.util.ArrayList; import java.util.List; import example.wim.androidretrofit.R; import example.wim.androidretrofit.listener.RecyclerViewItemClickListener; import example.wim.androidretrofit.model.CityData; /** * Created by Wim on 7/21/16. */ public class CityListAdapter extends RecyclerView.Adapter<CityListAdapter.CityViewHolder>{ private List<CityData> cityDataList; private Context context; private RecyclerViewItemClickListener recyclerViewItemClickListener; public CityListAdapter(Context context) { this.context = context; cityDataList = new ArrayList<>(); } private void add(CityData item) { cityDataList.add(item); notifyItemInserted(cityDataList.size() - 1); } public void addAll(List<CityData> cityDataList) { for (CityData cityData : cityDataList) { add(cityData); } } public void remove(CityData item) { int position = cityDataList.indexOf(item); if (position > -1) { cityDataList.remove(position); notifyItemRemoved(position); } } public void clear() { while (getItemCount() > 0) { remove(getItem(0)); } } public CityData getItem(int positon){ return cityDataList.get(positon); } @Override public CityViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_city, parent, false); final CityViewHolder cityViewHolder = new CityViewHolder(view); cityViewHolder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { int adapterPos = cityViewHolder.getAdapterPosition(); if (adapterPos != RecyclerView.NO_POSITION) { if (recyclerViewItemClickListener != null) { recyclerViewItemClickListener.onItemClick(adapterPos, cityViewHolder.itemView); } } } }); return cityViewHolder; } @Override public void onBindViewHolder(CityViewHolder holder, int position) { final CityData cityData = cityDataList.get(position); holder.city.setText(cityData.getKota()); } @Override public int getItemCount() { return cityDataList.size(); } public void setRecyclerViewItemClickListener(RecyclerViewItemClickListener recyclerViewItemClickListener) { this.recyclerViewItemClickListener = recyclerViewItemClickListener; } static class CityViewHolder extends RecyclerView.ViewHolder { TextView city; public CityViewHolder(View itemView) { super(itemView); city = (TextView) itemView.findViewById(R.id.city); } } }
MovieListAdapter.java
package example.wim.androidretrofit.adapter; import android.content.Context; import android.support.v7.widget.RecyclerView; import android.text.Html; 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.ArrayList; import java.util.List; import example.wim.androidretrofit.R; import example.wim.androidretrofit.listener.RecyclerViewItemClickListener; import example.wim.androidretrofit.model.MovieData; /** * Created by Wim on 7/20/16. */ public class MovieListAdapter extends RecyclerView.Adapter<MovieListAdapter.MovieViewHolder> { private List<MovieData> movieDataList; private Context context; private RecyclerViewItemClickListener recyclerViewItemClickListener; public MovieListAdapter(Context context) { this.context = context; movieDataList = new ArrayList<>(); } private void add(MovieData item) { movieDataList.add(item); notifyItemInserted(movieDataList.size() - 1); } public void addAll(List<MovieData> movieDataList) { for (MovieData movieData : movieDataList) { add(movieData); } } public void remove(MovieData item) { int position = movieDataList.indexOf(item); if (position > -1) { movieDataList.remove(position); notifyItemRemoved(position); } } public void clear() { while (getItemCount() > 0) { remove(getItem(0)); } } public MovieData getItem(int positon){ return movieDataList.get(positon); } @Override public MovieViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_movie, parent, false); final MovieViewHolder movieViewHolder = new MovieViewHolder(view); movieViewHolder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { int adapterPos = movieViewHolder.getAdapterPosition(); if (adapterPos != RecyclerView.NO_POSITION) { if (recyclerViewItemClickListener != null) { recyclerViewItemClickListener.onItemClick(adapterPos, movieViewHolder.itemView); } } } }); return movieViewHolder; } @Override public void onBindViewHolder(MovieViewHolder holder, int position) { final MovieData movieData = movieDataList.get(position); Picasso.with(context) .load(movieData.getPoster()) .into(holder.poster); holder.title.setText(Html.fromHtml(movieData.getMovie())); holder.genre.setText(context.getResources().getString(R.string.genre, movieData.getGenre())); holder.duration.setText(context.getResources().getString(R.string.duration, movieData.getDuration())); } @Override public int getItemCount() { return movieDataList.size(); } public void setRecyclerViewItemClickListener(RecyclerViewItemClickListener recyclerViewItemClickListener) { this.recyclerViewItemClickListener = recyclerViewItemClickListener; } static class MovieViewHolder extends RecyclerView.ViewHolder { ImageView poster; TextView title; TextView genre; TextView duration; public MovieViewHolder(View itemView) { super(itemView); poster = (ImageView) itemView.findViewById(R.id.poster); title = (TextView) itemView.findViewById(R.id.title); genre = (TextView) itemView.findViewById(R.id.genre); duration = (TextView) itemView.findViewById(R.id.duration); } } }
ShowtimeListAdapter.java
package example.wim.androidretrofit.adapter; import android.content.Context; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import java.util.ArrayList; import java.util.List; import example.wim.androidretrofit.R; import example.wim.androidretrofit.model.Showtime; import example.wim.androidretrofit.util.FlowLayout; /** * Created by Wim on 7/21/16. */ public class ShowtimeListAdapter extends RecyclerView.Adapter<ShowtimeListAdapter.ShowtimeViewHolder>{ private List<Showtime> showtimeList; private Context context; public ShowtimeListAdapter(Context context) { this.context = context; showtimeList = new ArrayList<>(); } private void add(Showtime item) { showtimeList.add(item); notifyItemInserted(showtimeList.size() - 1); } public void addAll(List<Showtime> showtimeList) { for (Showtime showtime : showtimeList) { add(showtime); } } public void remove(Showtime item) { int position = showtimeList.indexOf(item); if (position > -1) { showtimeList.remove(position); notifyItemRemoved(position); } } public void clear() { while (getItemCount() > 0) { remove(getItem(0)); } } public Showtime getItem(int positon){ return showtimeList.get(positon); } @Override public ShowtimeViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_showtime, parent, false); return new ShowtimeViewHolder(view); } @Override public void onBindViewHolder(ShowtimeViewHolder holder, int position) { final Showtime showtime = showtimeList.get(position); holder.theater.setText(showtime.getBioskop()); for (int i=0; i<showtime.getJam().size(); i++) { View view = LayoutInflater.from(context).inflate(R.layout.list_item_time, holder.lyTime, false); TextView time = (TextView) view.findViewById(R.id.time); time.setText(showtime.getJam().get(i)); holder.lyTime.addView(view); } holder.price.setText(showtime.getHarga()); } @Override public int getItemCount() { return showtimeList.size(); } static class ShowtimeViewHolder extends RecyclerView.ViewHolder { TextView theater; FlowLayout lyTime; TextView price; public ShowtimeViewHolder(View itemView) { super(itemView); theater = (TextView) itemView.findViewById(R.id.theater); lyTime = (FlowLayout) itemView.findViewById(R.id.lyTime); price = (TextView) itemView.findViewById(R.id.price); } } }
Buat interface untuk listener ketika recycler item di klik.
RecyclerItemClickListener.java
package example.wim.androidretrofit.listener; import android.view.View; /** * Created by Wim on 7/17/16. */ public interface RecyclerViewItemClickListener { void onItemClick(int position, View view); }
Langkah selanjutnya adalah buat activity berikut :
MainActivity.java
package example.wim.androidretrofit; import android.os.Handler; import android.support.v4.widget.SwipeRefreshLayout; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.View; import android.widget.Toast; import java.net.SocketTimeoutException; import example.wim.androidretrofit.adapter.CityListAdapter; import example.wim.androidretrofit.listener.RecyclerViewItemClickListener; import example.wim.androidretrofit.model.City; import example.wim.androidretrofit.service.ApiService; import example.wim.androidretrofit.util.DividerItemDecoration; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; public class MainActivity extends AppCompatActivity implements RecyclerViewItemClickListener { private RecyclerView rvCity; private SwipeRefreshLayout swipeRefreshLayout; private LinearLayoutManager linearLayoutManager; private CityListAdapter cityListAdapter; private ApiService apiService; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); rvCity = (RecyclerView) findViewById(R.id.rv_city); swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.refresh); linearLayoutManager = new LinearLayoutManager(this); cityListAdapter = new CityListAdapter(this); cityListAdapter.setRecyclerViewItemClickListener(this); rvCity.setLayoutManager(linearLayoutManager); rvCity.addItemDecoration(new DividerItemDecoration(this, LinearLayoutManager.VERTICAL)); rvCity.setAdapter(cityListAdapter); swipeRefreshLayout.setColorSchemeResources(R.color.colorPrimary); swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { @Override public void onRefresh() { refreshData(); } }); loadData(); } private void loadData(){ if (swipeRefreshLayout != null) swipeRefreshLayout.post(new Runnable() { @Override public void run() { swipeRefreshLayout.setRefreshing(true); } }); apiService = new ApiService(); apiService.getCityList(new Callback() { @Override public void onResponse(Call call, Response response) { City city = (City) response.body(); if(city != null) { if(city.getStatus().equals("success")) { cityListAdapter.addAll(city.getData()); }else{ Toast.makeText(MainActivity.this, city.getMessage(), Toast.LENGTH_LONG).show(); } Log.i("STATUS", city.getStatus()); }else{ Toast.makeText(MainActivity.this, "No Data!", Toast.LENGTH_LONG).show(); } if (swipeRefreshLayout != null) swipeRefreshLayout.setRefreshing(false); } @Override public void onFailure(Call call, Throwable t) { if(t instanceof SocketTimeoutException) { Toast.makeText(MainActivity.this, "Request Timeout. Please try again!", Toast.LENGTH_LONG).show(); }else{ Toast.makeText(MainActivity.this, "Connection Error!", Toast.LENGTH_LONG).show(); } Log.i("FAILURE", t.toString()); if (swipeRefreshLayout != null) swipeRefreshLayout.setRefreshing(false); } }); } private void refreshData(){ new Handler().post(new Runnable() { @Override public void run() { cityListAdapter.clear(); loadData(); } }); } @Override public void onItemClick(int position, View view) { MovieActivity.start(this, cityListAdapter.getItem(position)); } }
MovieActivity.java
package example.wim.androidretrofit; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.support.annotation.Nullable; import android.support.v4.widget.SwipeRefreshLayout; import android.support.v7.app.ActionBar; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.MenuItem; import android.view.View; import android.widget.Toast; import java.net.SocketTimeoutException; import example.wim.androidretrofit.adapter.MovieListAdapter; import example.wim.androidretrofit.listener.RecyclerViewItemClickListener; import example.wim.androidretrofit.model.CityData; import example.wim.androidretrofit.model.Movie; import example.wim.androidretrofit.service.ApiService; import example.wim.androidretrofit.util.DividerItemDecoration; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; /** * Created by Wim on 7/20/16. */ public class MovieActivity extends AppCompatActivity implements RecyclerViewItemClickListener { private RecyclerView rvMovie; private SwipeRefreshLayout swipeRefreshLayout; private LinearLayoutManager linearLayoutManager; private MovieListAdapter movieListAdapter; private ApiService apiService; private CityData cityData; private Movie movie; public static void start(Context context, CityData cityData) { Intent intent = new Intent(context, MovieActivity.class); intent.putExtra(MovieActivity.class.getSimpleName(), cityData); context.startActivity(intent); } @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_movie); cityData = getIntent().getParcelableExtra(MovieActivity.class.getSimpleName()); ActionBar actionBar = getSupportActionBar(); actionBar.setTitle(cityData.getKota()); actionBar.setDisplayHomeAsUpEnabled(true); rvMovie = (RecyclerView) findViewById(R.id.rv_movie); swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.refresh); linearLayoutManager = new LinearLayoutManager(this); movieListAdapter = new MovieListAdapter(this); movieListAdapter.setRecyclerViewItemClickListener(this); rvMovie.setLayoutManager(linearLayoutManager); rvMovie.addItemDecoration(new DividerItemDecoration(this, LinearLayoutManager.VERTICAL)); rvMovie.setAdapter(movieListAdapter); swipeRefreshLayout.setColorSchemeResources(R.color.colorPrimary); swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { @Override public void onRefresh() { refreshData(); } }); loadData(cityData.getId()); } private void loadData(String id){ if (swipeRefreshLayout != null) swipeRefreshLayout.post(new Runnable() { @Override public void run() { swipeRefreshLayout.setRefreshing(true); } }); apiService = new ApiService(); apiService.getMovieList(id, new Callback() { @Override public void onResponse(Call call, Response response) { movie = (Movie) response.body(); if(movie != null){ if(movie.getStatus().equals("success")) { movieListAdapter.addAll(movie.getData()); }else{ Toast.makeText(MovieActivity.this, movie.getMessage(), Toast.LENGTH_LONG).show(); } Log.i("STATUS", movie.getStatus()); }else{ Toast.makeText(MovieActivity.this, "No Data!", Toast.LENGTH_LONG).show(); } if (swipeRefreshLayout != null) swipeRefreshLayout.setRefreshing(false); } @Override public void onFailure(Call call, Throwable t) { if(t instanceof SocketTimeoutException) { Toast.makeText(MovieActivity.this, "Request Timeout. Please try again!", Toast.LENGTH_LONG).show(); }else{ Toast.makeText(MovieActivity.this, "Connection Error!", Toast.LENGTH_LONG).show(); } Log.i("FAILURE", t.toString()); if (swipeRefreshLayout != null) swipeRefreshLayout.setRefreshing(false); } }); } private void refreshData(){ new Handler().post(new Runnable() { @Override public void run() { movieListAdapter.clear(); loadData(cityData.getId()); } }); } @Override public void onBackPressed() { super.onBackPressed(); finish(); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: finish(); return true; default: return super.onOptionsItemSelected(item); } } @Override public void onItemClick(int position, View view) { ShowtimeActivity.start(this, movieListAdapter.getItem(position), movie.getDate()); } }ShowtimeActivity.java
import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v7.app.ActionBar; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.text.Html; import android.view.MenuItem; import example.wim.androidretrofit.adapter.ShowtimeListAdapter; import example.wim.androidretrofit.model.MovieData; /** * Created by Wim on 7/21/16. */ public class ShowtimeActivity extends AppCompatActivity { private RecyclerView rvShowtime; private LinearLayoutManager linearLayoutManager; private ShowtimeListAdapter showtimeListAdapter; private MovieData movieData; private String date; public static void start(Context context, MovieData movieData, String date) { Intent intent = new Intent(context, ShowtimeActivity.class); intent.putExtra(ShowtimeActivity.class.getSimpleName() + ".movie", movieData); intent.putExtra(ShowtimeActivity.class.getSimpleName() + ".date", date); context.startActivity(intent); } @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_showtime); movieData = getIntent().getParcelableExtra(ShowtimeActivity.class.getSimpleName() + ".movie"); date = getIntent().getStringExtra(ShowtimeActivity.class.getSimpleName() + ".date"); ActionBar actionBar = getSupportActionBar(); actionBar.setTitle(Html.fromHtml(movieData.getMovie())); actionBar.setSubtitle(date); actionBar.setDisplayHomeAsUpEnabled(true); rvShowtime = (RecyclerView) findViewById(R.id.rv_showtime); linearLayoutManager = new LinearLayoutManager(this); showtimeListAdapter = new ShowtimeListAdapter(this); rvShowtime.setLayoutManager(linearLayoutManager); rvShowtime.setAdapter(showtimeListAdapter); showtimeListAdapter.addAll(movieData.getJadwal()); } @Override public void onBackPressed() { super.onBackPressed(); finish(); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: finish(); return true; default: return super.onOptionsItemSelected(item); } } }Langkah terakhir jangan lupa tambahkan permission di AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET"/>
Finally, aplikasi kita sudah jadi sekarang tinggal di build dan running. Berikut screenshotnya :
Source code lengkap dapat dilihat di https://github.com/wimsonevel/Android-JadwalBioskop21
Sekian tutorial dari saya, semoga bermanfaat. :)
*Arigatou*
34 Komentar
Ijin sedot+rombak om hehe
Balasoke gan ^^
Balasijin sedot gan
BalasTanpa api key bisa yah gan
BalasBoleh sharing ga gan
BalasMau nanya nanya.
BalasMau nanya nanya.
BalasBoleh sharing ga gan
Balasgak pake api key gan, apinya sendiri free kok
Balasboleh
BalasGan kok base urlnya error padahal udah di tambah buildconfig fild di gradle
Balasrebuild dulu gan
BalasThanks gan. semoga berkah ilmunya :)
BalasGan mau nanya, kalo apinya yg pake key query nya gmna?
BalasPlease gan bantu dong saya mau retrieve postingan dari blogger tapi stuck di api interface nya gmna query untuk api yang menggunakan key
Balasquery apinya seperti apa gan? mungkin bisa di contohin dulu..
BalasMas wim, API di iBacor sekarang udah pake API key, ini gimana ya cara nambahin API key-nya di Retrofit ApiInterface-nya? Mohon bantuannya mas, terima kasih:D
Balasoke gan, sekarang udah di update pake API key,, bisa dilihat kembali tutorial di atas. Thanks :)
Balasdari ibacornya engga ada api yang buat coming soon ya mas?
Balasgak ada gan
BalasMas.. kalo pake Volley, ngambil list data Jam-nya gimana ya gan?
Balasbeda gan klo pake volley, ane blm bikin tutorialnya hehe
Balasgan buat dapetin KEY API nya gimana, di ibachor.com/login gagal
BalasWeb ibacor ngga bisa diakses ya gan ?
Balasmas wim request tutorial retrofit yang pakek google API yang pakek OAuth 2.0...
Balasiya gan, kyknya domainnya udh expired
Balasibacornya udh gak bisa diakses lg gan
Balassik ta lah, aku sik sibuk saiki, ntr tak buatin yo ...
BalasGan kalo yang gak pake API key jadi gimana ya gan? Soalnya ane gak ada API Key nya
Balassory gan service dari ibacornya udah mati jadi gak bisa dapet API key
Balaskang api key nya nggak bisa di ganti pakai api key yang lain ya (api key di web yang berbeda, contohnya pakai api key TMDB)?
BalasAPI key nya udh gak bisa gan, lihat artikel ini aja contoh lain pake TMDB >> http://wimsonevel.blogspot.co.id/2017/07/re-tutorial-android-http-client-on.html
Balaskak, nyari API key di ibacor itu gimana ya ?
BalasAPI ibacor sudah tidak aktif lg
BalasPenulisan markup di komentar