Android View Binding

Helder Pinhal
Nov 13 2020
Posted in Engineering & Technology

Using View Binding to replace findViewById

The new View Binding feature allows you to write simpler, more concise and safer code to interact with your views.

Since this is built into Android Studio, it requires no dependencies. All we need to do is enable this feature in our module's build.gradle.

android {
    buildFeatures {
        viewBinding true
    }
}

When this feature is enabled, a binding class is generated for each XML layout within that module. If you want to disable view binding for a particular layout, you can simply annotate the root view of that layout file with tools:viewBindingIgnore="true".

Each binding class contains a reference to the root view and to each view with an ID. As an example, let's look at the layout profile_header.xml below.

<?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="wrap_content">

    <ImageView
        android:id="@+id/avatar"
        android:layout_width="48dp"
        android:layout_height="48dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:ignore="ContentDescription"
        tools:src="@drawable/ic_account_circle_black_24dp" />

    <TextView
        android:id="@+id/name"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:ellipsize="end"
        android:lines="1"
        android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
        app:layout_constraintBottom_toTopOf="@id/email"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@id/avatar"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_chainStyle="packed"
        tools:text="John Doe" />

    <TextView
        android:id="@+id/email"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:ellipsize="end"
        android:lines="1"
        android:textAppearance="@style/TextAppearance.MaterialComponents.Caption"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@id/avatar"
        app:layout_constraintTop_toBottomOf="@id/name"
        tools:text="support@notifica.re" />

</androidx.constraintlayout.widget.ConstraintLayout>

The generated ProfileHeaderBinding class will contain the following properties:

  • rootConstraintLayout
  • avatarImageView
  • name & emailTextView

Let's look at some examples!

Using it from an Activity

class SettingsActivity : AppCompatActivity() {

    private lateinit var binding: ActivitySettingsBinding

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

        binding.name.text = "John Doe"
        // ...
    }
}

Using it from a Fragment

class SettingsFragment : Fragment() {

    private lateinit var binding: FragmentSettingsBinding

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

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        binding.name.text = "John Doe"
        // ...
    }
}

Using it in a RecyclerView.ViewHolder

The generated binding class also provides a static bind method to handle an existing view instead of inflating it.

class ProfileViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    private val binding = ProfileHeaderBinding.bind(itemView)

    fun bind() {
        binding.name.text = "John Doe"
        // ...
    }
}

Advantages of view binding

There's two major advantages of view binding when compared to the traditional findViewById.

  1. Type safety. The properties of the generated class have their types matching the ones if the XMl layout. This prevents class cast exceptions during runtime, failing instead at build time.
  2. Null safety. If a certain view is not present in all variations of a given layout it will be marked as nullable. It will force you to handle it in your code, preventing some more exceptions during runtime.

Wrapping it up

For more information, you can check the official documentation. Additionally, Google provides an open-source sample.

If you liked this article or have something to add, we are available, as always, via our Support Channel.

Keep up-to-date with the latest news