Before ViewBinding, developers used findViewById() or Kotlin synthetic extensions to access views. Both approaches have problems — runtime crashes from wrong IDs, null references, and boilerplate code.
Old way:
findViewById<TextView>(R.id.tvTitle) → Runtime lookup, typo-prone
ViewBinding:
binding.tvTitle.text = "Hello" → Compile-time safe, generated class
Problems with findViewById
Runtime errors if ID is misspelled or missing from layout
Repetitive boilerplate in every Activity and Fragment
No null safety — can return null if view not found
Slower — dictionary lookup on every call
ViewBinding vs DataBinding vs findViewById
Approach
Type Safety
XML Expressions
Use Case
findViewById
No
No
Legacy — avoid in new code
ViewBinding
Yes
No
Default choice for view access
DataBinding
Yes
Yes
MVVM, LiveData, two-way binding
2Setup
Enable ViewBinding and/or DataBinding in your app module's Gradle file. Android Studio generates binding classes automatically at build time.
Note: After enabling, sync Gradle and rebuild. Binding classes appear in app/build/generated/data_binding_base_class_source_out/. You import them directly — no manual creation needed.
3Using in Activity
In an Activity, inflate the binding class instead of calling setContentView(R.layout.activity_main). Access all views through the binding object.
Kotlin — User.ktdata class User(
val name: String,
val email: String,
val avatarUrl: String
)
Activity with DataBinding
Kotlin — ProfileActivity.ktclass ProfileActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityProfileBinding.inflate(layoutInflater)
setContentView(binding.root)
val user = User("Nikhil Kumar", "nikhil@example.com", "https://example.com/avatar.jpg")
binding.user = user
}
}
Binding expression syntax
Expression
Meaning
@{user.name}
One-way bind to property
@{viewModel.isLoading ? View.VISIBLE : View.GONE}
Conditional visibility
@{() -> handler.onClick()}
Click listener lambda
@={viewModel.searchQuery}
Two-way binding (see next section)
6Two-Way Binding
Two-way binding syncs data in both directions — when the user edits an EditText, the bound variable updates automatically, and vice versa. Use @={} instead of @{}.
One-way: @{viewModel.name} → Data flows to UI only
Two-way: @={viewModel.name} → Data flows UI ↔ ViewModel
Observable ViewModel field
Kotlin — LoginViewModel.ktclass LoginViewModel : ViewModel() {
val email = ObservableField("")
val password = ObservableField("")
val isFormValid = ObservableBoolean(false)
init {
// React when fields change
email.addOnPropertyChangedCallback(object : Observable.OnPropertyChangedCallback() {
override fun onPropertyChanged(sender: Observable?, propertyId: Int) {
validateForm()
}
})
}
private fun validateForm() {
isFormValid.set(
!email.get().isNullOrBlank() && !password.get().isNullOrBlank()
)
}
}
Kotlin — LoginActivity.ktval binding = ActivityLoginBinding.inflate(layoutInflater)
setContentView(binding.root)
val viewModel = ViewModelProvider(this)[LoginViewModel::class.java]
binding.viewModel = viewModel
binding.lifecycleOwner = this // Required for LiveData binding
7Binding Adapters
Binding Adapters are custom methods annotated with @BindingAdapter that map XML attributes to view properties — load images from URLs, set custom colors, format dates, etc.
Load image from URL
Kotlin — BindingAdapters.kt@BindingAdapter("imageUrl")
fun loadImage(imageView: ImageView, url: String?) {
if (url.isNullOrEmpty()) return
imageView.load(url) {
placeholder(R.drawable.placeholder)
error(R.drawable.error_image)
crossfade(true)
}
}
// Usage in XML:
// app:imageUrl="@{user.avatarUrl}"
Kotlin — ProfileFragment.ktoverride fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val viewModel = ViewModelProvider(this)[ProfileViewModel::class.java]
binding.viewModel = viewModel
binding.lifecycleOwner = viewLifecycleOwner // Critical for LiveData!
viewModel.loadUser("user_42")
}
Critical: Always set binding.lifecycleOwner = this (Activity) or viewLifecycleOwner (Fragment). Without it, LiveData observers in XML won't update the UI.
ViewBinding vs DataBinding — when to use which
Scenario
Recommendation
Simple screens, manual click handlers
ViewBinding
MVVM with LiveData/Flow
DataBinding
Forms with two-way field sync
DataBinding + @={}
RecyclerView ViewHolders
ViewBinding (lighter)
New project with Jetpack Compose
Compose state (skip XML binding)
9Hands-On Exercises
1
Enable ViewBinding in Gradle. Refactor an existing Activity from findViewById to ActivityMainBinding.
2
Convert a Fragment to ViewBinding with the nullable _binding pattern and proper cleanup in onDestroyView.
3
Use ViewBinding in a RecyclerView adapter ViewHolder for a contact list item.
4
Enable DataBinding. Create a profile layout with <layout> and bind a User object to TextViews using @{} expressions.
5
Build a login form with two-way binding on email and password fields. Disable the login button when fields are empty.
6
Write a BindingAdapter to load images from URLs into ImageView using Coil.
7
Connect a ViewModel's LiveData to a DataBinding layout. Show a ProgressBar while loading and display user data when ready.
8
Bonus: Create a visibleIf BindingAdapter and use it to toggle error messages based on a LiveData validation state.
ViewBinding & DataBinding MCQ Practice
10 Basic MCQs10 Advanced MCQs
10 Basic ViewBinding & DataBinding MCQs
1
ViewBinding generates?
ASQL tables
BType-safe binding class per layout XML
CManifest entries
DDex files
Explanation: ActivityMainBinding wraps views from activity_main.xml.
2
Enable ViewBinding in Gradle?
Aadb enable
Bmanifest merge
CbuildFeatures { viewBinding = true }
DProGuard only
Explanation: Set in module build.gradle.kts under android buildFeatures.
3
ViewBinding replaces?
AServices
BSQLite
CIntents
DfindViewById calls
Explanation: Generated binding exposes views as properties — null-safe for existing IDs.