Data binding avoids repeatedly traversing of the view hierarchy by findViewById (an expensive transaction) by generating a helper class at compile-time which the Activity/Fragment can refer to. It can also be used to bind a data class to a View so the View can access the data easily.
The idea behind data binding is to create an object that connects/maps/binds two pieces of distant information together at compile time, so that you don’t have to look for it at runtime. The object that surfaces these bindings to you is called the Binding object. You create an instance of the binding object, and then reference views through the binding object with no extra overhead.
See how you can combine this with Lifecycle components (ViewModel and LiveData) by going to this post.
In build.gradle (Module: app)
android {
compileSdkVersion 28
defaultConfig {
applicationId "com.example.aboutme"
minSdkVersion 19
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
dataBinding {
enabled = true
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
In each layout file wrap in <layout> tag, and optionally bind to a data class if you want to:
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<data>
<variable
name="myName" // Alias for this data variable
type="com.example.aboutme.MyName" /> // Points to the Kotlin data class
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingStart="@dimen/padding"
android:paddingEnd="@dimen/padding"
tools:context=".MainActivity">
<TextView
android:id="@+id/name_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@={myName.name}" // Refers to the variable declared above, with the 'name' property specified
android:textAlignment="center"/>
<TextView
android:id="@+id/nickname_text"
style="@style/NameStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@={myName.nickname}" // Refers to the variable declared above, with the 'nickname' property specified
android:textAlignment="center"
android:visibility="visible"/>
</LinearLayout>
</layout>
In Activity:
import ...
import com.example.aboutme.databinding.ActivityMainBinding // import this auto-generated class based on Activity name
import android.databinding.DataBindingUtil // This should be imported automatically
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding // Declare this class based on Activity name. A rebuild is required for this to be generated and remove error warning
private val myName: MyName = MyName("Al", "AldeZu") // Create an instance of the data class
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this,R.layout.activity_main) // Initialise the binding variable with the view
binding.myName = myName // Set the value of the myName variable used in the layout file to the data class
// findViewById<Button>(R.id.done_button).setOnClickListener { // Don't do this any more
binding.doneButton.setOnClickListener { // Use auto-generated view name instead of invoking costly findViewById
addNickname(it)
}
}
private fun addNickname(view: View) {
binding.apply { // Use Kotlin's apply function to make the code easier to read when multiple views are referenced
myName?.nickname = nicknameEdit.text.toString()
invalidateAll() // After changing data we need to invalidate all the references to force a refresh
nicknameEdit.visibility = View.GONE
doneButton.visibility = View.GONE
nicknameText.visibility = View.VISIBLE
}
val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(view.windowToken, 0)
}
}
data class MyName(var name: String = "", var nickname: String = "")