Home > OS >  ViewBinding is not able to use views from the layout file
ViewBinding is not able to use views from the layout file

Time:09-10

I'm building a Resume Builder App. For that I have to use view binding. In my app everting works well I'm able to create a view binding object of the variable with a particular layout. But when I call the views from the binding variable it's working in the .kt file but not while running the app. Binding object shows the view. But when I run the app it's not working as stated in the code.

class EducationActivity : AppCompatActivity() {

    private lateinit var binding : ActivityEducationBinding
    private lateinit var toolbar : Toolbar

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = ActivityEducationBinding.inflate(layoutInflater)
        setContentView(R.layout.activity_education)

        toolbar = findViewById(R.id.toolbar_eduction_activity)
        setUpToolbar()
    }

    private fun setUpToolbar(){
        setSupportActionBar(toolbar)
        val actionBar = supportActionBar
        if (actionBar!=null){
            actionBar.setDisplayHomeAsUpEnabled(true)
            actionBar.setHomeAsUpIndicator(R.drawable.ic_back_button_action_bar)
            actionBar.setDisplayShowTitleEnabled(false)
        }

        toolbar.setNavigationOnClickListener { onBackPressed() }
    }

The code above is working with : setSupportActionBar(toolbar)

but when I use, setSupportActionBar(binding.toolbarEducationActivity). It's not working while running the app. There is no error or anything. I didn't understand the problem. Thanks in advance!

CodePudding user response:

Why are u using findViewByID's with viewBinding?

Your code should be like this

--> KOTLIN

class EducationActivity : AppCompatActivity() {

    private lateinit var binding : ActivityEducationBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = ActivityEducationBinding.inflate(layoutInflater)
        setContentView(binding.root)

        setUpToolbar()
    }

    private fun setUpToolbar(){
        setSupportActionBar(binding.toolbar)
        val actionBar = supportActionBar
        if (actionBar!=null){
            actionBar.setDisplayHomeAsUpEnabled(true)
            actionBar.setHomeAsUpIndicator(R.drawable.ic_back_button_action_bar)
            actionBar.setDisplayShowTitleEnabled(false)
        }

        binding.toolbar.setNavigationOnClickListener { onBackPressed() }
    }

Do mark this as the answer if this helps.

Hope this helps! :)

CodePudding user response:

The problem is you're inflating your layout twice:

binding = ActivityEducationBinding.inflate(layoutInflater)
setContentView(R.layout.activity_education)

The inflate call inflates your layout XML, builds a view hierarchy, and stores references to that in the binding object.

Then the setContentView call with a layout reference inflates another, completely separate view hierarchy and displays it.

So you have two sets of views, one is visible and the other isn't. The ones in binding are the ones that aren't being displayed - the user can't see or interact with them, so they're pretty much useless. That's why things aren't working when you try to set stuff up with the views in binding.

Using findViewById instead is implicitly calling it on the Activity's view hierarchy, i.e. the stuff that's displayed. That way you get a reference to the Toolbar that's actually on-screen, instead of the stray one in binding. So when you set things up with that one, you actually see it working!


So to solve it, you need to ensure you're inflating your layout once, and then make everything use that. There's two common ways to do it:

// inflate your Binding class, and pass the resulting layout to setContentView to display
binding = ActivityEducationBinding.inflate(layoutInflater)
// this root property is the inflated view hierarchy
setContentView(binding.root)

or

// use setContentView with a layout resource, and let it inflate the views as normal
setContentView(R.layout.activity_education)
// now that the 'view' property for the Activity has been set with the inflated layout,
// you can grab that and -bind- its views to the properties in your Binding class
binding = ActivityEducationBinding.bind(view)

The bind call is basically what happens with inflate, except you're skipping the inflation step because you already have an inflated layout.


You can use either approach - I prefer the first because you only specify the layout resource once by referencing the appropriate binding class. Then you just pass whatever you inflate to setContentView for display. With the second approach, you have to make sure the Binding class you use matches the layout resource ID you pass into setContentView. Not a huge deal and sometimes one is more convenient than the other!

The important thing though is that in both cases, you only inflate something once. Then everything is using the same set of views, and nothing weird is going on. If you ever find yourself inflating more than once, and you don't have a very specific reason for doing it, then that's a warning sign!

  • Related