Mirza Ahmed Baig
Mirza Ahmed Baig

Reputation: 5865

How to use abstraction with ViewBinding with base activity?

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

Answers (4)

Usama Saeed US
Usama Saeed US

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

Reza
Reza

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

Sinner of the System
Sinner of the System

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

Mirza Ahmed Baig
Mirza Ahmed Baig

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

Related Questions