Synthetics imports are deprecated, Migrate to Jetpack view binding.

In general, findViewById is one of the most annoying boilerplate codes in Android development because it can result in some unexpected results. Here we will look at the View Binding library as an enhanced technique instead of findViewById in Android programming in order to improve efficiency and performance.

View binding is a feature that makes writing code that interacts with views easier.

Setup View Binding

Docs do an excellent job of describing how to take the next step. Basically add view binding to your project by knowing your Gradle plugin version. If you’re using Gradle 3.6.0 use like this:

android {
    ...
    viewBinding {
        enabled = true
    }
}

ViewBinding has been renamed to buildFeatures in Android Gradle Plugin 4.0 or higher, and you must use it as follows:

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

When you enable view binding in the compiler, it will recompile your project and produce binding objects for each layout automatically. View binding will build a class called ActivityMainBinding if you have a layout file called activity_main.xml. In actuality, the binding class’s name is created by transforming the XML file’s name to camel case and adding the word “Binding”.

// file name : about_layout.xml
<FrameLayout ... >
    <TextView android:id="@+id/text_name" ... />
    <ImageView ... />
    <Button android:id="@+id/button_continue" ... />
</FrameLayout>

This class has two fields: a TextView called text_name and a Button called button_continue. The ImageView in the layout has no ID, so there is no reference to it in the binding class.

So the generated class would have fields like,

public final class AboutLayoutBinding implements ViewBinding {
    //Root View is always generated
    @NonNull
    private final FrameLayout rootView; 
	
    @NonNull
    public final Button buttonContinue;
    
    @NonNull
    public final TextView textName;
  	
    ...
}

Now, how to use viewbinding in Activity and Fragment can be found in many articles out there. What I’m trying to explain is different, in which most of the developers are hard to find. 🙂

How to use abstraction with ViewBinding with Base Classes?

You can declare a lambda property in the constructor to create the binding object

In BaseActivity

abstract class BaseActivity<B : ViewBinding>(val bindingFactory: (LayoutInflater) -> B) : AppCompatActivity() {
    
    val binding: DB by lazy {
        bindingFactory(layoutInflater)
    }

    abstract fun setup()

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

In BaseFragment


abstract class BaseFragment<DB : ViewBinding>(val bindingFactory: (LayoutInflater, ViewGroup?, Boolean) -> DB): Fragment() {

    lateinit var binding: DB

    abstract fun setup()

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

Usage In Activity Class

//In Activity
class AboutActivity :BaseActivity<AboutLayoutBinding>(AboutLayoutBinding::inflate) {
          
  override fun setup(){
  	//.. do stuff with binding variable
        binding.text_name.text = "Welcome"
  }
}

//In Fragment
class AboutFragment :BaseFragment<AboutLayoutBinding>(AboutLayoutBinding::inflate) {

  override fun setup(){
  	//.. do stuff with binding variable
        binding.text_name.text = "Welcome"
  }
}

Bonus – Common errors / Double Inflating

// in activity
private lateinit var binding: AboutLayoutBinding
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.profile_layout) [ ❌ ]
    binding = AboutLayoutBinding.inflate(layoutInflater)
}
// in fragment
class AboutFragment : Fragment() {
    
override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    val rootView = inflater.inflate(R.layout.profile_layout,container, false)
    return rootView
}  

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val binding = ProfileLayoutBinding
            .inflate(LayoutInflater.from(requireContext()))  [ ❌ 🙅‍♀️ dont do it]
    }
}

Bonus 2 – Usage in Recyclerview Adapters

You can use static bind method of ViewBinding to create binding from an already existing layout. Add it as a property to viewholder:

class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
   val binding = AboutLayoutBinding.bind(view)
}

then you can access all the views through the binding field, for example:

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
    with(holder) {
          // TODO
          binding.tvMovieName.text = data[position].title
          binding.imageView.setDrawableImage(data[position].image)
    }
}

Find my github for a sample implementation of Jetpack Viewbinding

https://github.com/nihas/Jetpack-ViewBinding-Login-Mvvm

If you find the above usage a bit harder, try looking at the following links in StackOverflow for easier use:

https://stackoverflow.com/questions/63686289/how-to-use-abstraction-with-viewbinding-with-base-activity

https://stackoverflow.com/questions/62407823/how-using-viewbinding-with-an-abstract-base-class

Happy Coding 😉

By Nihas Nizar

I am a developer and designer who encourages readers to live a creative, adventurous life.

Leave a Reply