i'm new on kotlin and kodein development.
I want to inject data to a simple class which extends nothing.
I have my MainActivity which extends KodeinAppCompatActivity(),
my fragment which extends KodeinSupportFragment() calls a function from my simple class CallType. But this function must to change a boolean from an other simple class ConnectivitySate. I don't want to use static value.
Below, my code :
class App : Application(), KodeinAware {
override val kodein by Kodein.lazy {
import(autoAndroidModule(this#App))
bind<CallType>() with instance(CallType())
bind<ConnectivityState>() with instance(ConnectivityState())
bind<ContactData>() with instance(ContactData())
}
override fun onCreate() {
super.onCreate()
registerActivityLifecycleCallbacks(androidActivityScope.lifecycleManager)
}
MainActivity :
class MainActivity : KodeinAppCompatActivity() {
My Fragment :
class JournalFragment : KodeinSupportFragment(){
private val callType: CallType by instance()
#SuppressLint("MissingSuperCall")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
initializeInjector()
}
override fun onCreateView(inflater: LayoutInflater?, container:
ViewGroup?,savedInstanceState: Bundle?): View? {
// !! CALL MY FUNCTION !!
callType.call(callType.callNumber)
}
....
#SuppressLint("MissingSuperCall")
override fun onDestroy() {
super.onDestroy()
destroyInjector()
}
My simple class :
class CallType {
fun call(number: String) {
// !! I want to change gsmState value from ConnectivityState class
connectivityState.gsmState = true
}
My ConnectivityState class :
class ConnectivityState {
var gsmState = false
}
It is an example among many others, because in lots of situations, i'm blocked like that. I have try lots of things but i always have like error : value not injected
Thank you very much for your reply..
When you call super.onCreate(), it calls onCreateView, so the line callType.call(callType.callNumber) is called before initializeInjector().
Note that you should always call initializeInjector() before calling super.onCreate():
override fun onCreate(savedInstanceState: Bundle?) {
initializeInjector()
super.onCreate(savedInstanceState)
}
Related
I used Activity(), not AppcompatActivity() to make transparent background.
class CommentActivity : Activity() {
And when I tried to create ViewModel, I can't use this as ViewModelStoreOwner.
How can I solve this problem?
You have to at least extend ComponentActivity, native Activity doesn't contain any support for androidx components.
Try implementing the ViewModelStoreOwner interface:
class MainActivity : Activity(), ViewModelStoreOwner{
companion object{
var VIEWMODEL_STORE:ViewModelStore? = null
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val testViewModel:TestViewModel = ViewModelProvider(this).get(TestViewModel::class.java)
val textView = findViewById<TextView>(R.id.test_text)
textView.text = testViewModel.testMessage()
Log.i("MainActivity",testViewModel.testMessage())
}
override fun getViewModelStore(): ViewModelStore {
if(VIEWMODEL_STORE == null){
VIEWMODEL_STORE = ViewModelStore()
}
return VIEWMODEL_STORE!!
}
}
The viewModel class stays without changes:
class TestViewModel:ViewModel() {
fun testMessage():String = "From TestViewModel ${hashCode()}"
}
I have base activity< T : ViewDataBinding , VM : ViewModel > extends AppCompatActivity()
and i initialize view binding and view model but when run the app i get this error "lateinit property dataBinding has not been initialized"
I don't know what I miss or what the wrong
Below is Base Activity Code
open abstract class BaseActivity<T : ViewDataBinding , VM : ViewModel> : AppCompatActivity() {
lateinit var dataBinding : T
lateinit var viewModel : VM
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
super.onCreate(savedInstanceState, persistentState)
dataBinding = getViewBinding()
setContentView(dataBinding.root)
viewModel = generateViewModel()
}
abstract fun getViewBinding(): T
abstract fun generateViewModel(): VM
and this My HomeActivity
class HomeActivity : BaseActivity<ActivityHomeBinding, HomeViewModel>() {
override fun getViewBinding(): ActivityHomeBinding = ActivityHomeBinding.inflate(layoutInflater)
override fun generateViewModel(): HomeViewModel {
return ViewModelProvider(this).get(HomeViewModel::class.java)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
dataBinding.vm = viewModel
}
}
this is the error message
Because in your base class the onCreate() with 2 parameters is not going to get called during the activity's lifecycles. And in your subclass you override the onCreate() with a parameter.
Just simple change your base class to override the onCreate() with a parameter to fix the problem. And the other thing is you implement these class the java's way.
You can just make it better this way:
BaseClass
abstract class BaseActivity<T : ViewDataBinding , VM : ViewModel> : AppCompatActivity() {
protected abstract val dataBinding : T
protected abstract val viewModel : VM
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(dataBinding.root)
}
}
Subclass:
class HomeActivity : BaseActivity<ActivityHomeBinding, HomeViewModel>() {
override val viewModel get() = ViewModelProvider(this).get(HomeViewModel::class.java)
override val dataBinding get() = ActivityHomeBinding.inflate(layoutInflater)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
dataBinding.vm = viewModel
}
}
When you are calling super.onCreate(savedInstanceState) in your HomeActivity, the onCreate from android's Activity is called, but not the onCreate from BaseActivity - because it expected second param persistentState.
So you can do this options to fix the issue:
call super method with 2 params in your HomeActivity
class HomeActivity ... {
...
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
super.onCreate(savedInstanceState, persistentState)
dataBinding.vm = viewModel
}
OR
use onCreate with one param in your BaseActivity
open abstract class BaseActivity<T : ViewDataBinding , VM : ViewModel> : AppCompatActivity() {
lateinit var dataBinding : T
lateinit var viewModel : VM
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...
This question already has answers here:
Passing data between a fragment and its container activity
(16 answers)
Closed 1 year ago.
How to correctly transfer data from a fragment to an activity? I do as follows:
1- Create an interface
interface IProfileToActivity {
fun profileInfo(data: AllHeroes.Global)
}
2- Then I inheritance in the activity
class ProfileActivity : AppCompatActivity(), IProfileToActivity {
private lateinit var myBinding: ActivityProfileBinding
override fun profileInfo(data: AllHeroes.Global) {
myBinding.tvUsername.text = data.name
myBinding.tvDivision.text = data.rank.rankName
Log.i("Apex Info 3", data.toString())
}
}
3- sending from a fragment
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
(activity as? IProfileToActivity)?.profileInfo(allInfoApexResponse.global)
mHeroesAdapter.heroesList(allAdapterListHero)
}
but nothing happens, why? what did I do wrong?
You need not create an interface here. You can use requireActivity() to get a reference to the parent activity. Using it you can access public fields and functions of you activity.
class ProfileActivity : AppCompatActivity() {
private lateinit var myBinding: ActivityProfileBinding
fun profileInfo(data: AllHeroes.Global) {
myBinding.tvUsername.text = data.name
myBinding.tvDivision.text = data.rank.rankName
Log.i("Apex Info 3", data.toString())
}
}
And in your fragment:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
(requireActivity as ProfileToActivity).profileInfo(allInfoApexResponse.global)
mHeroesAdapter.heroesList(allAdapterListHero)
}
There are many ways to pass data from fragment to activity:
Using shared ViewModel.
A ViewModel is used to manage and store UI related data in a
lifecycle conscious way.
~Read more
class SharedViewModel: ViewModel() {
private val currItems: MutableLiveData<List<Item>> =
MutableLiveData<List<Item>>(listOf())
fun getCurrItem(): LiveData<List<Item>> {
return currItems
}
fun sendCurrItems(items: MutableList<Item>) {
currItems.postValue(items)
}
}
class ItemFragment: Fragment() {
private val sharedModel: SharedViewModel by activityViewModels()
}
MainActivity: AppCompactActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val model = ViewModelProvider(this).get(SharedViewModel::class.java)
}
}
In the above class, data is being stored and updated using an MutableList. Thing to be noted here is, the above class is a singleton class, i.e. once it is created, it gets destroyed only when the activity is ended.
Let us assume that an item has to be shared from a ItemFragment to the MainActivity
One callback has to be implemented the MainActivity. For that, one can use an Interface
interface ItemListener{
fun sendItem(item : MutableList<Item>)
}
ItemFragment:
class ItemFragment: Fragment() {
override fun sendItems(items: MutableList<Item>?) {
// Send an Item from here as well as update it
}
// Or just simply call sendItem method.
}
MainActivity:
class MainActivity: AppCompactActivity(){
fun receiveItem(context : Context){
private var mCallback: ItemListener? = null
mCallback = context
}
}
I am currently learning MVVM architecture.
I tried to make a BaseActivity class.
My BaseActivity:
abstract class BaseActivity<ViewModel : BaseViewModel, Binding : ViewDataBinding> :
AppCompatActivity(),
EventListener {
lateinit var binding: Binding
private var viewModel: ViewModel? = null
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
super.onCreate(savedInstanceState, persistentState)
binding = DataBindingUtil.setContentView(this, layoutid)
this.viewModel = viewModel ?: getViewModel()
binding.setVariable(getBindingVariable(), viewModel)
binding.executePendingBindings()
}
#get: LayoutRes
abstract val layoutid: Int
abstract fun getViewModel(): ViewModel
abstract fun getBindingVariable(): Int
private fun getViewModelClass(): Class<ViewModel> {
val type = (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0]
return type as Class<ViewModel>
}
}
Now I am using this BaseActivity in my SplashActivity:
class SplashActivity : BaseActivity<SplashActivityViewModel, ActivitySplashBinding>() {
private lateinit var viewModel: SplashScreenViewModel
override fun onFailure(message: String) {}
override fun onStarted() {}
override fun onSuccess() {}
override fun getViewModel(): SplashActivityViewModel {
viewModel = ViewModelProvider(this).get(SplashActivityViewModel::class.java)
return viewModel
}
override fun getBindingVariable(): Int {
return BR.splash_viewmodel
}
override val layoutid: Int
get() = R.layout.activity_splash
}
I have used following answer as a reference to implement my BaseActivity.kt: https://stackoverflow.com/questions/55289334/how-to-have-generic-viewmodel-in-baseactivty-class
But I am getting a blank white screen while running the app.
Can someone please tell me what is the problem here or how to make this BaseActivity (without using dependency injection)?
you have overridden the wrong onCreate
override fun onCreate(savedInstanceState: Bundle?) {
I did play around with something like that few years ago, you can find my approach here
I have an Activity that extend base Activity like so:
class MainActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
getInfo("Pojo")
}
}
My base activity does some API access and returns the answer like so:
abstract class BaseActivity : AppCompatActivity() {
companion object {
#JvmStatic var compositeDisposable: CompositeDisposable?=null
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
compositeDisposable = CompositeDisposable()
}
override fun onDestroy() {
super.onDestroy()
compositeDisposable?.clear()
}
fun getTvShows(query: String) {
compositeDisposable?.add(
ApiClient.getClient.getTV(Params.getParamsSearch(1, query))
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(this::handleResponseTvShows)
)
}
private fun handleResponseTvShows(result: ObjectsSearchTVShows) {
//Need to send result back to MainActivity or any activity that extends BaseActivity
}
}
I need to send the result back to MainActivity or any activity that extends BaseActivity, how I can achive this?
Make handleResponseTvShows abstract and protected:
protected abstract fun handleResponseTvShows(result: ObjectsSearchTVShows)
and implement it in MainActivity:
override protected fun handleResponseTvShows(result: ObjectsSearchTVShows) {
// process the result here
}