#AssistedInjection RecyclerViewAdapter parameters resulting in error - android

I'm working on a new android project using Dagger, and when I had to make an recycler view item trigger a click event I started having problems where to place my event, this event is pretty simple I'll just persist a new record on a realm database, however I'm using MVVM pattern then I should make my ViewModel do this kind of work, so this persistence logic should be away from my RecyclerView and ViewHolder, at this point I thought the best solution would be send the click event using my adapter parameters, and them I reach the point where my problem happens, when I try to send it using #AssistedInjection I'm getting the error below:
tl;dr;
Send parameters to recyclerview adapter using #AssistedInjection are resulting in error
This is my error message:
error: The parameters in the factory method must match the #Assisted parameters in com.curuto.footballdata.view.main_activity.adapter.ChampionshipAdapter.
public abstract com.curuto.footballdata.view.main_activity.adapter.ChampionshipAdapter start(#org.jetbrains.annotations.NotNull()
^
Actual: com.curuto.footballdata.view.main_activity.adapter.ChampionshipAdapterAssistedFactory#start(io.realm.OrderedRealmCollection<com.curuto.footballdata.model.Championship>, android.view.View.OnClickListener)
Expected: com.curuto.footballdata.view.main_activity.adapter.ChampionshipAdapterAssistedFactory#start(io.realm.OrderedRealmCollection<com.curuto.footballdata.model.Championship>, android.view.View.OnClickListener)
And here goes my code:
Adapter:
open class ChampionshipAdapter
#AssistedInject constructor(#Assisted("championshipData") championshipData: OrderedRealmCollection<Championship>,
#Assisted("donwloadDataClickListener") donwloadDataClickListener: View.OnClickListener,
):
RealmRecyclerViewAdapter<Championship, ChampionshipViewHolder>
(championshipData, true) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChampionshipViewHolder {
val binding = RowItemChampionshipBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ChampionshipViewHolder(binding)
}
override fun onBindViewHolder(holder: ChampionshipViewHolder, position: Int) {
holder.bind(championshipData[position])
}
}
Assisted Factory
#AssistedFactory
interface ChampionshipAdapterAssistedFactory {
fun start(#Assisted championshipData: OrderedRealmCollection<Championship>,
#Assisted donwloadDataClickListener: View.OnClickListener) : ChampionshipAdapter
}
Main Activity
class MainActivity : AppCompatActivity(), View.OnClickListener {
lateinit var championshipAdapter : ChampionshipAdapter
#Inject lateinit var championship: Championship
#Inject lateinit var championshipViewModel: ChampionshipViewModel
#Inject lateinit var championshipAdapterAssistedFactory: ChampionshipAdapterAssistedFactory
#Inject lateinit var realm: Realm
lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
//FootballDataApllication usa o compoente para ir injetando módulos nas atividades,
//talvez Componentes diferentes devem ser usados em activities diferentes
(applicationContext as FootballDataApplication).myComp.inject(this)
val championshipData = Realm.getDefaultInstance().where(Championship::class.java).findAllAsync()
val donwloadDataClickListener = View.OnClickListener{
}
championshipAdapter = championshipAdapterAssistedFactory.start(championshipData, donwloadDataClickListener)
binding.rvChampionshipList.adapter = championshipAdapter
binding.rvChampionshipList.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
logD(championshipViewModel.retText())
//binding.acbAddChampionship.setOnClickListener(this)
}
override fun onClick(p0: View?) {
when(p0?.id){
/*R.id.acb_add_championship -> {
championshipViewModel.addChampionship("SUPER LIGA VIEW MODEL")
logD("CLICKED ADD CHAMPS")
}*/
}
}
}

Related

Type mismatch error when trying to implement swipe views using ViewPager2

I'm a newbie learning kotlin and android and I'm trying out simple swipe-able views with ViewPager2.
This is my adapter code:
class ViewPagerAdapter(var images: List<Int>):
RecyclerView.Adapter<ViewPagerAdapter.ViewPagerViewHolder>()
{
inner class ViewPagerViewHolder(val binding: ItemViewPagerBinding): RecyclerView.ViewHolder(binding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewPagerViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_view_pager,parent,false)
return ViewPagerViewHolder(bind(view))
}
override fun onBindViewHolder(holder: ViewPagerViewHolder, position: Int) {
var curImage = images[position]
holder.binding.imgview.setImageResource(curImage)
}
override fun getItemCount(): Int {
return images.size
}
}
This is my MainActivity file where I'm getting the error:
class MainActivity : AppCompatActivity() {
private lateinit var binding:ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
binding = ActivityMainBinding.inflate(layoutInflater)
super.onCreate(savedInstanceState)
setContentView(binding.root)
var image = listOf {
R.drawable.illustration
R.drawable.pexelsphoto
R.drawable.preview
R.drawable.wallpaper
R.drawable.yh
}
val adapter = ViewPagerAdapter(image)
binding.viewPager.adapter = adapter
binding.viewPager.orientation = ViewPager2.ORIENTATION_VERTICAL
}
}
This is the error I'm getting on the image variable in the ViewPagerAdapter parenthesis
Type mismatch: inferred type is List<() -> Int> but List<Int> was expected
I'm unable to find any solution online on how to fix this
You defined the image list in wrong way, it must be like this
var image = listOf (
R.drawable.illustration,
R.drawable.pexelsphoto,
R.drawable.preview,
R.drawable.wallpaper,
R.drawable.yh
)
The expected type is List<Int> and it is defined like above. Meanwhile you use listOf {} which is List<() -> Int> type.

Filling in a MultiView ViewHolder inside a RecyclerView With Data Binding

So I was able to get to a close point in creating the MultiView ViewHolder, but I am still a bit confused with some details. First how would I fill in the RecyclerView since I have multiple data classes(in this case, manually). Second, how would the Adapter know when to show a particular view? I'll leave the code here
Data Class(es)
sealed class InfoRecyclerViewItems{
class WithPicture (
val id: Int,
val movieName: String,
val thoughts: String
): InfoRecyclerViewItems()
class WithoutPicture(
val id: Int,
val movieName: String,
val thoughts: String
): InfoRecyclerViewItems()
}
The Adapter
class RecyclerViewAdapter(infoItems: MutableList<InfoRecyclerViewItems>): RecyclerView.Adapter<MainViewHolder>() {
private var infoItems1: MutableList<InfoRecyclerViewItems>
init {
this.infoItems1 = infoItems
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MainViewHolder {
return when(viewType){
R.layout.container_one -> MainViewHolder.WithPictureViewHolder(
ContainerOneBinding.inflate(
LayoutInflater.from(parent.context), parent, false)
)
R.layout.container_two -> MainViewHolder.WithoutPictureViewHolder(
ContainerTwoBinding.inflate(
LayoutInflater.from(parent.context), parent, false)
)
else -> throw IllegalArgumentException("Invalid view given")
}
}
override fun onBindViewHolder(holder: MainViewHolder, position: Int) {
when(holder){
is MainViewHolder.WithPictureViewHolder -> holder.bind(infoItems1[position] as InfoRecyclerViewItems.WithPicture)
is MainViewHolder.WithoutPictureViewHolder -> holder.bind(infoItems1[position] as InfoRecyclerViewItems.WithoutPicture)
}
}
override fun getItemCount() = infoItems1.size
override fun getItemViewType(position: Int): Int {
return when(infoItems1[position]){
is InfoRecyclerViewItems.WithPicture -> R.layout.container_one
is InfoRecyclerViewItems.WithoutPicture -> R.layout.container_two
}
}
}
The ViewHolder(s)
sealed class MainViewHolder(binding: ViewBinding) : RecyclerView.ViewHolder(binding.root) {
class WithPictureViewHolder(private val binding: ContainerOneBinding) : MainViewHolder(binding){
fun bind(items: InfoRecyclerViewItems.WithPicture){
binding.part1 = items
binding.executePendingBindings()
}
}
class WithoutPictureViewHolder(private val binding: ContainerTwoBinding) : MainViewHolder(binding){
fun bind(items: InfoRecyclerViewItems.WithoutPicture){
binding.part2 = items
binding.executePendingBindings()
}
}
}
Main Activity
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.recyclerView.apply {
setHasFixedSize(true)
layoutManager = LinearLayoutManager(this#MainActivity)
}
}
}
Any suggestions are welcomed, Thank You.
SO I figured it out. based on this setup, I can create an empty MutableList and then call each different data class that I want to fill in. those data classes are linked to the ViewHolder that it is associated to, thus creating two different views inside the RecylerView.
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var manager: LinearLayoutManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val information: MutableList<InfoRecyclerViewItems> = ArrayList()
information.add(InfoRecyclerViewItems.WithPicture(2, "The Fallen", "ok"))
information.add(InfoRecyclerViewItems.WithoutPicture(4, "Black Panther", "10/10, Fantastic Movie"))
manager = LinearLayoutManager(this)
binding.recyclerView.apply {
adapter = RecyclerViewAdapter(information)
layoutManager = manager
}
}
}
Then you can just keep adding to whatever view you chose to add it to
P.S. The other files(ViewHolder, Adapter and Data Class) stay the same.

Return the result from a RecyclerView adaptor back to the MainActvity.kt?

I am trying do I return the result of the position from my Recyclerview Adapter, but I can't call the " adapter.setOnItemClickListener(this)" from MainActivity.kt? I get the error "Unresolved reference: setOnItemClickListener"?
I've had to post a new thread as I can't get all the code to be shown - after taking quite a few hours failing on this I am losing the will to live :-(
Adapter.kt
class UsersAdapter(
private val users: ArrayList<User>
) : RecyclerView.Adapter<UsersAdapter.DataViewHolder>() {
class DataViewHolder(itemView: View) :
RecyclerView.ViewHolder(itemView) {
fun bind(user: User) {
itemView.textViewUserName.text = user.name
Glide.with(itemView.imageViewAvatar.context)
.load(user.avatar)
.into(itemView.imageViewAvatar)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
DataViewHolder(
LayoutInflater.from(parent.context).inflate(
R.layout.item_layout, parent, false)
)
override fun getItemCount(): Int = Int.MAX_VALUE
override fun onBindViewHolder(holder: DataViewHolder, position: Int){
val pos = position % users.size
holder.bind(users[pos])
Log.d(Constraints.TAG, "onBindViewHolder:" + pos)
}
lateinit var listener: OnItemClickListener
public interface OnItemClickListener {
fun getAdapterPosition(position : Int )
}
public fun setOnItemClickListener(listener: OnItemClickListener) {
this.listener= listener
}
MainActivity.kt
class MainActivity : AppCompatActivity() {
lateinit var adapter: ConcatAdapter
lateinit var userVerticalAdapter: UsersAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setupDataInRecyclerView()
}
//override
public fun getAdapterPosition(position : Int ){
// required value is in the position variable
Log.d(Constraints.TAG, "This is where it is going!" )
}
private fun setupDataInRecyclerView() {
recyclerView.layoutManager = LinearLayoutManager(this,
LinearLayoutManager.HORIZONTAL, false)
userVerticalAdapter = UsersAdapter(DataSource.getUser())
val listOfAdapters = listOf(userVerticalAdapter)
adapter = ConcatAdapter(listOfAdapters)
//This errors with "Unresolved reference: setOnItemClickListener"
adapter.setOnItemClickListener(this)
recyclerView.adapter = adapter
recyclerView.scrollToPosition(Int.MAX_VALUE/2)
ItemSnapHelper().attachToRecyclerView(recyclerView)
}
public fun ShowWhatRecieved(isitthere: Int){
ShowMeIt.text = isitthere.toString()}
}
Revised and Updated MainActvity.kt
class MainActivity : AppCompatActivity(), UsersAdapter.OnItemClickListener {
lateinit var adapter: ConcatAdapter
lateinit var userVerticalAdapter: UsersAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setupDataInRecyclerView()
}
override public fun getAdapterPosition(position : Int ){
Log.d(Constraints.TAG, "This is where it is going!" )
}
private fun setupDataInRecyclerView() {
recyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)
userVerticalAdapter = UsersAdapter(DataSource.getUser())
val listOfAdapters = listOf(userVerticalAdapter)
adapter = ConcatAdapter(listOfAdapters)
adapter.setOnItemClickListener(this)
recyclerView.adapter = adapter
recyclerView.scrollToPosition(Int.MAX_VALUE/2)
ItemSnapHelper().attachToRecyclerView(recyclerView)
}
public fun ShowWhatRecieved(isitthere: Int){
ShowMeIt.text = isitthere.toString()
}
}
You are passing this as argument it will pass the context but in the parameter of function setOnItemClickListener in adapter you have defined listener of OnItemClickListener type. So you need to pass the argument of OnItemClickListener type.
You should implement the OnItemClickListener at your activity and you will be able to use this keyword. But you also can make an anonymous implementation of OnItemClickListener interface when you try to send.
But I would prefer the option 1. To implement the OnItemClickListener to your activity, implement all methods of that interface and than you can use this keyword when you need to pass OnItemClickListener anywhere in that class.
You have not implemented interface in your as below -
class MainActivity : AppCompatActivity(), RecycleViewCartAdapter.OnItemClickListener
Here is the problem mate.
Despite all your effort by creating an interface and implementing it inside your recycler-view, I suggest you use lambda instead of interface since you are writing Kotlin, and writing a lambda instead of an interface is a cleaner approach in Kotlin for ClickListener.
Create a lambda parameter both for your adapter and your viewholder. since you want to pass the position of a clicked item so your lambda must have an Int input and nothing as output so it would look like this: listener: (Int) -> Unit.
The rest of your code would be something like this, you get the idea I just edited important parts:
class UsersAdapter(private val users : ArrayList<CrashlyticsReport.Session.User>,
private val listener : (Int) -> Unit) :
RecyclerView.Adapter<UsersAdapter.DataViewHolder>() {
class DataViewHolder(itemView : View, private val listener : (Int) -> Unit) :
RecyclerView.ViewHolder(itemView) {
init {
itemView.setOnClickListener { listener(adapterPosition) }
}
fun bind(user : CrashlyticsReport.Session.User) {
itemView.textViewUserName.text = user.name
Glide.with(itemView.imageViewAvatar.context).load(user.avatar).into(itemView.imageViewAvatar)
}
}
}
then in your activity you can write:
userVerticalAdapter = UsersAdapter(DataSource.getUser()) {
//clicklistener event here
}
or:
userVerticalAdapter = UsersAdapter(DataSource.getUser(),this::ShowWhatRecieved)
also since you're using ConcatAdapter it's better to use bindingAdapterPosition instead of adapterPosition.

Observer on LiveData in RecyclerView - Kolin

how can I implement LiveData Observer in the Recycler View? I have a global var `someDataChanged' that gets updated from another Fragment. Any help would be greatly appreciated!
class GlobalActivity {
companion object {
someDataChanged = MutableLiveData<Int>()
}
}
Then in the Adapter.kt for the RecyclerView I need to listen if the someDataChanged. I have the following code:
Adapter.kt
var mRecyclerView: RecyclerView? = null
class MainAdapter(val myResponse: MyResponse): RecyclerView.Adapter<CustomViewHolder>(){
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomViewHolder {
//Error: this is not recognized.
GlobalActivity.someDataChanged`.observe(this, Observer{
println("Data Changed")
})
val layoutInflater = LayoutInflater.from(parent?.context)
val cellForRow = layoutInflater.inflate(R.layout.my_custom_cell, parent,false)
return CustomViewHolder(cellForRow)
}
override fun onBindViewHolder(holder: CustomViewHolder, position: Int) {
//some code
class CustomViewHolder(val view: View, var myPass: Passes? = null): RecyclerView.ViewHolder(view){
}
}
}
Not sure if I understood your question but if you want to update your recycler view every time the liveData is updated you should create something like this:
class MyActivity : AppCompatActivity() {
private val viewModel: MyViewModel by viewModels()
lateinit var adapter: MyAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
adapter = MyAdapter()
recyclerView.adapter = adapter
viewModel.myLiveData.observe(this, Observer { newContent ->
adapter.updateContent(newContent)
})
}
}
class MyViewModel: ViewModel() {
private val _myLiveData = MutableLiveData<String>()
val myLiveData: LiveData<String> = _myLiveData
// You should call this to update you liveData
fun updateInfo(newInfo: String) {
_myLiveData.value = newInfo
}
}
class MyAdapter: RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private var content: String = ""
...
fun updateInfo(newString: String) {
content = newString
notifyDataSetChanged() //or you can implement a DiffUtil.Callback
}
}
in RecyclerView.Adapter need to override onCreateViewHolder, pass lifeCycleOwner
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {
val lifeCycleOwner = parent.context as LifecycleOwner
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_layout, parent, false)
return ItemViewHolder(view, lifeCycleOwner)
}
your inner class constructor, should have lifecycleOwner as param
inner class ItemViewHolder(view: View, private val lifecycleOwner: LifecycleOwner) : RecyclerView.ViewHolder(view) {
then you can use code as normally
mutableLiveData.observe(lifecycleOwner) { /** your logic handle **/ }

How to observe member variables of a class in MutableLiveData?

I am trying to observe fields of my class without exposing it. So far, I've tried this:
TaskItemViewModel.kt
open class TaskItemViewModel(private val navigator: ITaskNavigator) : ViewModel() {
private val taskItem: MutableLiveData<TaskItem> = MutableLiveData()
val title: LiveData<String?> = Transformations.map(taskItem) { it.title }
val content: LiveData<String?> = Transformations.map(taskItem) { it.content }
var showCheck: LiveData<Boolean> = Transformations.map(taskItem) { it.isCompleted }
fun setModel(model: TaskItem) {
this.taskItem.value = model
}
}
ItemListScreenAdapter.kt
class ItemListScreenAdapter(private val navigator: ITaskNavigator) : RecyclerView.Adapter<ItemListScreenAdapter.TaskItemViewHolder>() {
private val TAG = "ItemListScreenAdapter"
private var dataset: List<TaskItem> = listOf()
override fun onBindViewHolder(viewHolder: TaskItemViewHolder, position: Int) {
with(viewHolder.binding) {
this.viewModel?.setModel(dataset[position])
executePendingBindings()
}
}
fun updateDataset(dataset: List<TaskItem>) {
Log.d(TAG,"Updating dataset")
this.dataset = dataset
notifyDataSetChanged()
}
override fun getItemCount(): Int = dataset.size
override fun onCreateViewHolder(parent: ViewGroup, type: Int): TaskItemViewHolder {
val inflater = LayoutInflater.from(parent.context)
val binding = ItemTaskBinding.inflate(inflater, parent, false)
binding.viewModel = TaskItemViewModel(navigator)
return TaskItemViewHolder(binding)
}
class TaskItemViewHolder(val binding: ItemTaskBinding) : RecyclerView.ViewHolder(binding.root)
}
If I call setModel before inflating the view, everything works fine. However, after the view is inflated, the view is not updated even if taskItem 's value is updated. You can be assured that updateDataset is called everytime there is a change in dataset.
I want the view to be updated whenever I call setModel in corresponding viewmodel. What are the ways to achieve this?
For this viewmodel, I want to use ViewModel rather than BaseObservable. Therefore, please give your answers according to this.
EDIT:
I have found the solution to the problem.
in ItemListScreenAdapter's onCreateViewHolder method, after inflating, I needed to set LifeCycleOwner of the binding.
I added the following line after inflating the ItemTaskBinding.
binding.setLifecycleOwner(parent.context as MainActivity)
and the problem is solved and view is being updated.

Categories

Resources