Emad Razavi
Emad Razavi

Reputation: 2113

How to use View Binding on custom views

View Binding got released as part of Android Jetpack

Docs: https://developer.android.com/topic/libraries/view-binding

My question is, how to use view binding with custom views. Google documentation has only show-cased Activity and fragment.

I tried this, but nothing was shown.

LayoutInflater inflater = LayoutInflater.from(getContext());

And then, I used this one, but again, no luck.

LayoutInflater inflater = (LayoutInflater)
            getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);

I guess maybe I don't target the correct layout inflater for my view but not sure.

Upvotes: 119

Views: 78610

Answers (6)

Pavneet_Singh
Pavneet_Singh

Reputation: 37404

To use the view binding, you need to use the generated binding class not the LayoutInflater, for example, if the layout name is result_profile.xml then you need to use ResultProfileBinding as:

class CustomView @kotlin.jvm.JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {
    
    private var binding: ResultProfileBinding
    
    init { // inflate binding and add as view
        binding = ResultProfileBinding.inflate(LayoutInflater.from(context))
        addView(binding.root)
    }
    
}
  1. Auto generated class : result_profile.xml -> ResultProfileBinding(name of layout, appended with Binding )

  2. Inflate the binding

    ResultProfileBinding.inflate(LayoutInflater.from(context))
    
  3. Use addView to add the view in the hierarchy as:

    addView(binding.root)
    

Note: If you are extending from ConstraintLayout(is the parent class) then use constraint set

Upvotes: 41

Iolanda Rosa
Iolanda Rosa

Reputation: 41

You can use DataBindingUtil

binding = DataBindingUtil.inflate(
        LayoutInflater.from(context),
        R.layout.your_layout_id,
        this,
        true
    )

Upvotes: 1

Androiderson
Androiderson

Reputation: 17083

Just inform the root, and whether you want to attach to it

init { // inflate binding and add as view
    binding = ResultProfileBinding.inflate(LayoutInflater.from(context), this)
}

or

init { // inflate binding and add as view
    binding = ResultProfileBinding.inflate(LayoutInflater.from(context), this, true)
}

which inflate method to use will depend on the root layout type in xml.

Upvotes: 194

Levon Petrosyan
Levon Petrosyan

Reputation: 9625

You can initialize the view binding property right away

private val binding = CustomViewBinding.inflate(LayoutInflater.from(context), this)

Upvotes: 15

JohnnyLambada
JohnnyLambada

Reputation: 12826

This is the simplest kotlin answer I can think of. It's a custom view that just wraps a single TextView and provides an update(s:String) function to update the text.

<!-- view_stub.xml -->
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <TextView android:id="@+id/myTextView" 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" />
</layout>

// StubView.kt
class StubView @JvmOverloads constructor(
        context: Context,
        attrs: AttributeSet? = null,
        defStyleAttr: Int = 0
) : FrameLayout(context,attrs,defStyleAttr) {

    val binding = ViewStubBinding.inflate(context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater)
            .also { addView(it.root) }

    fun update(updatedText: String) {
        binding.myTextView.text = updatedText
    }
}

The two things I like about this answer are:

  1. binding is a val instead of a var. I try to limit the number of vars as much as possible.
  2. The addView is closely associated with the val binding using the also {} scope function instead of an init {} clause, making the instantiation of the View feel much more declarative.

One could argue that the addView() is really a side effect and should be in the init {} section so that it is separate from the declaration of the binding val. I would argue the opposite -- declaring a val then feeding it to a section of code that needs it does not feel like a side effect to me.

Upvotes: 2

Jason Toms
Jason Toms

Reputation: 990

If you are trying to use View Binding with the root view, this is working for me:

class CustomView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr) {

    private lateinit var binding: CustomViewBinding

    override fun onFinishInflate() {
        super.onFinishInflate()
        binding = CustomViewBinding.bind(this)
    }
}

Upvotes: 10

Related Questions