Filling in a MultiView ViewHolder inside a RecyclerView With Data Binding - android

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.

Related

How can I display firebase data in a fragment with recyclerview? Kotlin Android Studio

hello I have an activity that has a navigation bar and fragments inside, in one of my fragments I save data in firebase, I want to get those data and display it in other fragment with recycler view. I watched a tutorial on youtube to do it cuz my teacher didn't teach us anything, the issue is the fragment that was supposed the display the data shows nothing, it's blank but I didn't understand why cuz I'm really new to this, if someone can take a look at my code and help me I would really appreciate it
This is my data class
data class Event(var eventName: String, var eventTime:String?=null)
This is ViewModel
class EventViewModel :ViewModel() {
private val repository : EventRepository
private val _allEvents = MutableLiveData<List<Event>>()
val allEvents : LiveData<List<Event>> = _allEvents
init {
repository= EventRepository().getInstance()
repository.loadEvents(_allEvents)
}
}
This is Repository
class EventRepository {
private val databaseReference: DatabaseReference= FirebaseDatabase.getInstance().getReference("PetEvents")
#Volatile private var INSTANCE : EventRepository ?=null
fun getInstance() : EventRepository{
return INSTANCE ?: synchronized(this) {
val instance = EventRepository()
INSTANCE=instance
instance
}
}
fun loadEvents(eventList : MutableLiveData<List<Event>>){
databaseReference.addValueEventListener(object:ValueEventListener{
override fun onDataChange(snapshot: DataSnapshot) {
try {
val _eventList : List<Event> = snapshot.children.map { dataSnapshot ->
dataSnapshot.getValue(Event::class.java)!!
}
eventList.postValue(_eventList)
}
catch (e: Exception){
}
}
override fun onCancelled(error: DatabaseError) {
TODO("Not yet implemented")
}
})
}
}
This is Adapter
class EventAdapter : RecyclerView.Adapter<EventAdapter.MyViewHolder>() {
private var eventList =ArrayList<Event>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(
R.layout.event_item_cell,
parent, false
)
return MyViewHolder(itemView)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val currentItem = eventList[position]
holder.eventName.text =currentItem.eventName
holder.eventTime.text =currentItem.eventTime
}
override fun getItemCount(): Int {
return eventList.size
}
fun updateEventList(eventList: List<Event>){
this.eventList.clear()
this.eventList.addAll(eventList)
notifyDataSetChanged()
}
class MyViewHolder(itemView: View): RecyclerView.ViewHolder(itemView){
val eventName :TextView = itemView.findViewById(R.id.txEventName)
val eventTime :TextView = itemView.findViewById(R.id.txEventTime)
}
}
And this is the fragment I wish to display the data
private lateinit var viewModel : EventViewModel
private lateinit var eventRecyclerView: RecyclerView
lateinit var adapter: EventAdapter
class MainFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_main, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
eventRecyclerView=view.findViewById(R.id.recyclerView)
eventRecyclerView.layoutManager= LinearLayoutManager(context)
eventRecyclerView.setHasFixedSize(true)
adapter = EventAdapter()
eventRecyclerView.adapter= adapter
viewModel = ViewModelProvider(this).get(EventViewModel::class.java)
viewModel.allEvents.observe(viewLifecycleOwner, Observer {
adapter.updateEventList(it)
})
}
}
In my fragment xml I put a recycler view, there's no issue about that
And my item cell is a cardview idk if it has anything to with the problem I'm getting tho

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.

kotlin recyclerview MVVM error - IndexOutOfBoundsException

On recyclerview I'm setting a list, using MVVM where I'm getting an error,
IndexOutOfBoundsException.
code I use for recyclerview adapter is below - onBindViewHolder is where I set articles:List
class MainAdapter(private val context: Context,private val articles:List<Article>) : ListAdapter<Article, MainAdapter.MainViewHolder>(DiffUtil()) {
inner class MainViewHolder(val binding: HomeRecViewBinding) : RecyclerView.ViewHolder(binding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MainViewHolder {
val binding = HomeRecViewBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return MainViewHolder(binding)
}
override fun onBindViewHolder(holder: MainViewHolder, position: Int) {
val articles_ = articles[position]
holder.binding.artical = articles_
}
class DiffUtil : androidx.recyclerview.widget.DiffUtil.ItemCallback<Article>() {
override fun areItemsTheSame(oldItem: Article, newItem: Article): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: Article, newItem: Article): Boolean {
return oldItem == newItem
}
}
}
my ViewModel is given below
val article : LiveData<NewsModel>
get() = repoHelper.article
my main Activity
class TestActivity : AppCompatActivity() {
lateinit var binding: ActivityTestBinding
lateinit var mainViewModel: MainViewModel
lateinit var article_: List<Article>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this,R.layout.activity_test)
article_ = ArrayList()
val mainAdapter = MainAdapter(this,article_)
binding.recView.apply {
this.layoutManager = LinearLayoutManager(this#TestActivity)
this.adapter = mainAdapter
}
val apiHelper = RetrofitHelper.getInstance().create(ApiHelper::class.java)
val database_ = DBHelper.getDatabase(applicationContext)
val repoHelper = RepoHelper(apiHelper,database_,applicationContext)
mainViewModel = ViewModelProvider(this,ViewModelFactory(repoHelper)).get(MainViewModel::class.java)
mainViewModel.article.observe(this, Observer {
Log.d("list_of_article",it.articles.toString())
mainAdapter.submitList(it.articles)
})
}
what's the main cause for this error and how do I resolve it.
article_ = ArrayList()
val mainAdapter = MainAdapter(this,article_)
Here you provide an empty list to the Adapter.
After this never changes!
mainAdapter.submitList(it.articles) does not set this list in the adapter.
So val articles_ = articles[position] will try to access something from an empty list.
As a matter of fact. You don't even need to have this list at all. ListAdapter has methods to access the list you provide with submitList
Instead of
val articles_ = articles[position]
you can do
val articles_ = getItem(position)
and then you can remove the articles completely there so change
class MainAdapter(private val context: Context,private val articles:List<Article>)
to
class MainAdapter(private val context: Context)
and change
val mainAdapter = MainAdapter(this,article_)
to
val mainAdapter = MainAdapter(this)
Answer:
You are not implementing the getItemCount(). It returns the size of the list.
override fun getItemCount(): Int {
return articles.size
}
Cause of IndexOutOfBoundsException is that you are trying to access the index of list which is beyond the size of list.
Example:
The size of list is 2. So the elements will be at index 0 and 1. If you try to access index 2 then you will get to see IndexOutOfBoundsException

Cant seem to get my adapter to add my list to recyclerView

Id like to initially have a list show up on my recyclerView before implementing some kind of edit function to it. However, no matter what I do, the list I made wont show up. I've got a feeling that I'm doing something wrong with the bind function.
Here is my adapter
class WorkoutAdaptor (
var workouts: List<Workout>
) : RecyclerView.Adapter<WorkoutAdaptor.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val workoutCardBinding = WorkoutCardBinding.inflate(
LayoutInflater.from(parent.context), parent, false)
return ViewHolder(workoutCardBinding)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(workouts[position])
}
override fun getItemCount(): Int {
return workouts.size
}
inner class ViewHolder(private val workoutCardBinding: WorkoutCardBinding) :
RecyclerView.ViewHolder(workoutCardBinding.root) {
fun bind(workout: Workout) {
workoutCardBinding.apply {
tvWorkoutCard
}
}
}
}
and here is my 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.btnNext.setOnClickListener {
Intent(this, SecondActivity::class.java).also {
startActivity(it)
}
}
// var workout = intent.getSerializableExtra("EXTRA_WORKOUT")
// binding.tvWorkoutCard.text = workout.toString()
var workoutList = mutableListOf(
Workout("a","d","d","d"),
Workout("a","d","d","d"),
Workout("a","d","d","d"),
Workout("a","d","d","d"),
Workout("a","d","d","d"),
)
val adaptor = WorkoutAdaptor(workoutList)
binding.recyclerView.adapter = adaptor
binding.recyclerView.layoutManager = LinearLayoutManager(this)
}
}
Change your bind method to this.
fun bind(workout: Workout) {
workoutCardBinding.textview.text = workout.objectName
}

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 **/ }

Categories

Resources