This all very complex I hope I can explain it well. So, I'm using recyclerview to show a list of User Profiles in form of cards. Each card contains 2 buttons for different actions. Now these buttons require observing to a LiveData object inside a ViewModel (I'm using MVVM, Android architecture components and Kotlin extensions) when clicked. One button in particular needs to observe to different live data based on some condition. This is why I've created a function which takes the condition and returns suitable OnClickListener. I pass this to the recyclerview adapter.
Now the problem is I use the same RecyclerView in multiple fragments. Everything works fine when i use the recyclerview in the same fragment where the function is written. Everywhere else I get the error like following:
java.lang.IllegalStateException: Fragment HomeFragment{1ef5330} (b299012d-c9c2-427f-8387-a7888289701b)} not attached to an activity.
at androidx.fragment.app.Fragment.requireActivity(Fragment.java:833)
at
com.halalrishtey.HomeFragment$$special$$inlined$activityViewModels$2.invoke(FragmentViewModelLazy.kt:80)
at com.halalrishtey.HomeFragment$$special$$inlined$activityViewModels$2.invoke(Unknown Source:0)
at androidx.lifecycle.ViewModelLazy.getValue(ViewModelProvider.kt:52)
at androidx.lifecycle.ViewModelLazy.getValue(ViewModelProvider.kt:41)
at com.halalrishtey.HomeFragment.getUserVM(Unknown Source:7)
at com.halalrishtey.HomeFragment.access$getUserVM$p(HomeFragment.kt:25)
at com.halalrishtey.HomeFragment$genInterestBtnListener$1.onClick(HomeFragment.kt:65)
at android.view.View.performClick(View.java:7125)
at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:967)
at android.view.View.performClickInternal(View.java:7102)
at android.view.View.access$3500(View.java:801)
at android.view.View$PerformClick.run(View.java:27336)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
And the function which creates the OnClickListener looks something like this:
fun genInterestBtnListener(
condition: Boolean,
v: View
): View.OnClickListener {
return View.OnClickListener {
if (!condition) {
userVM.initInterest().observe(viewLifecycleOwner, Observer { msg ->
Toast.makeText(context, msg, Toast.LENGTH_SHORT)
.show()
//More code here
})
} else {
userVM.removeInterest().observe(viewLifecycleOwner, Observer { msg ->
Toast.makeText(context, msg, Toast.LENGTH_SHORT)
.show()
})
}
}
}
I can't observe to things inside the adapter, so whats the optimal solution for this without repeating code??
Update: I declare userVM like this:
private val userVM: UserViewModel by activityViewModels()
Also Here's my adapter code:
class CardDataRVAdapter(private var items: List<ProfileCardData>) :
RecyclerView.Adapter<CardDataRVAdapter.CardDataViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CardDataViewHolder {
val inflatedView =
LayoutInflater.from(parent.context).inflate(R.layout.profile_card, parent, false)
return CardDataViewHolder(inflatedView)
}
override fun onBindViewHolder(holder: CardDataViewHolder, position: Int) {
val card = items[position]
holder.bindCard(card)
}
override fun getItemCount() = items.size
class CardDataViewHolder constructor(
v: View
) : RecyclerView.ViewHolder(v), View.OnClickListener {
private var view: View = v
private var cardData: ProfileCardData? = null
init {
v.setOnClickListener(this)
}
override fun onClick(p0: View?) {
//TODO: Implement a proper onClickListener
Toast.makeText(
p0?.context,
"${p0?.cardTitleTextView?.text} Card was clicked",
Toast.LENGTH_SHORT
).show()
}
companion object {
private val KEY = "CARD"
}
fun bindCard(card: ProfileCardData) {
this.cardData = card
if (card.isUserShortlisted) {
view.showInterestBtn.setIconResource(R.drawable.ic_favorite)
} else {
view.showInterestBtn.setIconResource(R.drawable.ic_favorite_border)
}
if (card.data.photoUrl.length > 5) {
Picasso.get().load(card.data.photoUrl)
.into(view.cardImageView)
Picasso.get().load(card.data.photoUrl)
.into(view.cardAvatarImageView)
}
view.cardTitleTextView.text = card.data.displayName
view.cardSubtitleTextView.text = "${card.data.age} - ${card.data.height}"
view.showInterestBtn.setOnClickListener(card.showBtnInterestListener)
view.sendMessageBtn.setOnClickListener(card.messageBtnListener)
}
}
}