In order to reduce boilerplate and simplify work with
view binding, it's often suggested to
use special delegates that provide a ViewBinding
object and automatically clear the associated
property when the view is destroyed.
There
are
many
libraries
and
articles
that describe how to create them and all these solutions use a similar principle, which you
can easily implement using the lifecycleAware
delegate (for activities) or the
viewLifecycleAware
delegate (for fragments).
So, for example, you can implement viewBinding
using reflection and the ViewBinding.bind
method:
// Creates `ViewBinding` instance via `ViewBinding.bind` method (reflective)
inline fun <reified T : ViewBinding> Fragment.viewBinding() = viewLifecycleAware(
initializer = {
val bindMethod = T::class.java.getMethod("bind", View::class.java)
bindMethod.invoke(null, requireView()) as T
}
)
// Creates `ViewBinding` instance via `ViewBinding.bind` method (reflective)
inline fun <reified T : ViewBinding> ComponentActivity.viewBinding() = lifecycleAware(
initializer = {
val bindMethod = T::class.java.getMethod("bind", View::class.java)
val view = findViewById<ViewGroup>(android.R.id.content).getChildAt(0)
bindMethod.invoke(null, view) as T
}
)
// ...
class SampleFragment : Fragment(R.layout.sample_fragment) {
private val binding: SampleFragmentBinding by viewBinding()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.vMessage.text = "Test message"
}
}
And this is how the implementation of viewBinding
for an activity can look like using
the ViewBinding.inflate
method and without using reflection:
// Creates `ViewBinding` instance via `ViewBinding.inflate` method (non-reflective)
fun <T : ViewBinding> ComponentActivity.viewBinding(inflateMethod: (LayoutInflater) -> T) =
lifecycleAware(
initializer = { inflateMethod(layoutInflater) }
)
// ...
class SampleActivity : AppCompatActivity() {
private val binding by viewBinding(SampleActivityBinding::inflate)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
}
}