[Late Post] Update ke Android Studio 4.1: Ubah Style ke Theme, KAE ke ViewBinding, AsyncTask ke Executor

Ahmad Arif Faizin
5 min readOct 27, 2023

Goal:
Ubah semua project ke format 4.1

  • Ubah styles ke theme & ubah tampilan GIF ke material design
  • Ubah KAE ke ViewBinding
  • Jika butuh Parcelize tambahkan kotlin-parcelize

Step Update Theme MaterialComponent :

1. Tambahkan Library Material sekalian Update versi library

  • Gradle 6.5
  • AGP 4.1.0
  • Kotlin 1.4.10
  • compileSdkVersion & targetSdkVersion 30
  • Library lain yg butuh update
  • Sesuaikan format build.gradle baru

Kotlin

//format plugin menggunakan DSL
plugins {
id 'com.android.application'
id 'kotlin-android'
}

android {
...
//tambahan options
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
// hapus filetree
//hapus -jdk7 di library kotlin
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.2.1'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
testImplementation 'junit:junit:4.13.1'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}

Java

plugins {
id 'com.android.application'
}
android {
compileSdkVersion 30
buildToolsVersion "30.0.2"
defaultConfig {
applicationId "com.dicoding.myjavaapplication"
minSdkVersion 21
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.2.1'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
testImplementation 'junit:junit:4.13.1'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}

2. Ubah Colors.xml

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

3. Buat themes : Klik kanan values -> new -> values resource file -> file name : themes

<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.MyViewAndViews" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/purple_500</item>
<item name="colorPrimaryVariant">@color/purple_700</item>
<item name="colorOnPrimary">@color/white</item>
<!-- Secondary brand color. -->
<item name="colorSecondary">@color/teal_200</item>
<item name="colorSecondaryVariant">@color/teal_700</item>
<item name="colorOnSecondary">@color/black</item>
<!-- Status bar color. -->
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
</style>
</resources>

Note: Sesuaikan style name dengan nama project

Tips: Jika sudah pernah bikin sekali, bisa copy paste aja.

4. Buat themes (night) : Klik kanan values -> new -> values resource file -> file name : themes -> pilih Night Mode -> klik >> -> Pilih Night -> OK

<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.MyViewAndViews" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/purple_200</item>
<item name="colorPrimaryVariant">@color/purple_700</item>
<item name="colorOnPrimary">@color/black</item>
<!-- Secondary brand color. -->
<item name="colorSecondary">@color/teal_200</item>
<item name="colorSecondaryVariant">@color/teal_200</item>
<item name="colorOnSecondary">@color/black</item>
<!-- Status bar color. -->
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
</style>
</resources>

Note: Sesuaikan style name dengan nama project

Tips: Jika sudah pernah bikin sekali, bisa copy paste aja. pada folder values-night

5. Hapus Styles.xml

6. Ganti theme di AndroidManifest.xml

HTML/XML

<application
...
android:theme="@style/Theme.MyViewAndViews">
...
</application>

7. Jalankan dan buat GIF

8. Update GIF dan Kode pada platform. ex: https://www.dicoding.com/academies/51/tutorials/1221

Sample commit : https://github.com/dicodingacademy/a165-android-expert-labs/commit/3df8f8930ab7a0c831305ed5010d3405b8328918

Step Update KAE ke ViewBinding (Kotlin & Java) :

1. Hapus plugin kotlin-android-android

plugins {
id 'com.android.application'
id 'kotlin-android'
}

2. enable view binding

android {
...
buildFeatures {
viewBinding = true
}
}

3. Tambahkan ViewBinding di Activity

class MainActivity : AppCompatActivity() {

private lateinit var binding: ActivityMainBinding

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
}
}

4. Ganti misal ed_email jadi binding.edEmail

Tips: Gunakan replace untuk langsung ganti semua

Sample commit : https://github.com/dicodingacademy/a165-android-expert-labs/commit/dec307ff603e0ec7b561690f5093063ca908fcb2

Warn:
Kalu di Frament pastikan di destroy, caranya seperti ini:

private var _binding: FragmentFavoriteBinding? = null
private val binding get() = _binding!!

override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentFavoriteBinding.inflate(inflater, container, false)
return binding.root
}
...
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}

Sample Commit : https://github.com/dicodingacademy/a165-android-expert-project/commit/ab0f667d5ff30dcac79267abac54a45db762709f

You might feel a bit uneasy about the double bangs !!, but all we are saying here is that we are sure this field will not be null when we use it (in the lifecycle between onCreateView and onDestroyView), which is exactly the same we are saying when we use lateinit var. https://proandroiddev.com/avoiding-memory-leaks-when-using-data-binding-and-view-binding-3b91d571c150

Sumber: https://proandroiddev.com/migrating-the-deprecated-kotlin-android-extensions-compiler-plugin-to-viewbinding-d234c691dec7

3. Kotlin-Parcelize

ternyata kotlin-parcelize harus pakai kotlin 1.4.20-M2.. ndak bisa kalau pakai1.4.10.. Jadi untuk sementara android-extensionnya dibiarin dulu aja (ndak dihapus) kalau butuh parcelize, tapi syntetic-nya tetap diganti ke Viewbinding

Update : Sekarang sudah ada 1.4.20 Jadi kotlin-android-extension bisa dihapus dan ganti dengan kotlin-parcelize

Lalu ganti import

import kotlinx.android.parcel.Parcelize

jadi

import kotlinx.parcelize.Parcelize

4. Binding di Adapter

tinggal tambahkan binding di ViewHolder, dan kalau butuh akses setOnclickListener bisa pakai binding.root

Kotlin

inner class ListViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val binding = ItemListTourismBinding.bind(itemView)
fun bind(data: TourismEntity) {
with(binding) {
Glide.with(itemView.context)
.load(data.image)
.into(ivItemImage)
tvItemTitle.text = data.name
tvItemSubtitle.text = data.address
}
}

init {
binding.root.setOnClickListener {
onItemClick?.invoke(listData[adapterPosition])
}
}
}

Sample commit : https://github.com/dicodingacademy/a165-android-expert-project/commit/6fa3042e8e77921a68ae48a7a8d992d3c868aa1a

5. Binding untuk includes layout
Layout includes harus diberi id

<include 
android:id="@+id/content"
layout="@layout/content_detail_main" />

Untuk mengambil id yang di dalam content_detail_tourism bisa dengan cara

binding.content.tvDetailDescription.text = …

Sample commit: https://github.com/dicodingacademy/a165-android-expert-project/commit/d76379e1889e7d3a6f441a034f5d39f13a0919ef

6. AsyncTask to Executor

ExecutorService executor = Executors.newSingleThreadExecutor();
Handler handler = new Handler(Looper.getMainLooper());
executor.execute(new Runnable() {
@Override
public void run() {
//do background process
handler.post(new Runnable() {
@Override
public void run() {
//do ui update
}
});
}
});

Lambda Java 8 version

ExecutorService executor = Executors.newSingleThreadExecutor();
Handler handler = new Handler(Looper.getMainLooper());
executor.execute(() -> {
//do background process
handler.post(() -> {
//do ui update
});
});

7. Switch dan Interface ganti langsung

- stepnya hapus implement View.OnClickListener,

- ganti setOnClickListener() jadi
setOnClickListener{ //kode } (Kotlin)

setOnClickListener ( v-> {//kode }); (Java)

- pindah kode yg di dalam onClick ke {},

- hapus onClick

Misal

class HomeFragment : Fragment(), View.OnClickListener {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val btnCategory: Button = view.findViewById(R.id.btn_category)
btnCategory.setOnClickListener(this)
}
override fun onClick(v: View?) {
when (v?.id){
R.id.btn_category -> {
val mCategoryFragment = CategoryFragment()
val mFragmentManager = parentFragmentManager
mFragmentManager.commit {
addToBackStack(null)
replace(R.id.frame_container, mCategoryFragment, CategoryFragment::class.java.simpleName)
}
}
}
}
}

Jadi

class HomeFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val btnCategory: Button = view.findViewById(R.id.btn_category)
btnCategory.setOnClickListener {
val mCategoryFragment = CategoryFragment()
val mFragmentManager = parentFragmentManager
mFragmentManager.commit {
addToBackStack(null)
replace(R.id.frame_container, mCategoryFragment, CategoryFragment::class.java.simpleName)
}
}
}
}

Sample commit: https://github.com/dicodingacademy/a14-made-labs5/commit/51fbabc3669bed0229acf14075aba12995b7d0d5?branch=51fbabc3669bed0229acf14075aba12995b7d0d5&diff=split

Other:

- setDrawerLayout udah deprecated, cukup ganti dengan setOpenableLayout

- ganti warna background material button belum bisa pakai backgroundColor, tapi bisa pake backgroundColorTint, tapi minSdkVersion diganti 21

- Viewbindingdelegate https://proandroiddev.com/make-android-view-binding-great-with-kotlin-b71dd9c87719

Untuk yg menggunakan Spinner:
https://blog.usejournal.com/there-is-no-material-design-spinner-for-android-3261b7c77da8

--

--