Reputation: 5865
I was making a base class so that all bindings for child will be set in base
I have done till this
abstract class BaseActivity2<B : ViewBinding?, T : BaseViewModel?> : AppCompatActivity() {
private var viewBinding: B? = null
private var baseViewModel: T? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
}
but am unable to get a way to bind view in oncreat() generally we bind layout in view binding as
binding = ActivityLoginBinding.inflate(layoutInflater)
setContentView(binding.root)
but i am looking for generalized way in base activity
Upvotes: 20
Views: 12988
Reputation: 1004
The answers given above work fine but at runtime onclick listerner and updating ui not working, came up with a modified version hoping it will help someone.
abstract class VBActivity<VB : ViewBinding> : AppCompatActivity() {
private var _binding: VB? = null
protected val binding get() = _binding!!
abstract fun getViewBinding(): VB
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
_binding = getViewBinding()
setContentView(binding.root)
}
override fun onDestroy() {
super.onDestroy()
_binding = null
}
}
then override your activities like this.
class SplashScreen : VBActivity<ActivitySplashScreenBinding>() {
override fun getViewBinding(): ActivitySplashScreenBinding {
return ActivitySplashScreenBinding.inflate(layoutInflater)
}
}
Upvotes: 0
Reputation: 924
It's cleaner to override binding object getter inside the child activity I think. So:
abstract class VBActivity<VB : ViewBinding> : AppCompatActivity() {
protected abstract val binding: VB
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
}
}
And lets say MainActivity will be something like:
class MainActivity : VBActivity<ActivityMainBinding>() {
override val binding get() = ActivityMainBinding.inflate(layoutInflater)
}
Upvotes: 7
Reputation: 2966
You can declare a lambda property in the constructor to create the binding object
abstract class BaseActivity<B : ViewBinding>(val bindingFactory: (LayoutInflater) -> B) : AppCompatActivity() {
private lateinit var binding: B
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = bindingFactory(layoutInflater)
setContentView(binding.root)
}
}
You can also define binding
as lazy property
private val binding: B by lazy { bindingFactory(layoutInflater) }
Then you need to override nothing in your activities
class MainActivity : BaseActivity<ActivityMainBinding>(ActivityMainBinding::inflate)
Upvotes: 27
Reputation: 5865
Other answer will also solve problem but I would like to do in a clean way.
My Base Class
abstract class BaseVMActivity<VM : ViewModel, B : ViewBinding> : BaseActivity() {
lateinit var viewModel: VM
lateinit var binding: B
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProvider(this, factory).get(getViewModelClass())
binding = getViewBinding()
setContentView(binding.root)
}
private fun getViewModelClass(): Class<VM> {
val type = (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0]
return type as Class<VM>
}
abstract fun getViewBinding(): B
}
My activity:
class MainActivity : BaseVMActivity<AppViewModel, ActivityMainBinding>() {
override fun getViewBinding() = ActivityMainBinding.inflate(layoutInflater)
}
Now I can directly access viewModel or binding:
fun dummy(){
binding.bvReport.text = viewModel.getReportText()
}
Upvotes: 18