owned this note
owned this note
Published
Linked with GitHub
# BELAJAR ANDROID
## MATERI :
1. Instalasi Android Studio
1. Pengenalan Android Studio
3. Mulai membuat tampilan antar muka tingkat dasar
4. Mulai berinteraksi dengan aplikasi
5. Mulai membuat tampilan antar muka tingkat tinggi
6. Sharedpreferences
7. Model View Presenter
## INSTALASI ANDROID STUDIO
Android Studio adalah perangkat lunak buatan Google untuk para developer android dalam membuat dan mengembangkan aplikasi android. Android Studio merupakan IDE resmi untuk pengembangan aplikasi Android, berdasarkan InteliiJ IDEA. Android Studio menawarkan banyak fitur yang memungkinkan alur kerja pengembangan yang akan dibuat menjadi lebih mudah dan menyenangkan dalam satu set.
1. Instalasi Android Studio Integrated Development Environment (IDE)
Berikut langkah-langkah untuk menginstal Android Studio IDE (Pada sistem operasi Windows):
1) Setelah selesai download Andorid Studio, cari file Android Studio instalasi executable (bernama android-studio-bundle-<version>.exe) pada jendela Windows Explorer, klik dua kali untuk memulai proses instalasi
2) Klik tombol Yes paada dialog User Account Control jika muncul – klik Next

3) Klik Next ketika layar seperti gambar dibawah ini

4) Pada layar di gambar bawah ini adalah perjanjian lisensi. Jika setuju maka klik pada tombol I Agree

5) Lalu akan muncul layar pengaturan konfigurasi semua jalur instalasi yang akan ditampilkan. Jika perlu di ubah maka ubah lah,kemudian klik Next

6) Layar berikutnya adalah untuk menciptakan sebuah shourcuts pada start menu – biarkan saja pengaturan ini deafult dan klik Install. Maka proses instalasi yang sebenarnya dari Android Studio akan mulai dan tunggu hingga instalasi selesai

7) Jika proses instalasi telah selesai, maka layar seperti dibawah ini akan muncul. Lalu klik Next

8) Setelah itu masuk ke halaman Finish. Centang Start Android Studio jika ingin memulai membuka Android Studio untuk pertama kali – klik Finish

Pertama kali Android Studio diluncurkan setelah diinstal, dialog akan muncul menyediakan opsi untuk megimpor pengaturan dari versi Android Studio sebelumnya.

Jika memiliki pengaturan versi sebelumnya dan ingin mengimpor kedlaam instalasi terbaru, pilih opsi yang sesuai lokasi atau menunjukan bahwa tidak diperlukan impor pengaturan sebelumnya.
Jika baru pertama kali menginstal Android Studio, pilih I do not have a previous version of Android Studio or I do not want to import my settings dan klik OK
Kemudian akan muncul halaman berikutnya untuk mendownload dan menginstall komponen Android SDK Tools. Pastikan komputer terhubung dengan internet. Tunggulah proses download dan menginstall Android SDK tools hingga selesai.

Setelah proses download dan install komponen telah selesai. Klik Finish.

Apabila proses setup pada Android Studio telah selesai, maka akan tampil layar Selamat Datang Android Studio .
Sampai disini proses instalasi Android Studio sudah selesai. Android studio sudah dapat membuat project baru untuk mengembangkan aplikasi berbasis Android dengan Anndroid Studio. Namun masih dibutuhkan untuk instalasi paket SDK Android terbaru yang akan dijelaskan pada pembahasan selanjutnya.
2. Instalasi Android SDK
Android SDK adalah sekumpulan custom images yang digunakan untuk mengetest bahwa aplikasi Android yang telah dibuat dapat berjalan di berbagai macam versi Android. Android SDK juga mempunyai tools lainnya yang dibutuhkan dalam pengembangan aplikasi Android.
Langkah-langkah dalam menginstal Android SDK, sebagai berikut :
1) Pada main window Android Studio, pilih menu Tools pada toolbar, kemudian pilih Android > SDK Manager
2) Akan muncul jendela seperti berikut

3) Pilih SDK yang ingin di download dan instal dengan cara menandai checkbox yang ada di sebelah nama SDK dan klik install*packages
4) Pilih Accept All > Install
5) Tunggu hingga proses download dan installasi selesai
Setelah Android Studio dan SDK nya telah selesai di instal, maka layar Android Studio akan mengarahkan kepada layar Selamat Datang Andorid Studio seperti gambar dibawah ini:

Sampai disini proses instalasi Android Studio sudah selesai dan Android Studio sudah dapat digunakan untuk membuat project baru untuk mengembangkan aplikasi berbasiis Android dengan Android Studio.
## PENGENALAN ANDROID STUDIO
Sebelum memulai membuat proyek aplikasi android, ada beberapa langkah yang sebaiknya dilakukan untuk pengaturan sistem Android sehingga memudahkan penggunaan.
1. Pengaturan Android Software Development Kit (SDK) Manager
1) Klik Configure > SDK Manager sehingga muncul gambar berikut

Disini kita bisa memasang tools dan paket yang diperlukan uuntuk membangun aplikasi android, termasuk mendownload dan memasang versi android yang berbeda-beda. Proses download membutuhkan akses internet yang baik dan stabil dan juga butuh waktu yang cukup lama tergantuung banyaknya tools dan pakeyt yang dipasang dengan kecepatan akses internetnya. Silahkan pilih tools dan paket yang akan dipasang lalu klik butoon Install Packages dan tunggu sampai download dan pemasangan selesai.
2) Jika sudah masuk ke layar IDE Android Studio langkah akses SDK Manager juga dapat dilakukan yaitu dengan cara klik menu Tools > Android > SDK Manager, sehingga muncul gambar seperti di atas . Lakukan hal yang sama seperti di atas.
2. Pengaturan Android Virtual Devices (AVD) Manager
Setelah mendownload paket android beberapa versi dan tools yang dibtuhkan melalui SDK Manager, langkah selanjutnya adalah memasang AVD sehingga dapat dijalankan sebagai emulator android. Caranya sebagai berikut :
1) Jalankan SDK Manager seperti langkah sebelumnya di atas, baik melalui layar Welcome Android Studio maupun layar IDE Android Studio
2) Di layar Android SDK Manager pilih menu Tools > Manage AVDs .. sehingga muncul gambar berikut:

3) Jika belum pernah dilakukan pengaturan sebelumnya maka layar AVD Manager masih kosong. Untuk menambhkan AVD makan pilih/klik Device Definitions sehingga muncul gambar berikut:

4) Pilih lah device yang diinginkan dengan mempertimbangkan kemampuan laptop. Sebaiknya pilih spesifikasi yangg rendah dulu misalnya Nexus S by Google dnegan layar 4 inchi dan resolusi layar 480 x 800 (tegak), RAM 343 MiB. Klik di Nexus S by Google sehingga button Create AVD disebelah kanan ON.

5) Klik button Create AVD sehingga muncul gambar berikut

6) Isilah seperti contoh gambar di bawah ini untuk Target Android 2.3.3 (Ginger Bread) – API level 10

7) Setelah selesai mengisi dan button OK sudah ON yang berarti tidak ada error maka klik-lah button OK itu sheingga di bagian Android Virtual Devices akan muncul AVD yang telah disetting
8) Lakukan langkah pemasangan nomer 3 s/d 7 Nexus S by Google untuk android versi lain, misal android 4.2.2 (Jelly Bean) dan android 4.4.2 (KitKat) , sehingga akan terpasang 4 versi android, termasuk android 5.0 (Lollipop) yang sudah terpasang default. Lihat gambar dibawah ini

9) Untuk menjalankan android yang kita inginkan maka pilihlah android itu lalu klik button Start disebelah kanan

Maka muncul gambar berikut

10) Klik button Launch sehingga muncul gambar Starting Android Emulator seperti dibawah ini.

Jika tidak ada keterangan lain, termasuk kata error maka kemungkinan emulator android akan sukses muncul di layar. Tunggu beberapa tergantung spesifikasi laptop/komputernya.
## MULAI MEMBUAT TAMPILAN ANTARMUKA TINGKAT DASAR
Dalam membuat tampilan antar muka di Android Studio maka yang harus dikenali pertama kali adalah View dan ViewGroup.
View group merupakan komponen yang berperan mengkoordinasi komponen-komponen view lainnya. Beberapa contoh komponen View dan ViewGroup disajikan pada tabel berikut.
| View | ViewGroup |
| -------- | -------- |
| TextView | LinearLayout |
| Button | RelativeLayout |
| ImageView | ListView |
| EditText | GridView |
Secara hierarki, dapat dijelaskan seperti Gambar berikut :

Secara umum, layout Android ada 4 jenis yaitu LinearLayout, RelativeLayout, AbsoluteLayout dan FrameLayout. Namun, untuk kali ini kita akan bahas hanya LinearLayout dan RelativeLayout.
1. LinearLayout
LinearLayout yaitu tampilan dimana user hanya menempatkan 1(satu) widget per baris/kolom. Jika ingin 1 widget/baris, maka harus menggunakan orientation: vertical, dan jika 1 widget/kolom harus menggunakan orientation:horizontal.
```android
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal | vertical">
<!-- widget2 disini-->
</LinearLayout>
```
Contoh hasil dari tampilan LinearLayout adalah sebagai berikut:

Source: http://developer.android.com/resources/tutorials/views/images/hello-linearlayout.png
2. RelativeLayout
RelativeLayout adalah tampilan dimana user dapat menempatkan widget-widget didalamnya seperti layer, sehingga sebuah widget dapat berada di atas/di bawah widget lainnya. Layout ini biasanya berguna untuk membuat tampilan yang lebih universal sehingga dapat dibuka dengan baik di segala resolusi ponsel Android.
```android
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<!-- widget2 disini-->
<TextView
android:id="@+id/label1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:text="This is label1"/>
<TextView
android:id="@+id/label2"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@id/label1"
android:text="This is label2"/>
<TextView
android:id="@+id/label3"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/label2"
android:text="This is label3"/>
</RelativeLayout>
```
Contoh dari RelativeLayout adalah sebagai berikut:

Source: http://developer.android.com/resources/tutorials/views/images/hello-relativelayout.png
Dalam latihan kali ini, mari membuat aplikasi penghitung Luas Persegi.
1) Buat layout pada activity_main.xml
2) Ganti viewgroup dengan LinearLayout
3) Tambahkan view :
a. 2 TextView untuk label ‘Panjang’ dan ‘Lebar’
b. 2 EditText untuk mengisi nilai ‘Panjang’ dan ‘Lebar’
c. 1 Button untuk menghitung luas persegi
4) Adapun code pada xml sebagai berikut:
```android
<LinearLayout
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"
android:orientation="vertical"
tools:context="com.project.lianda.luaspersegi.MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:text="Panjang" />
<EditText
android:id="@+id/edt_panjang"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="numberDecimal"
android:layout_marginBottom="5dp"/>
<TextView
android:text="Lebar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"/>
<EditText
android:id="@+id/edt_lebar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="numberDecimal"
android:layout_marginBottom="5dp"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/btn_hitung"
android:text="Hitung"
android:layout_marginBottom="8dp"/>
</LinearLayout>
```
Bedah Kode:
1. LinearLayout
a. Digunakan untuk menjadi parent view layout agar menyusun view-view didalamnya secara berurutan
b. Menggunakan attribute ‘Orientation = Vertical” menandakan bahwa urutan yang dibentuk LinearLayout tersusun secara vertical dari atas ke bawah
2. TextView
a. Digunakan untuk memberi label pada sebuah text
b. Attribute ‘Text = value” berfungsi untuk memberikan tulisan pada TextView
c. Attribute ‘MarginBottom = value” berfungsi untuk memberikan jarak antara view tersebut dengan view yang ada dibawahnya
3. EditText
a. Digunakan untuk memberikan input box yang dapat diisi oleh user
b. Attribute ‘InputType = value’ berfungsi untuk memberikan tipe inputan yang diberikan user pada box tersebut, dalam hal ini ‘NumberDecimal’ digunakan untuk membuat user memberi tipe nilai angka decimal
4. Button
a. Digunakan untuk memberi tombol yang dapat menghubungkan interaksi antara user dan aplikasi
b. Menambahkan attribute ‘Id’ sangat dianjurkan agar button dapat di identifikasi saat hendak menambahkan aksi pada tombol tersebut
## MULAI BERINTERAKSI DENGAN APLIKASI
1. Interaksi Dasar
Dalam Android, untuk menambahkan interaksi antara user dan aplikasi hanya cukup dengan menambahkan metode “OnClickListener’ bawaan default dari Android Studio.
Adapun penulisan code :
```
{idView}.setOnClickListener{
//TODO: Set action here
}
```
Code diatas bermaksud untuk menambahkan aksi saat user menekan view tersebut hingga menjalankan aksi yang sudah ditentukan.
2. Interaksi dengan Intent
Dalam Android, ada class yang bernama ‘Intent’ bawaan Android yang digunakan untuk perpindahan halaman.
Intent juga merupakan suatu objek yang terdapat dalam suatu activity dimana objek tersebut dapat komunikasi dengan activity yang lain, baik activity pada fungsi internal android misal seperti memanggil activity dalam satu package atau beda package yang masih berada dalam satu project. Intent merupakan objek tipe android.content.Intent. Melalui metode startActivity() yang digunakan untuk memulai sebuah activity lain.
Intent dibagi menjadi 2, yaitu :
1. Explicit Intent berfungsi untuk mengaktifkan komponen-komponen dalam satu aplikasi yang sama. Misalnya seperti : Berpindah Activity.
2. Implicit Intent berfungsi untuk memanggil fungsi activity yang sudah ada di fungsi internal android seperti Dial Number, Open Browser, Share dan lainnya.
Lanjutkan latihan,
1. Buka MainActivity.Kt
2. Di dalam function OnCreate, tambahkan code sebagai berikut :
```kotlin
btnHitung.setOnClickListener{
val panjang = edtPanjang.text.trim().toString()
val lebar = edtLebar.text.trim().toString()
val result = panjang.toDouble()*lebar.toDouble()
Toast.makeText(this, “Hasilnya$result”, Toast.LENGTH_SHORT).show()
}
```
3. Buat Activity baru untuk menampilkan hasil perhitungan melalui halaman lain. Adapun cara membuat activity baru:
a. Klik kanan pada package utama
b. Klik menu ‘New’
c. Klik ‘Activity’ lalu pilih ‘EmptyActivity’
d. Tuliskan nama activity ‘ResultActivity’
4. Pada activity_result.xml tambahkan TextView untuk menampilkan hasil
5. Pada MainActivity.Kt, Tambahkan Explicit Intent
a. Intent tanpa data
```kotlin
btnHitung.setOnClickListener{
val panjang = edtPanjang.text.trim().toString()
val lebar = edtLebar.text.trim().toString()
val result = panjang.toDouble()*lebar.toDouble()
val intent = Intent()
startActivity(intent, ResultActivity::class.java)
}
```
b. Intent dengan data
- Masukkan code berikut di dalam MainActivity.Kt sebagai pengirim data
```kotlin
btnHitung.setOnClickListener{
val panjang = edtPanjang.text.trim().toString()
val lebar = edtLebar.text.trim().toString()
val result = panjang.toDouble()*lebar.toDouble()
val intent = Intent()
intent.putExtra(“resultKey”, result)
startActivity(intent, ResultActivity::class.java)
}
```
- Masukkan code berikut di dalam ResultActivity.Kt sebagai penerima data di dalam function OnCreate
```kotlin
val result = intent.getBooleanExtra(“resultKey”)
tvResult.text = result.toString()
```
6. Untuk menggunakan Implicit Intent, tambahkan 3 button di activity_result.xml
7. Buat Buat beberapa fungsi didalam ResultActivity.Kt sebagai berikut:
```kotlin
fun showWebPage(){
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(“http://www..com”))
startActivity(intent)
}
fun share(){
val intent = Intent()
intent.action = Intent.ACTION_SEND
intent.type = “text/plain”
intent.putExtra(Intent.EXTRA_TEXT, “this is text to send”)
statrActivity(intent)
}
fun showDialPage(){
val intent = Intent(Intent.ACTION_CALL)
intent.data = Uri.parse(“tel:” + phoneNumber)
startActivity(intent)
}
```
8. Tambahkan code berikut di dalam function OnCreate
```kotlin
btnWebPage.setOnClickListener{
showWebPage()
}
btnShare.setOnClickListener{
share()
}
btnDial.setOnClickListener{
showDialPage()
}
```
Bedah Kode:
Dalam melakukan perpindahan halaman dengan membawa data, perlu diperhatikan pada code berikut:
```kotlin
intent.putExtra(“resultKey”, result)
```
resultKey : Merupakan key untuk mengirim data yang harus bersifat unik
```kotlin
val result = intent.getBooleanExtra(“resultKey”)
```
getBooleanExtra : Merupakan function untuk mengambil data dengan mengikuti tipe data yang dikirim
## MULAI MEMBUAT TAMPILAN ANTARMUKA TINGKAT TINGGI
### Menggunakan Fragment
Fragment adalah salah satu komponen di android yang memiliki sifat dan fungsi yang sama dengan Activity. Namun fragment tidak bisa berdiri sendiri tanpa adanya Activity.
Lanjutkan Latihan:
1. Buat Activity baru dengan nama “MainActivity” yang digunakan untuk menampung fragment
2. Klik kanan pada package app utama
3. Klik “New”
4. Klik “Fragment” pilih “BlankFragment”
1. Beri nama fragment "HomeFragment"
5. Tambahkan kode berikut pada activity_main.xml
```android
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:padding="16dp"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
android:layout_marginBottom="16dp"/>
<FrameLayout
android:id="@+id/containerHome"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
```
6. Tambahkan kode berikut pada MainActivity.Kt
```kotlin
class MainActivity : AppCompatActivity(), OnIncreaseNumberListener {
var counter: Int = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val bundle = Bundle()
bundle.putString("name", "Sidiq Permana")
val homeFragment = HomeFragment()
homeFragment.onIncreaseNumberListener = this
homeFragment.arguments = bundle
supportFragmentManager.beginTransaction()
.replace(R.id.containerHome, homeFragment,
HomeFragment::class.java.simpleName)
.commit()
}
override fun onNumberIncreased() {
counter += 1
Toast.makeText(this,
"Dipanggil dari fragment, counter $counter", Toast.LENGTH_LONG)
.show()
}
}
```
7. Tambahkan kode berikut pada home_fragment.xml
```kotlin=
class HomeFragment : Fragment() {
var onIncreaseNumberListener:
OnIncreaseNumberListener? = null
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? = inflater.inflate(R.layout.fragment_home, container, false)
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
if (arguments != null){
val name = arguments?.getString("name")
tvHello.text = "Hai $name"
}
btnProfile.setOnClickListener {
val intent = Intent(context,
ProfileActivity::class.java)
startActivity(intent)
}
btnUpdateCounter.setOnClickListener {
onIncreaseNumberListener?.onNumberIncreased()
}
Picasso.get().load("https://www.partyrama.co.uk/wp-content/uploads/2017/06/minions-kevin-lifesize-cardboard-cutout-180cms-product-image.jpg")
.into(imgMinions)
}
}
```
8. Buat satu buah interface seperti kode dibawah ini:
```kotlin
interface OnIncreaseNumberListener {
fun onNumberIncreased()
}
```
9. Jalankan aplikasi
Aplikasi anda akan tampak seperti gambar dibawah ini:

Dan apabila di klik button update counter akan tampak seperti gamabr dibawah ini:

Bedah Kode:
```kotlin
val homeFragment = HomeFragment()
supportFragmentManager.beginTransaction()
.replace(R.id.containerHome, homeFragment,
HomeFragment::class.java.simpleName)
.commit()
```
Kode diatas merupakan kode untuk memanggil sebuah fragment ke dalam activity
```kotlin
val bundle = Bundle()
bundle.putString("name", "Sidiq Permana")
val homeFragment = HomeFragment()
homeFragment.arguments = bundle
```
Kode diatas digunakan untuk mengirim sebuah data ke dalam fragment
```kotlin
if (arguments != null){
val name = arguments?.getString("name")
tvHello.text = "Hai $name"
}
```
Untuk mengambil data yang sudah dikirim ke fragment, maka kode diatas digunakan fragment untuk menerima data dalam bentuk argument.
Selanjutnya merupakan kode untuk interaksi dari fragment ke activity dengan menggunakan interface
```kotlin
interface OnIncreaseNumberListener {
fun onNumberIncreased()
}
```
Kode diatas merupakan sebuah interface yang akan menghubungkan fragment dengan activity yang mengimplementasikan interface tersebut
```kotlin=
var onIncreaseNumberListener:
OnIncreaseNumberListener? = null
btnUpdateCounter.setOnClickListener {
onIncreaseNumberListener?.onNumberIncreased()
}
```
Kode diatas merupakan cara untuk menggunakan interface pada fragment yaitu dengan membuat sebuah objek interface dan memanggil fungsinya saat button update counter di klik oleh user
```kotlin
class MainActivity : AppCompatActivity(), OnIncreaseNumberListener {
var counter: Int = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val homeFragment = HomeFragment()
homeFragment.onIncreaseNumberListener = this
}
override fun onNumberIncreased() {
counter += 1
Toast.makeText(this,
"Dipanggil dari fragment, counter $counter", Toast.LENGTH_LONG)
.show()
}
}
```
Sedangkan kode diatas digunakan activity untuk mengimplement interface dan kemudian melakukan sesuatu pada function onNumberIncreased.
### Menggunakan RecyclerView
Recyclerview adalah salah satu komponen dasar dalam android untuk menampilkan banyak data yang dapat di gulir dalam bentuk list. RecyclerView merupakan versi ListView yang telah ditingkatkan dan lebih fleksibel. Jadi recyclerview memungkinkan kita untuk menambah data secara dinamis kedalam tampilan.
Terdapat 4 komponen utama pada Recyclerview :
• Data
• Layout
• ViewHolder
• Adapter
Layout adalah tampilan yang akan dibuat untuk setiap item yang akan ditampilkan kedalam recyclerview.
ViewHolder berisi tampilan informasi untuk menampilkan satu item dari layout.
Adapter digunakan untuk menghubungkan data dengan recyclerview dan menyiapkan data dengan menampilkan data dalam viewholder.
Hubungan keempat komponen tersebut dapat digambarkan sebagai berikut:

Latihan:
1. Langkah pertama adalah tambahkan dependency baru untuk menggunakan RecyclerView
a. Pada project structure, pilih “build.gradle(Module:app)”
b. Tambahkan code berikut di dalam tag
```android
dependencies{
implementation ‘com.android.support:recyclerview-v7:x.x.x’
}
```
2. Buat activity baru dengan nama “SimpleListActivity”
3. Pada acitivity_simple_list.xml tambahkan view berikut
```android
<android.support.v7.widget.RecyclerView
android:id="@+id/rvList"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
```
4. Buat layout baru dengan nama “item_list.xml”
a. Klik kanan pada folder “res”
b. Pilih “New”
c. Pilih “Layout Resource File”
d. Beri nama “item_list”
e. Klik finish
f. Tunggu sampai layout terbuat
5. Tambahkan kode berikut pada “item_list.xml”
```android
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="@+id/txtName"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
```
6. Buat kelas Adapter dengan nama “SimpleAdapter”. Bila ada penanda error yang muncul, abaikan dulu sementara.
```kotlin
class SimpleAdapter() : RecyclerView.Adapter<SimpleHolder.ViewHolder>() {
}
```
7. Buat kelas ViewHolder sebagai inner class dari kelas adapter yang sudah dibuat.
```kotlin
class SimpleAdapter() : RecyclerView.Adapter<SimpleAdapter.ViewHolder>() {
//Viewholder = item object manipulator
inner class Viewholder(itemView: View) :
RecyclerView.ViewHolder(itemView) {
//bind data to item view
fun bind(item: String){
itemView.tvItem.text = item
}
}
}
```
8. Tambahkan kode dibawah ini untuk melengkapi kelas SimpleAdapter.
```kotlin
class SimpleAdapter():
RecyclerView.Adapter<SimpleAdapter.Viewholder>() {
val itemList = mutableListOf<String>()
//purpose of this method is to inflate the itemView layout in xml
//and convert it into itemView object
override fun onCreateViewHolder(viewGroup: ViewGroup, position: Int): Viewholder {
val itemView = LayoutInflater
.from(viewGroup.context)
.inflate(R.layout.item_list, viewGroup,
false)
return Viewholder(itemView)
}
//purpose of this method is to get total numbers of
//items will be iterated
override fun getItemCount() = itemList.size
//purpose of this method is to bind the data into
//list item object (ViewHolder)
override fun onBindViewHolder(viewholder: Viewholder, position: Int) {
viewholder.bind(itemList[position])
}
//Viewholder = item object manipulator
inner class Viewholder(itemView: View) :
RecyclerView.ViewHolder(itemView) {
//bind data to item view
fun bind(item: String){
itemView.tvItem.text = item
}
}
}
```
Penjelasan mengenai kode pada SimpleAdapter:
a. Kelas Viewholder: Kelas ini bertujuan untuk menampilkan data yang telah ditambakan ke adapter kepada user. Setiap view holder menampung satu rangkaian data dari list. Terdapat fungsi bind() yang bertujuan untuk mengikat data pada masing-masing objek tampilan untuk ditampilkan kepada pengguna.
b. getItemCount(): Fungsi ini bertujuan untuk mendapatkan jumlah data yang telah ditambakan untuk ditampilkan.
c. onCreateViewHolder(): Fungsi ini bertujuan untuk mengisi layout xml yang telah dibuat kedalam ViewHolder, mengubahnya menjadi object ItemView, dan mengembalikan nilainya.
d. onBindViewHolder(): Fungsi ini bertujuan untuk menghubungkan data dengan viewholder pada posisi yang ditentukan oleh recyclerview.
9. Tambahkan kode berikut pada SimpleListActivity.Kt di dalam fungsi onCreate
```kotlin
val list = mutableListOf(
“Thor”, “Captain America”, “Iron Man”)
//configure the recyclerview
rvMovie.setHasFixedSize(true)
rvMovie.layoutManager = LinearLayoutManager(this)
rvMovie.addItemDecoration(DividerItemDecoration(this,
DividerItemDecoration.VERTICAL))
//create an adapter instance
val adapter = SimpleAdapter(this)
adapter.itemList.addAll(listMovie)
//set adapter to RecyclerView
rvMovie.adapter = adapter = adapter
}
```
Pada Recyclerview, terdapat 3 layout manager yang dapat digunakan, yaitu:
- **LinearLayoutManager**: Menampilkan item dalam bentuk list secara vertikal atau horizontal
- **GridLayoutManager**: Menampilkan item dalam kumpulan petak
- **StaggeredGridLayoutManager**: Menampilkan item dalam kumpulan petak dengan bentuk yang zigzag
Pada latihan kali ini, kita menggunakan LinearLayoutManager.
Setelah itu coba jalankan kode anda pada device/emulator yang digunakan. Hasilnya adalah sebagai berikut

10. Lalu bagaimana caranya agar user dapat berinteraksi dengan list data yang ditampilkan dengan recyclerview, contohnya melakukan klik pada suatu data? Untuk melakukan hal tersebut diperlukan suatu *callback* yang menangani tindakan klik data. *Callback* disini bertujuan sebagai penghubung antara kelas Adapter dan Activity dimana reaksi dari suatu data yang mendapatkan tindakan klik dalam adapter akan diproses pada Activity sehingga kelihatan oleh pengguna. Tambahkan suatu interface dan tambahkan clickListener pada itemView pada kelas SimpleAdapter sehingga kodenya menjadi seperti berikut
```kotlin
class SimpleAdapter(val onItemClickListener:
OnItemClickListener):
RecyclerView.Adapter<SimpleAdapter.Viewholder>() {
val itemList = mutableListOf<String>()
//this method is to inflate the itemView layout in xml
// and convert
//it into itemView object
override fun onCreateViewHolder(viewGroup: ViewGroup, position: Int): Viewholder {
val itemView = LayoutInflater
.from(viewGroup.context)
.inflate(R.layout.item_list, viewGroup,
false)
return Viewholder(itemView)
}
//this method is to get total numbers of
//items will be iterated
override fun getItemCount() = itemList.size
//this method is to bind the data into
//list item object (ViewHolder)
override fun onBindViewHolder(viewholder: Viewholder, position: Int) {
viewholder.bind(itemList[position])
}
//Viewholder = item object manipulator
inner class Viewholder(itemView: View) :
RecyclerView.ViewHolder(itemView) {
//bind data to item view
fun bind(item: String){
itemView.tvItem.text = item
itemView.itemList.setOnClickListener {
onItemClickListener.onItemClicked(item)
}
}
}
//Callback action listener
interface OnItemClickListener{
fun onItemClicked(item: String)
}
}
```
11. Implementasikan fungsi pada interface OnItemClickListener pada kelas SimpleListActivity dan tambahkan kode yang akan dijalankan yang akan kita tentukan untuk menampilkan sebuah Toast mengenai judul film yang diklik sehingga kelas SimpleListActivity menjadi:
```kotlin
class SimpleListActivity : AppCompatActivity(),
SimpleAdapter.OnItemClickListener {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_simple_list)
//set the data
val listMovie = mutableListOf(
"Thor", "Captain America", "Iron Man")
//configure the recyclerview
rvMovie.setHasFixedSize(true)
rvMovie.layoutManager = LinearLayoutManager(this)
rvMovie
.addItemDecoration(DividerItemDecoration(this,
DividerItemDecoration.VERTICAL))
//create an adapter instance
val adapter = SimpleAdapter(this)
adapter.itemList.addAll(listMovie)
//set adapter to RecyclerView
rvMovie.adapter = adapter
}
override fun onItemClicked(item: String) {
Toast
.makeText(this, "$item clicked", Toast.LENGTH_LONG)
.show()
startActivity(Intent(this,
MovieGalleryActivity::class.java))
}
}
```
## SHAREDPREFERENCES
***SharedPreferences*** adalah salah satu jenis penyimpanan yang disediakan Android untuk menyimpan suatu data dalam tempat penyimpanan perangkat dalam ukuran kecil dengan tipe data primitif. Dalam proses penyimpanan data, sharedpreferences menggunakan kombinasi *key-value*.
Latihan:
1. Buat Project baru dan pilih *Empty Activity*
2. Buat sebuah *file* Kotlin dengan nama *PreferenceKey*
3. Tambahkan konstanta yang akan digunakan sebagai *key* untuk mengakses *sharedpreferences* sebagai berikut:
```kotlin
const val PREF_KEY_NAME = "key_name"
const val PREF_KEY_EMAIL = "key_email"
const val PREF_KEY_PHONE = "key_phone"
const val PREF_KEY_AGE = "key_age"
const val PREFERENCE_NAME = "PREF_USERDATA"
```
4. Buat sebuah kelas dengan nama *AppPreference* dengan parameter context. Kelas ini bertujuan untuk menampung semua perintah untuk menyimpan dan mengambil data dari *sharedpreferences*
```kotlin
class AppPreference(context: Context) {
var userPreference: SharedPreferences ?= null
init {
userPreference = context
.getSharedPreferences(PREFERENCE_NAME,
Context.MODE_PRIVATE)
}
fun setValue(key: String, value: String){
userPreference?.edit()?.putString(key, value)?.apply()
}
fun getStringValue(key: String): String? =
userPreference?.getString(key, null)
fun setValue(key: String, value: Int){
userPreference?.edit()?.putInt(key, value)?.apply()
}
fun getIntValue(key: String): Int? =
userPreference?.getInt(key, 0)
fun clear(){
userPreference?.edit()?.clear()?.apply()
}
}
```
Ketika kelas tersebut di inisialisasi, kelas tersebut akan membuat sebuah objek sharedpreferences dengan nama berdasarkan konstanta PREFERENCE_NAME dimana *preferences* tersebut hanya bisa diakses oleh aplikasi ini saja (Context.MODE_PRIVATE).
Untuk menyimpan suatu data dalam *sharedpreferences*, diguanakan metode edit() disertai dengen nilai key dan value yang akan disimpan berdasarkan tipe datanya. Contohnya pada hal ini adalah ketika ingin menyimpan nilai bertipe string digunakan perintah
> userPreference?.edit()?.putString(key, value)?.apply().
Fungsi apply() berarti proses penyimpanan data dilakukan secara *asynchronus*
Lalu untuk memgambil suatu data dalam *sharedpreferences*, digunakan metode *get* berdasartkan tipe data yang di inginkan dengan nilai *key* yang dimaksud beserta melampirkan nilai *default* yang di inginkan.
> userPreference?.getInt(key, 0)
Terdapat fungsi clear yang digunakan untuk menghapus semua data yang tersimpan pada *sharedpreferences* tersebut
>userPreference?.edit()?.clear()?.apply()
5. Ubah tampilan *activity_main.xml* menjadi seperti berikut
```android
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Name"
android:layout_marginBottom="4dp"/>
<EditText
android:id="@+id/edtName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
android:inputType="textPersonName"
android:hint="Eg: John Doe"
android:layout_marginBottom="16dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Email"
android:layout_marginBottom="4dp"/>
<EditText
android:id="@+id/edtEmail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
android:inputType="textEmailAddress"
android:hint="Eg: user@foo.com"
android:layout_marginBottom="16dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Phone Number"
android:layout_marginBottom="4dp"/>
<EditText
android:id="@+id/edtPhoneNumber"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
android:inputType="phone"
android:hint="Eg: 081210841382"
android:layout_marginBottom="16dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Age"
android:layout_marginBottom="4dp"/>
<EditText
android:id="@+id/edtAge"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
android:inputType="number"
android:layout_marginBottom="16dp"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/btnSave"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Simpan"/>
<Button
android:id="@+id/btnClear"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Hapus"/>
</LinearLayout>
</LinearLayout>
```
6. Pada kelas *MainActivity* tambahkan kode berikut:
```kotlin
class MainActivity : AppCompatActivity() {
private lateinit var appPreference: AppPreference
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
appPreference = AppPreference(this)
if (!TextUtils.isEmpty(appPreference.getStringValue(PREF_KEY_NAME))){
edtName.setText(appPreference.getStringValue(PREF_KEY_NAME))
}
if (!TextUtils.isEmpty(appPreference.getStringValue(PREF_KEY_PHONE))){
edtPhoneNumber.setText(appPreference.getStringValue(PREF_KEY_PHONE))
}
if (!TextUtils.isEmpty(appPreference.getStringValue(PREF_KEY_EMAIL))){
edtEmail.setText(appPreference.getStringValue(PREF_KEY_EMAIL))
}
edtAge.setText(appPreference.getIntValue(PREF_KEY_AGE).toString())
btnSave.setOnClickListener {
val name = edtName.text.toString().trim()
val email = edtEmail.text.toString().trim()
val phone = edtPhoneNumber.text.toString().trim()
val age = edtAge.text.toString().trim()
appPreference.setValue(PREF_KEY_NAME, name)
appPreference.setValue(PREF_KEY_EMAIL, email)
appPreference.setValue(PREF_KEY_PHONE, phone)
appPreference.setValue(PREF_KEY_AGE, age.toInt())
Toast.makeText(this, "Data saved",
Toast.LENGTH_LONG).show()
}
btnClear.setOnClickListener {
appPreference.clear()
edtName.setText("")
edtEmail.setText("")
edtPhoneNumber.setText("")
edtAge.setText("0")
}
}
}
```
Penjelasan kode pada MainActivity:
a. Pada objek *appPreference*, terdapat *keyword lateinit* ketika mendeklarasikan objek tersebut. Pada Kotlin, *lateinit* berfungsi untuk melakukan inisialisasi nilai yang bersifat telat pada suatu objek ketika tidak mungkin dilakukan inisialisasi nilai pada saat kelas tersebut dipanggil. Nilai dari objek *lateinit* tidak boleh null, harus di inisialisasi pada suatu titik, dan tidak boleh berupa tipe data primitif.
b. Dalam metode *onCreate()*, objek appPreference di inisialisasi dengan parameter konteks *MainActivity*. Artinya objek *AppPreference* dapat digunakan dalam kelas *MainActivity* untuk mengakses *sharedpreference*.
c. Pada *EditText* nama, dilakukan pengecekan apakah terdapat nilai untuk nama pada *sharedpreferences*. Jika ada, nilai tersebut akan diisi pada *editText* nama.
> Fungsi dari *appPreference.getStringValue(PREF_KEY_NAME)* adalah untuk mengembalikan nilai preference dengan *key* *PREF_KEY_NAME* bertipe data *String*.
Hal yang sama juga dilakukan pada EditText nomor telepon, email, dan umur. tetapi pada EditText umur tidak perlu dilakukan pengecekan karena secara *default* nilai yang dikembalikan oleh *preferences* dengan tipe data *integer* adalah 0.
d. ketika tombol simpan ditekan, akan terjadi proses peyimpanan data dari masing-masing *EditText* ke sharedpreference berdasarkan *key* dan *value* yang telah ditentukan. Lalu sebuah *toast* akan dimunculkan ketika proses penyimpanan data berhasil.
> *appPreference.setValue(key,value)* berfungsi untuk menyimpan data pada *sharedpreferences* sesuai dengan nilai *key* dan *value* yang diberikan.
e. Tombol Hapus digunakan untuk mengosongkan semua data pada form dalam MainActivity serta menghapus semua data yang tersimpan pada *sharedpreferences* melalui *appPreference.clear()*
Ketika pertama kali kode dijalankan, tampilannya adalah sebagai berikut:

Lalu silahkan mengisi data pada setiap *EditText*. Setelah itu tekan tombol simpan dan tutup aplikasi tersebut lalu buka kembali aplikasi tersebut. Maka nilai yang sudah tersimpan akan ditampilkan kembali pada aplikasi. Hasil tampilannya adalah:

## MODEL-VIEW-PRESENTER
### Pengertian
***Model-View-Presenter*** (MVP) adalah sebuah arsitektur dalam membangun sebuah aplikasi yang memisahkan antara proses bisnis dengan tampilan. Tujuan menggunakan MVP adalah untuk membuat kode yang dibangun menjadi lebih terstruktur, lebih mudah dilakukan pengujian dan lebih mudah dipelihara.
Terdapat 3 komponen layer pada MVP, yaitu:
1. **Model**: Layer yang berhubungan secara langsung dengan data dari *API*, *cache*, *preference*, dan *database*. Dengan kata lain, pada layer ini terjadi proses pengambilan data dan melakukan manipulasi data yang berasal dari penyimpanan pada perangkat ataupun dari *API*.
2. **View**: Layer tampilan yang dilihat oleh pengguna yang hanya berfungsi untuk menampilkan data saja.
3. **Presenter**: Layer yang menghubungkan antara layer *View* dengan *Model*. Presenter mengambil data yang berasal dari *Model* untuk ditampilkan pada *View*. Setiap proses bisnis yang terjadi dilakukan pada layer ini.
Secara sederhana proses kerja MVP dapat digambarkan sebagai berikut:

### Menerapkan MVP dalam pengembangan aplikasi android
Pada latihan kali ini kita akan mencoba untuk menerapakan arsitektur Model-View-Presenter dalam pengembangan aplikasi android. Untuk layer *Model* kita akan mengakses API yaitu melalui TheMovieDb API untuk mendapatkan data. Selain itu, untuk berinteraksi dengan API, kita akan menggunakan salah satu library yang populer digunakan yaitu Retrofit 2.
Pertama-tama buat projek baru pada Android Studio lalu pilih *Empty Activity*. Setelah itu modifikasi Build.Gradle pada Module app pada bagian buildTypes dan dependencies menjadi seperti berikut:
```gradle
buildTypes {
debug {
buildConfigField("String",
"BASE_URL", "\"https://api.themoviedb.org/3/\"")
buildConfigField("String",
"BASE_IMAGE_URL", "\"https://image.tmdb.org/t/p/w200\"")
buildConfigField("String",
"BASE_BACKDROP_IMAGE_URL",
"\"https://image.tmdb.org/t/p/w500\"")
buildConfigField("String",
"API_KEY", "\"7012fc3c96c9f6f707e4edb9c9725718\"")
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
buildConfigField("String",
"BASE_URL", "\"https://api.themoviedb.org/3/\"")
buildConfigField("String",
"BASE_IMAGE_URL", "\"https://image.tmdb.org/t/p/w200\"")
buildConfigField("String",
"BASE_BACKDROP_IMAGE_URL",
"\"https://image.tmdb.org/t/p/w500\"")
buildConfigField("String",
"API_KEY", "\"7012fc3c96c9f6f707e4edb9c9725718\"")
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
implementation 'com.squareup.picasso:picasso:2.71828'
implementation 'com.squareup.retrofit2:retrofit:2.4.0'
implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
implementation 'com.squareup.okhttp3:okhttp:3.11.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.11.0'
implementation 'com.android.support:recyclerview-v7:28.0.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
```
Pada Build.Gradle, pertama kita telah menambahkan library yang akan digunakan. Library tersebut adalah Retrofit 2, OkHttp sebagai REST Client untuk berinteraksi dengan API, RecyclerView untuk menggunakan recyclerview dan Picasso untuk proses memuat gambar.
Selain itu pada buildTypes, kita telah menambahkan 1 varian baru yaitu debug yang akan gunakan dalam pengembangan aplikasi kali ini. Di dalam tiap varian, kita juga menambahkan beberapa konstanta yang akan digunakan untuk mengakses API MovieDb dengan menggunakan buildConfigField. Keunntungan kita menambahkan konstanta dalam varian adalah kita dapat memanggil Konstanta-konstanta tersebut dengan mudah pada file apapun.
Lalu pada AndroidManifest, tambahkan sebuah permision diluar tag <application> supaya aplikasi dapat mengakses internet.
```android
<uses-permission android:name="android.permission.INTERNET"/>
```
Selanjutnya, pada package utama, tambahkan beberapa package seperti berikut:

Supaya dapat berinteraksi dengan API, kita perlu menyiapkan beberapa file untuk mempersiapkan Retrofit dan OkHttp3 supaya dapat digunakan. Pertama-tama buat sebuah Object baru didalam package data->lib dengan nama OkHttpClientFactory. lalu isi file tersebut sebagai berikut:
```kotlin
object OkHttpClientFactory {
private const val DEFAULT_MAX_REQUEST = 30
fun create(): OkHttpClient {
val builder = OkHttpClient.Builder()
.readTimeout(120, TimeUnit.SECONDS)
.connectTimeout(120, TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
.addInterceptor(Interceptor {
val request = it.request().newBuilder()
.url(it.request()
.url()
.newBuilder()
.addQueryParameter("api_key",
BuildConfig.API_KEY).build())
.build()
it.proceed(request)
})
if (BuildConfig.DEBUG) {
val interceptor = HttpLoggingInterceptor()
interceptor.level = HttpLoggingInterceptor.Level.BODY
builder.addInterceptor(interceptor).build()
}
val dispatcher = Dispatcher()
dispatcher.maxRequests = DEFAULT_MAX_REQUEST
builder.dispatcher(dispatcher)
return builder.build()
}
}
```
Object OkHttpClientFactory digunakan sebagai HttpClient yang akan kita gunakan untuk me-request data ke API. Pada object tersebut, kita membuat suatu interceptor untuk menambahkan api key dari theMovieDB sehingga pada setiap metode request nilai api key akan selalu terlampir/
Lalu buat sebuah objek baru dengan nama ApiService. Object ini bertujuan untuk membuat Retrofit client yang akan membentuk URL ketika diakses. Gson disini bertujuan untuk menkonversi respon yang diberikan dari bentuk JSON menjadi bentuk sebuah object sehingga dapat digunakan oleh aplikasi.
```kotlin
object ApiService {
fun <S> createService(serviceClass: Class<S>,
okhttpClient: OkHttpClient,
baseURl: String): S {
val gson = GsonBuilder()
.create()
val retrofit = Retrofit.Builder()
.baseUrl(baseURl)
.client(okhttpClient)
.addConverterFactory(GsonConverterFactory
.create(gson))
.build()
return retrofit.create(serviceClass)
}
}
```
Buat sebuah kelas dengan nama UseCase. Kelas ini bertujuan sebagai perantara antara layer Presenter dengan layer Model. Kelas ini dibuat abstract karena kita akan mendefinisikan fungsi callApi pada kelas UseCase tiap endpoint.
```kotlin
abstract class UseCase {
abstract fun callApi()
companion object{
val apiRequest: Repository by lazy {
ApiService.createService(
Repository::class.java,
OkHttpClientFactory.create(),
BuildConfig.BASE_URL
)
}
}
}
```
Terakhir, buat tampilan beserta recylerview adapter nya. Kita akan menggunakan GridLayoutManager untuk menampilkan gambar dari list film tersebut. Modifikasi activity_main.xml menjadi:
```android
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".view.popularmovie.MainActivity">
<ProgressBar android:id="@+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"/>
<android.support.v7.widget.RecyclerView
android:id="@+id/rvMovie"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
```
Lalu buat layout untuk digunakan pada recyclerview dengan nama item_movie_list.xml.
```android
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/imgMovie"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true"
android:layout_gravity="center"/>
</LinearLayout>
```
Buat kelas MovieAdater untuk menampung data MovieItem
```kotlin
class MovieAdapter:
RecyclerView.Adapter<MovieAdapter.MovieViewholder>() {
val list = mutableListOf<MovieItem>()
var onMovieItemClickListener: OnMovieItemClickListener ?= null
override fun onCreateViewHolder(viewGroup: ViewGroup,
position: Int): MovieViewholder {
val view = LayoutInflater
.from(viewGroup.context)
.inflate(R.layout.item_movie_list, viewGroup,
false)
return MovieViewholder(view)
}
override fun getItemCount() = list.size
override fun onBindViewHolder(viewholder: MovieViewholder, position: Int) {
viewholder.bind(list[position])
}
inner class MovieViewholder(itemView: View)
: RecyclerView.ViewHolder(itemView) {
fun bind(movieItem: MovieItem){
Picasso.get()
.load(BuildConfig.BASE_IMAGE_URL +
movieItem.poster)
.into(itemView.imgMovie)
itemView.imgMovie.setOnClickListener {
onMovieItemClickListener?.onMovieItemClicked(movieItem)
}
}
}
interface OnMovieItemClickListener{
fun onMovieItemClicked(movieItem: MovieItem)
}
}
```
Terakhir, modifikasi kelas MainActivity menjadi seperti berikut:
```kotlin
class MainActivity : AppCompatActivity(), MainView,
MovieAdapter.OnMovieItemClickListener{
var adapter: MovieAdapter?= null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
supportActionBar?.apply {
title = "Popular Movie"
}
rvMovie.setHasFixedSize(true)
rvMovie.layoutManager =
GridLayoutManager(this, 2)
adapter = MovieAdapter()
adapter?.onMovieItemClickListener = this
rvMovie.adapter = adapter
}
override fun onMovieItemClicked(movieItem: MovieItem){
}
}
```
Setelah seleasi, waktunya untuk menerapkan arsitektur MVP dalam aplikasi kita. Tahap yang akan kita lakukan adalah sebagai berikut:

Sekarang, kita akan membuat suatu request untuk mendapatkan daftar film populer untuk ditampilkan pada tampilan.
#### 1. Create Repository
Pertama, buat sebuah interface dengan nama Repository pada package data->lib. Interface ini akan kita gunakan untuk memuat semua endpoint API yang akan kita akses. lalu tambahkan sebuah fungsi sebagai berikut
```kotlin
@GET("discover/movie?sort_by=popularity.desc")
fun getListPopularMovie(): Call<MovieResponse>
```
Fungsi tersebut akan membuat aplikasi berinteraksi dengan endpoint https://api.themoviedb.org/3/discover/movie?api_key=7012fc3c96c9f6f707e4edb9c9725718&sort_by=popularity.desc dengan metode GET() dan mengembalikan data dengan tipe MovieResponse. Oleh sebab itu kita perlu membuat sebuah response model untuk menampung data dengan tipe MovieResponse. Respon yang dikembalikan berupa:
```json
{
page: 1,
total_results: 431334,
total_pages: 21567,
results: [
{
vote_count: 581,
id: 384018,
video: false,
vote_average: 6.5,
title: "Fast & Furious Presents: Hobbs & Shaw",
popularity: 456.135,
poster_path: "/keym7MPn1icW1wWfzMnW3HeuzWU.jpg",
original_language: "en",
original_title: "Fast & Furious Presents: Hobbs & Shaw",
genre_ids: [
28
],
backdrop_path: "/hpgda6P9GutvdkDX5MUJ92QG9aj.jpg",
adult: false,
overview: "A spinoff of The Fate of the Furious, focusing on Johnson's US Diplomatic Security Agent Luke Hobbs forming an unlikely alliance with Statham's Deckard Shaw.",
release_date: "2019-08-01"
},
{
vote_count: 1739,
id: 420818,
video: false,
vote_average: 7.1,
title: "The Lion King",
popularity: 265.913,
poster_path: "/2bXbqYdUdNVa8VIWXVfclP2ICtT.jpg",
original_language: "en",
original_title: "The Lion King",
genre_ids: [
12,
16,
10751,
18,
28
],
backdrop_path: "/1TUg5pO1VZ4B0Q1amk3OlXvlpXV.jpg",
adult: false,
overview: "Simba idolises his father, King Mufasa, and takes to heart his own royal destiny. But not everyone in the kingdom celebrates the new cub's arrival. Scar, Mufasa's brother—and former heir to the throne—has plans of his own. The battle for Pride Rock is ravaged with betrayal, tragedy and drama, ultimately resulting in Simba's exile. With help from a curious pair of newfound friends, Simba will have to figure out how to grow up and take back what is rightfully his.",
release_date: "2019-07-12"
}
]
}
```
Pada respon tersebut terdapat object results yang isinya adalah array dari object sebuah film. kita akan mengubah object results beserta isinya menjadi sebuah kelas objek yang dapat digunakan oleh aplikasi.
Pada package model buat sebuah Data Class dengan nama MovieResponse dan MovieItem. Data Class pada kotlin berfungsi sebagai kelas untuk menampung suatu data. isi kedua Data Class tersebut dengan kode berikut:
```kotlin
data class MovieResponse(
@SerializedName("results")
val result: List<MovieItem>
)
```
```kotlin
data class MovieItem(
@SerializedName("original_title")
val title: String?,
@SerializedName("poster_path")
val poster: String?,
@SerializedName("id")
val id: String?,
@SerializedName("overview")
val overview: String?,
@SerializedName("homepage")
val homePage: String?,
@SerializedName("backdrop_path")
val backdrop: String?,
@SerializedName("original_language")
val originalLanguage: String?,
@SerializedName("genres")
val genres: MutableList<Genre>
)
data class Genre(
@SerializedName("id")
val id: String?,
@SerializedName("name")
val name: String?
)
```
#### 2. Create Use Case
Pada tahap ini, kita akan membuat sebuah kelas UseCase yang bertindak sebagai interactor antara Presenter dan Model. Pada package Domain->Movie buat sebuah kelas dengan nama GetPopularMovieUseCase lalu isi kelas tersebut sebagai berikut:
```kotlin
class GetPopularMovieUseCase: UseCase() {
var onPopularMovieCallback: OnPopularMovieCallback ?= null
override fun callApi() {
val request =
apiRequest.getListPopularMovie()
request.enqueue(object: Callback<MovieResponse>{
override fun onResponse(call: Call<MovieResponse>,
response: Response<MovieResponse>) {
if (response.isSuccessful){
onPopularMovieCallback?.
onPopularMovieSuccess(response
.body()?.result)
}else{
onPopularMovieCallback?.
onPopularMovieFailed("Request gagal")
}
}
override fun onFailure(call: Call<MovieResponse>,
t: Throwable) {
onPopularMovieCallback?.
onPopularMovieFailed("Request gagal")
}
})
}
interface OnPopularMovieCallback{
fun onPopularMovieSuccess(movies: List<MovieItem>?)
fun onPopularMovieFailed(message: String)
}
}
```
Pada kelas GetPopularMovieUseCase, terjadi request data dari aplikasi dengan API getListPopularMovie yang ditunjukan dengan perintah:
```kotlin
val request = apiRequest.getListPopularMovie()
```
Proses request data tersebut berlangsung secara asynchronus, ditunjukkan dengan perintah enqueue dan akan menghasilkan 1 dari 2 kemungkinan yaitu apakah proses tersebut berhasil atau gagal yang ditunjukan oleh:
```kotlin
override fun onResponse(call: Call<MovieResponse>,
response: Response<MovieResponse>) {
if (response.isSuccessful){
onPopularMovieCallback?.
onPopularMovieSuccess(response
.body()?.result)
}else{
onPopularMovieCallback?.
onPopularMovieFailed("Request gagal")
}
}
override fun onFailure(call: Call<MovieResponse>,
t: Throwable) {
onPopularMovieCallback?.
onPopularMovieFailed("Request gagal")
}
```
Lalu untuk mengembalikan respon ke presenter, dibutuhkan sebuah callback yang menangani ketika proses tersebut berhasil atau gagal. Maka dibuatlah sebuah interface bernama OnPopularMovieCallback yang memiliki 2 fungsi yaitu onPopularMovieSuccess() yang membawa data array MovieItem dan onPopularMovieFailed() yang membawa data String.
```kotlin
interface OnPopularMovieCallback{
fun onPopularMovieSuccess(movies: List<MovieItem>?)
fun onPopularMovieFailed(message: String)
}
```
Setelah itu callback tersebut dipasang bila proses request data berhasil dan gagal. Bila respon tersebut berhasil maka akan mengembalikan nilai data array MovieItem ke presenter, dan bila gagal akan mengembalikan nilai String yaitu pesan Request Gagal.
#### 3. Create Interface for View
Buat satu kelas interface baru dengan nama MainView dalam package view->popularmovie. Pada kelas ini, kita akan membuat fungsi-fungsi yang nantinya akan dipanggil oleh presenter untuk melakukan perubahan pada tampilan.
```kotlin
interface MainView {
fun showMovieList(movies: List<MovieItem>?)
fun showErrorMessage(message: String)
fun showHideProgressBar(isShown: Boolean)
}
```
#### 4. Implement View Interface to Activity/Fragment
Sekarang, implementasikan MainView pada kelas MainActivity. lalu implementasikan ketiga fungsi tersebut pada MainActivity
```kotlin
override fun showMovieList(movies: List<MovieItem>?) {
if (movies != null){
adapter?.list?.addAll(movies.toMutableList())
adapter?.notifyDataSetChanged()
}
}
override fun showErrorMessage(message: String) {
Toast.makeText(this, message,
Toast.LENGTH_LONG).show()
}
override fun showHideProgressBar(isShown: Boolean) {
if (isShown){
progressBar.visibility = View.VISIBLE
}else{
progressBar.visibility = View.GONE
}
}
```
Fungsi showMovieList digunakan untuk mengisi data adapter dengan list dari MovieItem ketika proses request data dengan API berhasil dilakukan. setelah itu wajib memanggil fungsi notifyDataSSetChanged() untuk memberi tahu pada adapter jika data pada adapter sudah berubah dan adapter akan menampilkan data terbaru.
Fungsi showErrorMessage digunakan untuk menampilkan pesan error ketika interaksi dengan API gagal.
Fungsi showHideProgressBar digunakan untuk menampilkan dan menyembunyikan progress bar.
#### 5. Create Interface for Presenter
Tahap selanjutnya adalah membuat Interface untuk presenter yang akan digunakan. disini kita akan membuat fungsi fungsi yang akan dapat dipanggil oleh View untuk berinteraksi dengan Presenter. Buat interface baru dengan nama IMainPresenter pada package view->popularmovie
```kotlin
interface IMainPresenter {
fun getMovieList()
}
```
#### 6. Create Presenter Class
Buat kelas Presenter dengan nama MainPresenter dengan konstruktor utamanya interface MainView.
```kotlin
class MainPresenter(val mainView: MainView){
}
```
#### 7. Implement Presenter Interface to Presenter Class
Lalu, implementasikan IMainPresenter pada MainPresenter
```kotlin
class MainPresenter(val mainView: MainView): IMainPresenter,
override fun getMovieList() {
mainView.showHideProgressBar(true)
}
override fun onPopularMovieSuccess(movies: List<MovieItem>?) {
mainView.showHideProgressBar(false)
mainView.showMovieList(movies)
}
override fun onPopularMovieFailed(message: String) {
mainView.showHideProgressBar(false)
mainView.showErrorMessage(message)
}
}
```
#### 8. Implement Use Case on Presenter
Inisialisasi GetPopularMovieUseCase pada Presenter, lalu implementasikan callback yang terdapat pada GetPopularMovieUseCase pada Presenter
```kotlin
class MainPresenter(val mainView: MainView): IMainPresenter,
GetPopularMovieUseCase.OnPopularMovieCallback{
var getPopularMovieUseCase:
GetPopularMovieUseCase ?= null
init {
getPopularMovieUseCase = GetPopularMovieUseCase()
getPopularMovieUseCase?.onPopularMovieCallback = this
}
override fun getMovieList() {
mainView.showHideProgressBar(true)
getPopularMovieUseCase?.callApi()
}
override fun onPopularMovieSuccess(movies: List<MovieItem>?) {
mainView.showHideProgressBar(false)
mainView.showMovieList(movies)
}
override fun onPopularMovieFailed(message: String) {
mainView.showHideProgressBar(false)
mainView.showErrorMessage(message)
}
}
```
Pada fungsi getMovieList, akan dilakukan proses request data dengan API popular movie, oleh karena itu untuk menunjukan ke user bahwa proses sedang berlangsung, aplikasi akan menampilkan progress bar.
Ketika proses tersebut berhasil, pada onPopularMovieSuccess, nilai list MovieItem akan dikembalikan lalu nilai tersebut akan diisi kepada adapter pada View. Lalu progress bar dihilangkan untuk menunjukan bahwa proses telah berhasil.
Sebaliknya, jika proses request data gagal, Progress bar akan dihilangkan dan akan memunculkan pesan error pada view dari nilai string yang dibawa dimana pada kasus ini pesan tersebut akan ditampilkan dengan sebuah toast.
#### 9. Integrate View to Presenter
Tahap terakhir pada proses ini adalah mengintegrasikan View dengan Presenter lalu View melakukan request data popular movie melalui presenter. Pada kelas MainActivity inisialisasi kelas MainPresenter lalu lakukan request data melalui objek mainPresenter. Lalu tambahkan perintah didalam fungsi onMovieItemClicked untuk menampilkan judul film ketika film tersebut dipencet. Modifikasi fungsi onCreate dan onMovieItemClicked sehingga menjadi seperti berikut:
```kotlin
var mainPresenter: IMainPresenter?= null
var adapter: MovieAdapter?= null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
supportActionBar?.apply {
title = "Popular Movie"
}
rvMovie.setHasFixedSize(true)
rvMovie.layoutManager =
GridLayoutManager(this, 2)
adapter = MovieAdapter()
adapter?.onMovieItemClickListener = this
rvMovie.adapter = adapter
mainPresenter = MainPresenter(this)
mainPresenter?.getMovieList()
}
override fun onMovieItemClicked(movieItem: MovieItem) {
Toast.makeText(this, movieItem.title, Toast.LENGTH_LONG).show()
}
```
Setelah semuanya selesai, jalankan kode yang telah dibuat pada device/emulator yang digunakan. Hasilnya akan seperti ini:

