Android Kotlin extensions RIP
The Android Kotlin Extensions Gradle plugin almost 3 years ago brought two new conveniences to Android development in Kotlin:
Here we are not talking about KTX, it’s all about Android Kotlin Extensions Gradle plugin
• Synthetics let you replace calls to
findViewById with kotlinx.android.synthetic bindings.
- Parcelize allows you to remove boilerplate and easily create Parcelables through the @Parcelize annotation.
Major drawbacks of Kotlin synthetics
- They pollute the global namespace
- They don’t expose nullability information
- They only work in Kotlin code
Replacement for Kotlin synthetics
Recommended approach that you should follow is ViewBinding
Let’s see step by step how we can migrate to viewBinding
// 1. step enable viewBinding
buildFeatures {
viewBinding true
}
Now rebuild your project and you are good to go: 💃
- Now for all the layouts in your project respected dataBinding classes will be generated that you can find in the following path:
- Now we can use viewBinding in our respected activities and fragments:
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) // old way to set the content
// setContentView(R.layout.activity_main) binding = ActivityMainBinding.inflate(layoutInflater)
binding.tv.setOnClickListener {
// perform your action onClick
} // get the root view from binding and set it to as ContentView.
setContentView(binding.root)
}
}
- binding.root : It referes to the parentView of Layout
In case of fragments we need to manage the binding more carefully otherwise it might leads to unexpected memory leaks
- Let’s see in case of fragments how we can use viewBindng:
class SampleFragment : Fragment() {
private var binding: FragmentSampleBinding? = null
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
// Inflate the layout for this fragment
binding = FragmentSampleBinding.inflate(inflater, container, false)
return binding?.root!!
}
override fun onDestroyView() { // we need to reset the binding to null once fragment's view get's destroyed which helps to avoid memory leaks
binding = null super.onDestroyView()
}
}
Accessing included layouts:
- Let’s check how we can access the views of included layouts :
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="64dp"
android:clickable="true"
android:focusable="true"
android:text="Hello World!"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/logo" /> <!-- in case of include, we must have to provide id tag to generate proper viewBinding variable which helps to access the views from included layout-->
<include
android:id="@+id/included"
layout="@layout/include_sample_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv" />
</androidx.constraintlayout.widget.ConstraintLayout>
- Content of include_sample_layout:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="64dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
- Here is the sample of accessing tv2 from included layout :
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
binding.included.tv2.text = "Included textview"
setContentView(binding.root)
}
}
If you want to ignore any view from viewBinding or layout file you can do that by adding following properotes at appropriate levels in your layout file:
- Ignore view from generating viewBinding : Don’t provide id property for view. ViewBinding will simply ignore that view.
<!-- the following view will be ignored by ViewBinding -->
<ImageView
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_margin="32dp"
android:importantForAccessibility="no"
android:src="@drawable/ic_launcher_background"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
- Ignore the entire layout file : Add tools:viewBindingIgnore=”true” tag to the parent view of the layout file that you want to ignore by viewBinding generation
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:viewBindingIgnore="true"
tools:context=".MainActivity">
Replacement for Parcelize
- In your respect module replace the old android-kotlin-extensions plugin with the new one which is specific to Parceable
plugins {
id 'kotlin-parcelize'
}
The
kotlin-parcelize
plugin provides aParcelable
implementation generator.
You need to use the following package for Parcelize 👇
// update all the imports from new package and you are good to go 💃
import kotlinx.parcelize
You should start using the standalone kotlin-parcelize plugin instead of android-kotlin-extensions.
- Here is the sample code with all the code samples that we have discussed in this article: