[Android] DataBinding-ktx 4.0.0 Released
What is DataBinding-ktx
It is a library for solving problems related to DataBinding and ViewBinding, and for safe and easy use.
Problems in DataBinding / ViewBinding
1. Variable declaration method differs between Activity and Fragment
Activity can use by lazy, but Fragment cannot use by lazy because there is View regeneration (*).
*: Because no binding instance is generated for the View after regeneration
2. Fragment wastes memory unless binding set to null in onDestroyView
When using the navigation component, back stack, and detach, Fragment’s view is released after onDestroyView, but the Fragment is still alive. The binding wastes memory if it is not released since it holds the View Tree.
3. Forget call to setLifecycleOwner
Only DataBinding is a problem, but if you are using LiveData, LiveData will not be binding unless you call setLifecycleOwner.
Solution with DataBinding-ktx
1. Variable declaration method is different between Activity and Fragment
Kotlin’s Delegated Properties allows you to declare properties in the same way in Activity and Fragment.
2. Fragment wastes memory unless binding set to null in onDestroyView
Because the binding is released at the same time as the View inside the Delegated Properties, the library user does not need to be aware of it.
3. Forget call to setLifecycleOwner
The setLifecycleOwner is automatically called when you first access the binding variable, so you will never forget to call it.
How to use DataBinding-ktx
DataBinding
private val binding: DataBindingActivityBinding by dataBinding ()
ViewBinding
private val binding by viewBinding {
ViewBindingActivityBinding.bind(it)
}
Activity
Call setContentView before using the binding variable
class DataBindingActivity : FragmentActivity() {
// Declare the `binding` variable using `by dataBinding()`.
private val binding: DataBindingActivityBinding by dataBinding()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.data_binding_activity)
// You can use binding
}
}
or pass the layout ID to the constructor.
class DataBindingActivity : AppCompatActivity(R.layout.data_binding_activity) {
// Declare the `binding` variable using `by dataBinding()`.
private val binding: DataBindingActivityBinding by dataBinding()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// You can use binding
}
}
Fragment
Call inflater.inflate in onCreateView
class DataBindingFragment : Fragment() {
// Declare the `binding` variable using `by dataBinding()`.
private val binding: DataBindingFragmentBinding by dataBinding()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.data_binding_fragment, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// You can use binding
}
}
or pass the layout ID to the constructor.
class DataBindingFragment : Fragment(R.layout.data_binding_fragment) {
// Declare the `binding` variable using `by dataBinding()`.
private val binding: DataBindingFragmentBinding by dataBinding()
// DO NOT override onCreateView
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// You can use binding
}
}
Note
If you forget to pass the layout ID to the constructor, it will crash in Activity and will not appear in Fragment. Therefore, I think that you should define the following class.
DataBinding
Activity
open class DataBindingAppCompatActivity<T : ViewDataBinding>(@LayoutRes contentLayoutId : Int) : AppCompatActivity(contentLayoutId) {
protected val binding: T by dataBinding()
}
class YourActivity : DataBindingAppCompatActivity<YourActivityBinding>(R.layout.your_activity) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// You can use binding
}
}
Fragment
open class DataBindingFragment<T : ViewDataBinding>(@LayoutRes contentLayoutId : Int) : Fragment(contentLayoutId) {
protected val binding: T by dataBinding()
}
class YourFragment : DataBindingFragment<YourFragmentBinding>(R.layout.your_fragment) {
// DO NOT override onCreateView
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// You can use binding
}
}
ViewBinding
Activity
open class ViewBindingAppCompatActivity<T : ViewBinding>(@LayoutRes contentLayoutId: Int, bind: (View) -> T) : AppCompatActivity(contentLayoutId) {
protected val binding by viewBinding(bind)
}
class YourActivity : ViewBindingAppCompatActivity<YourActivityBinding>(R.layout.your_activity, YourActivityBinding::bind) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// You can use binding
}
}
Fragment
open class ViewBindingFragment<T : ViewBinding>(@LayoutRes contentLayoutId : Int, bind: (View) -> T) : Fragment(contentLayoutId) {
protected val binding: T by viewBinding(bind)
}
class YourFragment : ViewBindingFragment<YourFragmentBinding>(R.layout.your_fragment) {
// DO NOT override onCreateView
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// You can use binding
}
}