Introduction
- This series is not going to be in any particular order, so feel free to read whatever blog post you want. Anytime I find something that I think could use a blog post, I will write one and put it here
Setting up
- So first things first, we need to enable view binding. Inside the module level
gradle.build
file(the build file with module written next to it) we place the buildFeatures block into the android block:
android{
buildFeatures {
viewBinding true
}
}
- This will enable the compiler to recompile the project and create ViewBinding object for each XML file.
What is a ViewBinding object?
- a
ViewBinding
object is created for each layout file. By default, the name of the object is based on the name of the layout file, converting it toPascal
case and adding theBinding
suffix to it. So if we had a layout file calledmain_fragment
its corresponding ViewBinding object would beMainFragmentBinding
. This object creates immutable fields for each view in out layout file that has an ID. So anything inside a layout file that has aandroid:id=""
will get a field in the ViewBinding object with the same name. We can then use these fields to access the wanted view. Each ViewBinding object also has a static method calledinflate()
that we can use to inflate the view hierarchy.
Using view binding in Fragments
- To use ViewBinding with a fragment we need to perform 3 mains steps:
1) Set up instance variable
2) Override the onCreateView() method
3) Override the onDestroyView() method
1) Set up instance variables
- There are going to be two instance variables that we create when dealing with view binding:
private var _binding: NewCalfBinding? = null
private val binding get() = _binding!!
- Ignoring all the syntax, I want to draw your attention to
NewCalfBinding
this name is chosen because I have a XML layout file callednew_calf
. Make sure that you use the appropriate name for your code. Now in order to understand all the syntax lets talk a little about null inside of Kotlin
Null saftey
Kotlin has a type system that is aimed at eliminating the danger of
NullPointerException
. Unless explicitly stated that we will allow one, Kotlin helps us avoidNullPointerExceptions
.Kotlin distinguishes between references that can hold null(nullable references) and those that can not(non-null references). We the developer can use the
?
operator to define a variable that is allowed to hold null. So the statement,private var _binding: NewCalfBinding? = null
, is how we define a private variable called_binding
of typeNewCalfBinding
and thanks to?
this variable can also hold null. Notice the error you get when you remove the?
.
The !! operator
- The
!!
operator is called thenot null assertion operator
and it converts any value to a non-null type. If the value is null then it with throw an very specificNullPointerException
. The!!
operator is how we explicitly tell Kotlin we want to throw aNullPointerException
2) Override the onCreateView() method
- The
onCreateMethod()
is called to have the fragment instantiate its user interface view. It will return the instantiated view. It is recommended to only inflate the layout in this method and move the logic that operates on the View to the onViewCreated() method.
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = NewCalfBinding.inflate(inflater, container, false)
val view = binding.root
return view
}
- As stated, this is the method where we inflate(create) our view. Also, notice the
: View?
indicating that this method can return View or null. Theinflate()
method is a static method that is defined on all of the ViewBinding objects.binding.root
is how we get a reference to theroot view
(Constraint layout or what ever layout is surrounding all other views in the layout file ). Lastly we return the root view withreturn view
.
Override the onDestroy() method
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
As you can see that this is fairly straight forward, when this method is called by the system we set our
_binding
variable back to null... but why? I have done some googling and mainly it comes down to memory leaks. If we do not set_binding = null
this can lead to a memory leak which will lead to performance issues as our app scales. But lets go a little deep and try to understand the lifecycles and what is happening.so to really understand what is going on you should read up on the Fragment's lifecycle documentation, especially the section on destroying views and fragments.
It is also important to point out that a fragment's view has a separate lifecycle that is managed independently from the fragment's lifecycle. So a Fragment can outlast a View
-But ultimately this is what happens: when the Fragment is no longer in view(visible to the user) and all the exit animations and transitions have been completed. The fragment's view will transition into it's DESTROYED
state and emits a ON_DESTROY
event to it's observer's(the Fragment). This then triggers the fragment to invoke its onDestroyView() method. It's documentation states, The next time the fragment needs to be displayed, a new view will be created
. So we set _binding = null
to allow for a new View to be created and referenced. Thus giving us a fresh View and no memory leaks
Conclusion
- Thank you for taking the time out of your day to read this blog post of mine. If you have any questions or concerns please comment below or reach out to me on Twitter.
Top comments (0)