Why didn't Moxy initialize the Presenter (Kotlin, Android) - android

I got error that mPresenter has not been initialized. I didn't understand WHY?
I got this error when I use Kotlin but if I use JAVA everything is fine
Here is my code
View
#StateStrategyType(value = AddToEndStrategy::class)
interface IHelloWorldView : MvpView {
fun showMessage(message: Int)
}
Presenter
#InjectViewState
class HelloWorldPresenter : MvpPresenter<IHelloWorldView>() {
fun show() = viewState.showMessage(R.string.message)
}
MainActivity
class MainActivity : MvpAppCompatActivity(), IHelloWorldView {
#InjectPresenter
lateinit var mPresenter: HelloWorldPresenter
lateinit var mText: TextView
lateinit var mButton: Button
override fun showMessage(message: Int) {
mText.setText(message)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mText = findViewById(R.id.text)
mButton = findViewById(R.id.button)
mButton.setOnClickListener {
mPresenter.show()
}
}
}

Because you need to init it like in JAVA Foo foo = new Foo();
It seems like you only created a variable
class MainActivity : MvpAppCompatActivity(), IHelloWorldView {
#InjectPresenter
lateinit var mPresenter: HelloWorldPresenter
//initialisation
#ProvidePresenterTag(presenterClass = HelloWorldPresenter::class, type =PresenterType.GLOBAL)
fun providemPresenter() = HelloWorldPresenter()
fun mPresenter() = HelloWorldPresenter()
lateinit var mText: TextView
lateinit var mButton: Button
override fun showMessage(message: Int) {
mText.setText(message)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mText = findViewById(R.id.text)
mButton = findViewById(R.id.button)
mButton.setOnClickListener {
mPresenter.show()
}
}
}

type = PresenterType.GLOBAL - do not exist (at least in kotlin)

Related

android - RecyclerView and Intent : No value passed for parameter 'ListUserAdapter'

hi im currently using kotlin for my android project,as per instruction,i was told to make an apps that has recycleview to show list item and intent when you click on one of the list shown.
but i have this error when i want to run the app,the error was "No value passed for parameter 'ListUserAdapter'"
here is my code
ListUserAdapter.kt
class ListUserAdapter(private val ListUserAdapter: ArrayList<User>) : RecyclerView.Adapter<ListUserAdapter.ListViewHolder>() {
private lateinit var onItemClickCallBack: OnItemClickCallBack
fun setOnItemClickCallback(onItemClickCallback: OnItemClickCallBack) {
this.onItemClickCallBack = onItemClickCallback
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ListViewHolder {
val view: View = LayoutInflater.from(parent.context).inflate(R.layout.row_user, parent, false)
return ListViewHolder(view)
}
override fun onBindViewHolder(holder: ListViewHolder, position: Int) {
val (name, username) = ListUserAdapter[position]
holder.tvName.text = name
holder.tvUserName.text= username
holder.itemView.setOnClickListener {
onItemClickCallBack.onItemClicked(ListUserAdapter[holder.adapterPosition])
}
}
override fun getItemCount(): Int = ListUserAdapter.size
class ListViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var tvName: TextView = itemView.findViewById(R.id.tv_username)
var tvUserName: TextView = itemView.findViewById(R.id.tv_name)
}
interface OnItemClickCallBack {
fun onItemClicked(data : User)
}
}
MainActivity.kt
class MainActivity : AppCompatActivity() {
private lateinit var adapter: ListUserAdapter
private lateinit var dataName: Array<String>
private lateinit var dataUsername: Array<String>
private lateinit var dataLocation: Array<String>
private lateinit var dataRepo: Array<String>
private lateinit var dataCompany: Array<String>
private lateinit var dataFollowers: Array<String>
private lateinit var dataFollowing: Array<String>
private lateinit var dataPhoto: TypedArray
private var users = arrayListOf<User>()
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setAdapter()
prepare()
addItem()
}
private fun setAdapter() {
adapter = ListUserAdapter() //error in here
with(binding) {
rvList.adapter = adapter
rvList.layoutManager =
GridLayoutManager(this#MainActivity, 2, GridLayoutManager.HORIZONTAL, false)
rvList.setHasFixedSize(true)
}
adapter.setOnItemClickCallback(object : ListUserAdapter.OnItemClickCallBack{
override fun onItemClicked(user: User) {
val intent = Intent(this#MainActivity, DetailActivity::class.java)
intent.putExtra(DetailActivity.KEY_USER, user)
startActivity(intent)
}
})
}
private fun prepare() {
dataName = resources.getStringArray(R.array.name)
dataUsername = resources.getStringArray(R.array.username)
dataPhoto = resources.obtainTypedArray(R.array.avatar)
dataLocation = resources.getStringArray(R.array.location)
dataRepo = resources.getStringArray(R.array.repository)
dataCompany = resources.getStringArray(R.array.company)
dataFollowers = resources.getStringArray(R.array.followers)
dataFollowing = resources.getStringArray(R.array.following)
}
private fun addItem() {
for (position in dataName.indices) {
val user = User(
dataUsername[position],
dataName[position],
dataLocation[position],
dataCompany[position],
dataRepo[position],
dataFollowers[position],
dataFollowing[position],
dataPhoto.getResourceId(position, -1)
)
users.add(user)
}
}
}
DetailActivity.kt
class DetailActivity : AppCompatActivity() {
private lateinit var binding: ActivityDetailBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityDetailBinding.inflate(layoutInflater)
setContentView(binding.root)
setData()
}
private fun setData() {
val dataUser = intent.getParcelableExtra<User>(KEY_USER) as User
with(binding) {
Glide.with(root)
.load(dataUser.photo)
.circleCrop()
.into(ivDetailAvatar)
}
}
companion object {
const val KEY_USER = "key_user"
}
}
just you need to replace adapter = ListUserAdapter() //error in here with adapter = ListUserAdapter(users) then your problem solve
You're just forgetting the constructor parameter :)
Your adapter class needs to receive a ArrayList of User to be instantiated, you already have it in your Activity, you just need to pass it as the constructor parameter :)
I would also rename the parameter name to something like users or usersList instead of `ListUserAdapter but because currently it is misleading, since this parameter is not an adapter, it is a list of users.
Just change the line with error to
adapter = ListUserAdapter(users)
But I think it is best for you to first call prepare() and addItem() and then instantiate your adapter. Or you can instantiate your adapter, but also create a addItems function, since it is a best practice for adapters.
fun addItems(users: List<User>) {
this.ListUserAdapter.addAll(users)
notifyDataSetChanged()
}
and use it after setting everything up inside onCreate doing something like
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setAdapter()
prepare()
addItem()
adapter.addItems(users)
}
but you could probably reorganize these methods to improve readability as well, but it will work :)

"lateinit property has not been initialized" exception when setting a list adapter

class MainActivity() : AppCompatActivity() {
lateinit var tumBurcBilgileri: ArrayList<Burc>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var myBaseAdapter = BurclarBaseAdapter(this, tumBurcBilgileri)
ListBurclar.adapter = myBaseAdapter
}
private fun veriKaynagiHazirla(){
tumBurcBilgileri=ArrayList<Burc>(12)
var burclar = resources.getStringArray(R.array.Burc)
var BurcTarihleri = resources.getStringArray(R.array.burcTarih)
var BurcResimleri = arrayOf(R.drawable.koc1, R.drawable.boga2, R.drawable.ikizler3,
R.drawable.yengec4, R.drawable.aslan5,
R.drawable.basak6, R.drawable.terazi7, R.drawable.akrep8,
R.drawable.yay9, R.drawable.oglak10, R.drawable.kova11,
R.drawable.balik12)
var BüyükBurcResim = arrayOf(R.drawable.koc_buyuk1, R.drawable.boga_buyuk2,
R.drawable.ikizler_buyuk3, R.drawable.yengec_buyuk4, R.drawable.aslan_buyuk5,
R.drawable.basak_buyuk6, R.drawable.terazi_buyuk7, R.drawable.akrep_buyuk8, R.drawable.yay_buyuk9,
R.drawable.oglak_buyuk10, R.drawable.kova_buyuk11, R.drawable.balik_buyuk12)
var burcGenelÖzellikler = resources.getStringArray(R.array.burcGenelOzellikler)
for (i in 0..11){
var arayListeAtanacakBurc=Burc(burclar[i], BurcTarihleri[i], BurcResimleri[i], BüyükBurcResim[i],
burcGenelÖzellikler[i])
tumBurcBilgileri.add(arayListeAtanacakBurc)
}
}
}
Logcat Log:
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.burlar/com.example.burlar.MainActivity}: kotlin.UninitializedPropertyAccessException: lateinit property tumBurcBilgileri has not been initialized
You are getting the error because you have to intitialise the tumBurcBilgileri in onCreate() method as you have used lateinit for the variable. You can put this:-
tumBurcBilgileri=ArrayList<Burc>(12)
in onCreate() method
You are trying to access a lateinit properly before initializing it. Prepare the tumBurcBilgileri ArrayList before setting the adapter.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
private fun veriKaynagiHazirla(){
tumBurcBilgileri=ArrayList<Burc>(12)
var burclar = resources.getStringArray(R.array.Burc)
var BurcTarihleri = resources.getStringArray(R.array.burcTarih)
var BurcResimleri = arrayOf(R.drawable.koc1, R.drawable.boga2, R.drawable.ikizler3,
R.drawable.yengec4, R.drawable.aslan5,
R.drawable.basak6, R.drawable.terazi7, R.drawable.akrep8,
R.drawable.yay9, R.drawable.oglak10, R.drawable.kova11,
R.drawable.balik12)
var BüyükBurcResim = arrayOf(R.drawable.koc_buyuk1, R.drawable.boga_buyuk2,
R.drawable.ikizler_buyuk3, R.drawable.yengec_buyuk4, R.drawable.aslan_buyuk5,
R.drawable.basak_buyuk6, R.drawable.terazi_buyuk7, R.drawable.akrep_buyuk8, R.drawable.yay_buyuk9,
R.drawable.oglak_buyuk10, R.drawable.kova_buyuk11, R.drawable.balik_buyuk12)
var burcGenelÖzellikler = resources.getStringArray(R.array.burcGenelOzellikler)
for (i in 0..11){
var arayListeAtanacakBurc=Burc(burclar[i], BurcTarihleri[i], BurcResimleri[i], BüyükBurcResim[i],
burcGenelÖzellikler[i])
tumBurcBilgileri.add(arayListeAtanacakBurc)
}
// set the adapter after the **tumBurcBilgileri** ArrayList is ready.
var myBaseAdapter = BurclarBaseAdapter(this, tumBurcBilgileri)
ListBurclar.adapter = myBaseAdapter
}
You are not calling the function in the onCreate where the initialization of the lateinit variable is called.
In a lateinit variable you have to assign a value to it before you use it
lateinit var tumBurcBilgileri: ArrayList<Burc>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
veriKaynagiHazirla() // call the function here
var myBaseAdapter = BurclarBaseAdapter(this, tumBurcBilgileri)
ListBurclar.adapter = myBaseAdapter
}

Can't access views or change their values

class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
getCurrentCountry()
}
private fun getCurrentCountry(){
val api = Retrofit.Builder().baseUrl(BASE_URL).addConverterFactory(GsonConverterFactory.create()).build().create(CountryReq::class.java)
GlobalScope.launch(Dispatchers.IO) {
val response = api.getCountries().awaitResponse()
if(response.isSuccessful){
val data = response.body()!!
withContext(Dispatchers.Main){
binding.selectCountry.text = "Demo text"
}
}
}
}
}
I'm trying to get data from an API and display it in a textview. But I can't seem to access the views or change its values from the GlobalScope.launch{} block.

Kotling :: Android :: java.lang.ClassCastException: java.lang.Class cannot be cast to androidx.lifecycle.ViewModel

I am getting an exception in the first line of the code below
viewModel.homeLiveData.observe(this, Observer { list ->
list?.let {
mList.addAll(list)
adapter.notifyDataSetChanged()
}
})
java.lang.ClassCastException: java.lang.Class cannot be cast to androidx.lifecycle.ViewModel
The Whole code is Below
what is wrong with the cast ? is there anything wrong of I am creating my ViewModel?
My BaseActivity
abstract class BaseActivity<V : ViewModel> : DaggerAppCompatActivity(), HasSupportFragmentInjector {
#Inject
lateinit var fragmentAndroidInjector: DispatchingAndroidInjector<Fragment>
#Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
#LayoutRes
abstract fun layoutRes(): Int
protected lateinit var viewModel : V
protected abstract fun getViewModel() : Class<V>
override fun onOptionsItemSelected(item: MenuItem): Boolean {
val id = item.itemId
if (id == android.R.id.home)
onBackPressed()
return super.onOptionsItemSelected(item)
}
override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
setContentView(layoutRes())
viewModel = ViewModelProviders.of(this, viewModelFactory).get(getViewModel())
}
override fun supportFragmentInjector(): AndroidInjector<Fragment> = fragmentAndroidInjector
}
then My Activity
class MainActivity : BaseActivity<MainViewModel>() {
override fun layoutRes(): Int = R.layout.activity_main
override fun getViewModel(): Class<MainViewModel> = MainViewModel::class.java
private val mList = mutableListOf<Any>()
private lateinit var adapter: DataAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// setSupportActionBar(toolbar)
recyclerView.apply {
layoutManager = GridLayoutManager(applicationContext, 2)
adapter = DataAdapter(context, mList)
}
viewModel.homeLiveData.observe(this, Observer { list ->
list?.let {
mList.addAll(list)
adapter.notifyDataSetChanged()
}
})
viewModel.getHomeItems()
}
and this is my ViewModel
class MainViewModel #Inject constructor() : ViewModel() {
val homeLiveData: MutableLiveData<List<HomeScreenModel>> = MutableLiveData()
fun getHomeItems() {
Handler().post {
val homeModleList = listOf(
HomeScreenModel(R.drawable.ic_launcher_background, MyApplication.instance.getString(R.string.settings))
)
homeLiveData.setValue(homeModleList)
}
}
}
In my opinion, your viewModel property, which you try to access in viewModel.homeLiveData is shadowed by getViewModel() abstract function that you declare in BaseActivity. This is because Kotlin thinks that getXyZ() is a getter for the field xyZ and thus when you access viewModel, the compiler thinks you want to call getViewModel, which is of type Class<V>. I suggest renaming either the function or the property and it should work then.
Check your viewModel factory if you implemented it correctly

BaseActivity mvvm architecture android, viewmodel not initialized

I am trying to write a base activity with code like this
abstract class BaseActivity<T : ViewDataBinding, V : BaseViewModel> : AppCompatActivity(), HasSupportFragmentInjector {
val NO_VIEW_MODEL_BINDING_VARIABLE = -1
private lateinit var mViewModel: V
private lateinit var mViewDataBinding: T
#Inject
lateinit var mViewModelFactory : ViewModelProvider.Factory
#Inject
lateinit var mFragmentInjector: DispatchingAndroidInjector<Fragment>
abstract fun getViewModelBindingVariable() : Int
#LayoutRes
abstract fun getLayoutId() : Int
fun getDataBinding() : T {
return mViewDataBinding
}
fun getViewModel() : V {
return mViewModel
}
override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
performDataBinding()
provideViewModel()
}
override fun supportFragmentInjector(): AndroidInjector<Fragment> {
return mFragmentInjector
}
private fun performDataBinding() {
mViewDataBinding = DataBindingUtil.setContentView(this, getLayoutId())
if (getViewModelBindingVariable() != NO_VIEW_MODEL_BINDING_VARIABLE) {
setViewModelBindingVariable()
}
}
private fun setViewModelBindingVariable() {
mViewDataBinding.setVariable(getViewModelBindingVariable(), mViewModel)
mViewDataBinding.executePendingBindings()
}
private fun provideViewModel() {
val clazz: Class<V> = getViewModelClass(javaClass)
mViewModel = ViewModelProviders.of(this, mViewModelFactory).get(clazz)
}
private fun getViewModelClass(aClass: Class<*>): Class<V> {
val type = aClass.genericSuperclass
return if (type is ParameterizedType) {
type.actualTypeArguments[1] as Class<V>
} else {
getViewModelClass(aClass.superclass)
}
}
}
and I also using databinding here, the problem I encountered is whenever I tried to reference the viewmodel class in my xml and return the generated value of the viewmodel by using BR.viewModel in getViewModelBindingVariable it gave an error saying that
Caused by: kotlin.UninitializedPropertyAccessException: lateinit property mViewModel has not been initialized
at com.rahmat.app.newsapp.features.base.BaseActivity.setViewModelBindingVariable(BaseActivity.kt:70)
at com.rahmat.app.newsapp.features.base.BaseActivity.performDataBinding(BaseActivity.kt:65)
at com.rahmat.app.newsapp.features.base.BaseActivity.onCreate(BaseActivity.kt:53)
at com.rahmat.app.newsapp.features.main.MainActivity.onCreate(MainActivity.kt:31)
This error didn't happen if I return NO_VIEW_MODEL_BINDING_VARIABLE but this made me cannot reference the viewmodel class that I want in my xml. I think there is some problem in my provideViewModel() function but I still don't know why. Any help would be much appreciated. thank you

Categories

Resources