Home > other >  Can somebody explain me the difference between these two usages?I am aware of the fragment inflation
Can somebody explain me the difference between these two usages?I am aware of the fragment inflation

Time:10-21

This snippet was used in an activity(onCreate) :

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

This snippet was used in an Fragment(onCreateView) :

    cameraView = inflater.inflate(R.layout.fragment_camera_view, container, false)
    return cameraView

CodePudding user response:

There are a few reasons to use Binding over direct inflation:

  • Runtime performance : with direct inflation findViewById() it performs a top-down search of the View hierarchy until it finds a match. It is not optimised, and has no internal cache, so if we make the call that method twice in quick succession with the same View ID, then both invocations will require the same top-down search. While with binding, there is no hidden cost of accessing the text1 field of a ActivityMainBinding instance.
  • Crash avoidance : sometimes we encounter crashes using Kotlin synthetic view properties if we attempt to access them too early in the Android lifecycle before they have been initialised. While it is still necessary to perform the inflation of the layout and binding at the correct phase of the Android lifecycle, that initialisation will actually be done within the Activity or Fragment code making it much more visible precisely when in the lifecycle the initialisation is taking place.
  • Build Time : The improvements in runtime performance will incur a cost at build-time because it uses code generation. It will also increase your APK size and method count.

Reference: https://blog.stylingandroid.com/view-binding-internals/

CodePudding user response:

Two separate things really. setContentView is how an Activity displays its view hierarchy. You can either inflate the hierarchy yourself and pass that in (like you're doing here, with binding.inflate and then passing the resulting binding.root), or you can call setContentView(R.layout.some_layout) and it'll take care of inflating that layout itself.

onCreateView is a Fragment method, it gets called when the Fragment is initialising, and it needs to create its view hierarchy. So you need to override this, inflate your hierarchy, and then return it at the end so the Fragment can use it.

So setContentView gets called in an Activity, and can take a layout reference or an actual inflated set of Views. onCreateView is a method in a Fragment where you need to return an inflated set of Views.


When it comes to actually inflating those Views, you have a few options. Your examples are each using a different approach - so on top of one being an Activity and one being a Fragment, where you do things differently, you're also mixing and matching the way you inflate stuff.

The standard way to inflate a layout is to just grab a LayoutInflater and call inflate on it, passing a layout XML file so it can use that as a recipe to create all the necessary View objects and connect them together:

// Typically in an Activity you'd use 'this' as the Context, since an Activity -is- a Context
// In a Fragment's onCreateView you get an inflater passed in, in a RecyclerView.Adapter's
// onCreateViewHolder you have access to the 'parent' which you can grab a Context from, etc
val inflater = LayoutInflater.from(context)

// Inflate an XML file - the second parameter is the parent container, used for working out
// things like layout parameters (e.g. to make 'match_parent' work).
// The last parameter is about attaching the View you're inflating - this is usually false!
// We're handing off the View, the thing that's using it will handle attaching it
val view = inflater.inflate(R.layout.some_layout, container, false)

And that's it! Now you have a bunch of actual Views you can hand over for display.

// in your activity
setContentView(view)

// at the end of onCreateView
return view

If you're using View Binding, you usually don't want to use a LayoutInflater directly. The generated binding class (e.g. ActivityMainBinding) has an inflate call that does the inflation for you using the relevant XML file, and then it creates a binding object with variables for all the View objects in that inflated hierarchy.

That way you don't need to look anything up with findViewById - that's basically already been done for you, and you can access them with binding.someId. The binding object holds the inflated hierarchy too, which you can access with binding.root. You can pass that for display in the same way as before:

// in your activity
setContentView(binding.root)

// at the end of onCreateView
return binding.root

See what I mean? You do things differently in an Activity and a Fragment, but the way you inflate the views can be the same - it just depends on what you're doing, if you're using View Binding then you'll be using the binding class instead of inflater.inflate.

For completeness's sake, you can also take an already-inflated view hierarchy and create a binding object from that, using the bind method - e.g. ActivityMainBinding.bind(view). This is useful if the inflation has already been taken care of, so you want to skip that step and just look up all the views and get your binding object.

Remember, if you inflate a layout again, you get a completely different set of objects, and if you display those you can't see or interact with the original ones. Generally, you only want to inflate a layout once - having two sets of Views is a sign you've made a mistake, and can lead to bugs like "why aren't my click listeners working" (you set them on the other Views that you replaced) etc.

A typical situation where you might have an already-inflated layout is if you're using the Activity and Fragment constructors where you supply a layout XML resource as a parameter, like class MainActivity : AppCompatActivity(R.layout.activity_main). When you call super.onCreate() that takes care of the setContentView bit (and you might not need to override onCreate at all if that's all it does!). To create your binding object, you just need to bind to the Activity's view property.

Same thing for Fragments - you can put the layout file in the constructor, then there's no need to override onCreateView since that's all taken care of. Instead, you can override onViewCreated to do your setup - you get passed the inflated view and you can bind to that in there

  • Related