Recycler view not update itself - android

I have a problem with recycler view. In my previous app, when i get the list for recycler view adapter from database and observe it in my fragment, i used the notifyDataSetChanged() and when i tried to delete a item , view updated successfully. But in this app this does not work and i don't understand why. When i click the delete button the item deleted in database successfully but i can't see it immediatly. When i go to any other fragment and back to this Favourites fragment i see the items deleted.
I tried all the options in stackoverflow but still i can't fix it.
My Adapter:
class FavouritesAdapter(owner: ViewModelStoreOwner, val favouritesList : ArrayList<Vocabulary>) : RecyclerView.Adapter<FavouritesAdapter.FavouritesViewHolder>() {
val viewModel = ViewModelProvider(owner).get(FavouritesViewModel::class.java)
class FavouritesViewHolder(val binding: FavouritesItemRowBinding) : RecyclerView.ViewHolder(binding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FavouritesViewHolder {
return FavouritesViewHolder(FavouritesItemRowBinding.inflate(LayoutInflater.from(parent.context), parent, false))
}
override fun onBindViewHolder(holder: FavouritesViewHolder, position: Int) {
holder.binding.englishWordTV.text = favouritesList[position].word
holder.binding.turkishWordTV.text = favouritesList[position].translation
holder.binding.deleteButtonRV.setOnClickListener {
viewModel.deleteVocabulary(favouritesList[position])
notifyDataSetChanged()
}
}
override fun getItemCount(): Int {
return favouritesList.size
}
fun updateList(myList : List<Vocabulary>) {
favouritesList.clear()
favouritesList.addAll(myList)
notifyDataSetChanged()
}
}
My problem is in delete button in my recycler row;
holder.binding.deleteButtonRV.setOnClickListener {
viewModel.deleteVocabulary(favouritesList[position])
notifyDataSetChanged()
}
And here is my fragment ;
class FavouritesFragment : Fragment() {
private var _binding: FragmentFavouritesBinding? = null
private val binding get() = _binding!!
private lateinit var favouritesAdapter : FavouritesAdapter
private lateinit var viewModel : FavouritesViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentFavouritesBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = ViewModelProvider(this).get(FavouritesViewModel::class.java)
favouritesAdapter = FavouritesAdapter(this, arrayListOf())
viewModel.getAllVocabulariesFromDB()
prepareRecyclerView()
observeFavouritesLiveData()
}
fun prepareRecyclerView(){
binding.favouritesRecyclerView.apply {
layoutManager = LinearLayoutManager(context)
adapter = favouritesAdapter
}
}
fun observeFavouritesLiveData(){
viewModel.favouritesListLiveData.observe(viewLifecycleOwner, Observer {
it?.let {
favouritesAdapter.updateList(it)
}
})
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}

Try with notifyItemRemoved(position) instead of notifyDataSetChanged().

It all looks fine to me - you observe the favourites LiveData, that passes the new data to an update function in your Adapter, and that modifies the internal data set and calls notifyDataSetChanged() (which works for any kind of update).
So, are you sure your ViewModel is updating favouritesListLiveData properly when you call deleteVocabulary? Check if your observer is actually firing with a new value when you hit delete, and check if its contents are what you expect (the previous list minus the thing you want removed)
You could check it with some logging, but setting some breakpoints and debugging the app might be more helpful if you're not sure where it's going wrong
(also your button doesn't need to call notifyDataSetChanged() - that only needs to happen when the data is updated, which happens through the update function, in there is the right place for it!)

i tried to use the path "button click -> UI sends delete event to VM -> VM updates data -> observer sees new data -> observer calls update with new data" as #cactuctictacs mentioned. I added this lines to my adapter,
lateinit var onDeleteItemClick : ((Vocabulary) -> Unit)
holder.binding.deleteButtonRV.setOnClickListener {
onDeleteItemClick.invoke(favouritesList[position])
notifyItemRemoved(position)
}
and added to my fragment,
fun deleteButtonClicked(){
favouritesAdapter.onDeleteItemClick = {
viewModel.deleteVocabulary(it)
viewModel.getAllVocabulariesFromDB()
observeFavouritesLiveData()
favouritesAdapter.notifyDataSetChanged()
}
}
I hope this is the proper way to do this.

Related

How to fix IndexOutOfBoundsException in RecyclerView (Jetpack Navigation, DataBinding)

My app uses "Jetpack Navigation, DataBinding, ViewModel".
A Fragment has RecyclerView.
If I touch one of the items, it is navigated to B Fragment.
And when I come back to A fragment (by back button), the crash occurs.
IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter
A Fragment is:
#AndroidEntryPoint
class AFragment : Fragment() {
private var _binding: FragmentABinding? = null
private val binding: FragmentABinding
get() = _binding!!
private val viewModel: AViewModel by viewModels()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentABinding.inflate(inflater, container, false)
binding.vm = viewModel
binding.lifecycleOwner = viewLifecycleOwner
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.rvItems.adapter = ItemsAdapter()
viewModel.getItems()
}
}
A ViewModel is:
#HiltViewModel
class AViewModel #Inject constructor(
private val itemRepo: ItemRepo,
private val disposable: CompositeDisposable
) : ViewModel() {
private var _items: MutableLiveData<List<Item>> = MutableLiveData()
val items: LiveData<List<Item>>
get() = _items
override fun onCleared() {
super.onCleared()
disposable.clear()
}
fun getItems() {
itemRepo.getAll()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
_items.postValue(it)
}, { t ->
})
.addTo(disposable)
}
}
XML Layout is:
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rv_items"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:bind_items="#{vm.items}"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
Binding Adapter is:
#BindingAdapter("bind_items")
fun setItems(view: RecyclerView, list: List<Item>?) {
if (!list.isNullOrEmpty()) {
(view.adapter as? ItemsAdapter)?.update(list)
}
}
ItemsAdapter is:
class ItemsAdapter : RecyclerView.Adapter<ItemsAdapter.ItemHolder>() {
private val items: MutableList<Item> = mutableListOf()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemHolder {
return ItemHolder (ItemItemBinding.inflate(LayoutInflater.from(parent.context), parent, false))
}
override fun onBindViewHolder(holder: ItemHolder, position: Int) {
holder.bind(items[position])
}
fun update(newItems: List<Item>) {
items.clear()
items.addAll(newItems)
notifyItemRangeInserted(0, newItems.size)
}
class ItemHolder(
private val binding: ItemItemBinding
) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: Item) {
with(binding) {
tvDate.text = item.date
}
}
}
}
I think this issue is related to the "Jetpack Navigation".
But I don't know exactly what should I do...
From the android documentation of RecyclerView.Adapter of notifyItemRangeInserted:
Notify any registered observers that the currently reflected itemCount items >starting at positionStart have been newly inserted. The items previously located at >positionStart and beyond can now be found starting at position positionStart + >itemCount.
This means that after you call that function the adapter expects the list to be long oldList.size + newList.size, but finds only the new elements and so goes out of bounds.
The simplest solution is to use notifyDataSetChanged instead, though you may want to use other solutions for performance.
You're not telling the adapter when you remove the items, you'd have to do something like this:
fun update(newItems: List<Item>) {
// remove the old items
val originalSize = items.size
items.clear()
notifyItemRangeRemoved(0, originalSize)
// add the new items
items.addAll(newItems)
notifyItemRangeInserted(0, newItems.size)
}
But this is going to look a bit weird, I doubt that's what you want (it will animate the old items out, and then animate the new items in). Like the previous answer suggested, the easiest way is to use notifyDataSetChanged instead (the list will just change visually immediately with no animations)
fun update(newItems: List<Item>) {
items.clear()
items.addAll(newItems)
notifyDataSetChanged()
}
If you want nice change animations, you should probably look into DiffUtil (that will calculate the difference between your two lists - but it is an expensive call for large lists). You can get away with running on the UI thread for small lists, but you shouldn't really (using DiffUtil off the UI thread is quite a bit more complicated if you want to guarantee robust code). You could get the DiffResult like this:
fun createDiffResult(oldList: List<Thing>, newList: List<Thing>): DiffUtil.DiffResult {
return DiffUtil.calculateDiff(object : DiffUtil.Callback() {
override fun getOldListSize(): Int {
return oldList.size
}
override fun getNewListSize(): Int {
return newList.size
}
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
// are the items conceptually the same item?
// (you might compare a unique id here)
return true
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
// do the items _look_ the same when rendered in your recycler view?
// (you only need to compare the values for the things that are visible in your implementation of the item view)
return true
}
})
}
then use it like this:
diffResult.dispatchUpdatesTo(adapter);

notifyDataSetChanged and toMutableList don 't Works (Kotlin)

I am getting data from API http and using dataBinding and viewModel, all works but when I try to convert my list to mutableList and add All don't getting nothing, also try pass the data of simple way and working , the data is be there, the problem is that notifyDataSetChanged() don't do changes in my Adapter and I dont see nathing in the recyclerView.
class MainActivity : AppCompatActivity() {
private lateinit var viewModel: MostPopularTVShowsViewModel
private lateinit var activityMainBinding : ActivityMainBinding
private var tvShows: List<TVShow> = ArrayList()
private lateinit var tvShowAdapter:TVShowsAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activityMainBinding = DataBindingUtil.setContentView(this,R.layout.activity_main)
doInitialization()
}
private fun doInitialization(){
activityMainBinding.tvShowRecycleView.setHasFixedSize(true)
viewModel= ViewModelProvider(this).get(MostPopularTVShowsViewModel::class.java)
tvShowAdapter= TVShowsAdapter(tvShows)
activityMainBinding.tvShowRecycleView.adapter=tvShowAdapter
getMostPopularTVShows()
}
private fun getMostPopularTVShows(){
activityMainBinding.isLoading=true
viewModel.getMostPopularTVShows(0).observe(this, { mostPopularTVShowsResponse ->
activityMainBinding.isLoading=false
if (mostPopularTVShowsResponse != null){
tvShows.toMutableList().addAll(mostPopularTVShowsResponse.tvShows)
//tvShows=mostPopularTVShowsResponse.tvShows
println("size is: "+tvShows.size)
tvShowAdapter.notifyDataSetChanged()
}else{
Toast.makeText(this," NULL", Toast.LENGTH_SHORT).show()
}
})
}
}
when i change tvShows=mostPopularTVShowsResponse.tvShows insted tvShows.toMutableList().addAll(mostPopularTVShowsResponse.tvShows) the list gets the data but notifyDataSetChanged dont working
this is my adapter
class TVShowsAdapter(private val items: List<TVShow>): RecyclerView.Adapter<TVShowsAdapter.ViewHolder>(){
private lateinit var layoutInflater: LayoutInflater
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
layoutInflater= LayoutInflater.from(parent.context)
val binding: ItemContainerTvShowBinding = DataBindingUtil.inflate(
layoutInflater, R.layout.item_container_tv_show,parent,false)
return ViewHolder(binding)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int)= holder.bind(items[position])
override fun getItemCount()= items.size
class ViewHolder(private val itemContainerTvShowBinding: ItemContainerTvShowBinding):
RecyclerView.ViewHolder(itemContainerTvShowBinding.root){
fun bind (tvShow: TVShow){
itemContainerTvShowBinding.tvShow=tvShow
itemContainerTvShowBinding.executePendingBindings()
}
}
}
You're not actually changing the data in the adapter. You're passing in a reference to the tvShows ArrayList when you create the adapter, so if you changed the contents of that list the adapter would be able to see it. But when you do
tvShows.toMutableList().addAll(mostPopularTVShowsResponse.tvShows)
you're creating a new, separate list by calling toMutableList, and you're adding stuff to that. tvShows is still the original, immutable ArrayList with nothing in it.
If you're going to do it this way, you need to make the list mutable from the beginning
val tvShows = mutableListOf<TVShow>()
then you can clear and add to it when you get new data.
Generally though, a better idea is to give the adapter some kind of setData function where you pass in a list, so it can update its own internal data set and notify itself about the change. That way the adapter manages it state itself, all your activity or fragment needs to do is pass it some new data

Cannot populate spinner with data from database?

I'm trying to populate a spinner with data using room, I'm getting no errors but my spinner isn't displaying anything. I think it might have something to do with how I'm calling initFirstUnitSpinnerData() in my onCreateView method? But I'm having no luck. I'm using kotlin.
Thanks in advance.
DAO:
#Query("SELECT firstUnit FROM conversion_table WHERE category LIKE :search")
fun getByCategory(search: String): LiveData<List<String>>
Repository:
fun getByCategory(search: String): LiveData<List<String>>{
return conversionsDAO.getByCategory(search)
}
View Model:
fun getByCategory(search: String): LiveData<List<String>> {
return repository.getByCategory(search)
}
Fragment:
class UnitsFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
private lateinit var mConversionsViewModel: ConversionsViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_units, container, false)
mConversionsViewModel = ViewModelProvider(this).get(ConversionsViewModel::class.java)
initFirstUnitSpinnerData()
return view
}
private fun initFirstUnitSpinnerData() {
val spinnerFirstUnit = view?.findViewById<Spinner>(R.id.firstUnitSpinner)
if (spinnerFirstUnit != null) {
val allConversions = context?.let {
ArrayAdapter<Any>(it, R.layout.support_simple_spinner_dropdown_item)
}
mConversionsViewModel.getByCategory("Distance")
.observe(viewLifecycleOwner, { conversions ->
conversions?.forEach {
allConversions?.add(it)
}
})
spinnerFirstUnit.adapter = allConversions
spinnerFirstUnit.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(
parent: AdapterView<*>?,
view: View?,
position: Int,
id: Long
) {
Toast.makeText(requireContext(), "$allConversions", Toast.LENGTH_LONG).show()
}
override fun onNothingSelected(parent: AdapterView<*>?) {
}
}
}
}
}
This is the kind of thing you should debug really - click on the left gutter for the first line of initFirstUnitSpinnerData (the val spinnerFirstUnit one), click the Debug App button up near the Run one, and it'll pause when it hits that breakpoint you added.
Then you can move through, step by step, looking at the values of stuff and checking if it looks right, and how the code executes. It's a super useful thing to learn and it'll save you a lot of headaches!
Anyway I'm guessing your problem is that you're calling initFirstUnitSpinnerData from inside onCreateView - the latter is called by the Fragment when it needs its layout view inflating, which you do and then return it to the Fragment.
So inside initFirstUnitSpinnerData, when you reference view (i.e. the Fragment's view, which it doesn't have yet, because onCreateView hasn't returned it yet) you're getting a null value. So spinnerFirstUnit ends up null, and when you null check that, it skips setting up the adapter.
Override onViewCreated (which the Fragment calls when it has its layout view) and call your function from there, it'll be able to access view then - see if that helps!

Shared element does not return to RecyclerView item

I have a fragment called MainFragment that contains a ViewPager that contains another fragment called LibraryFragment.
LibraryFragment contains a RecyclerView with a list of items that contain an ImageView. The ImageView's contents are loaded with Coil.
When an item is clicked, LibraryFragment navigates to another fragment called ArtistDetailFragment and uses the ImageView from the item.
The problem is that while the enter transition works fine, the ImageView does not return to the list item when navigating back and only fades away. Ive attached an example below:
Ive tried using postponeEnterTransition() and startPostponedEnterTransition() along with adding a SharedElementCallback but neither have worked that well. I've also ruled out Coil being the issue.
Heres LibraryFragment:
class LibraryFragment : Fragment() {
private val musicModel: MusicViewModel by activityViewModels()
private val libraryModel: LibraryViewModel by activityViewModels()
private lateinit var binding: FragmentLibraryBinding
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentLibraryBinding.inflate(inflater)
binding.libraryRecycler.adapter = ArtistAdapter(
musicModel.artists.value!!,
BindingClickListener { artist, itemBinding ->
navToArtist(artist, itemBinding)
}
)
return binding.root
}
private fun navToArtist(artist: Artist, itemBinding: ItemArtistBinding) {
// When navigation, pass the artistImage of the item as a shared element to create
// the image popup.
findNavController().navigate(
MainFragmentDirections.actionShowArtist(artist.id),
FragmentNavigatorExtras(
itemBinding.artistImage to itemBinding.artistImage.transitionName
)
)
}
}
Heres ArtistDetailFragment:
class ArtistDetailFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val binding = FragmentArtistDetailBinding.inflate(inflater)
sharedElementEnterTransition = TransitionInflater.from(requireContext())
.inflateTransition(android.R.transition.move)
val musicModel: MusicViewModel by activityViewModels()
val artistId = ArtistDetailFragmentArgs.fromBundle(requireArguments()).artistId
// Get the transition name used by the recyclerview ite
binding.artistImage.transitionName = artistId.toString()
binding.artist = musicModel.artists.value?.find { it.id == artistId }
return binding.root
}
}
And heres the RecyclerView Adapter/ViewHolder:
class ArtistAdapter(
private val data: List<Artist>,
private val listener: BindingClickListener<Artist, ItemArtistBinding>
) : RecyclerView.Adapter<ArtistAdapter.ViewHolder>() {
override fun getItemCount(): Int = data.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(
ItemArtistBinding.inflate(LayoutInflater.from(parent.context))
)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(data[position])
}
// Generic ViewHolder for an artist
inner class ViewHolder(
private val binding: ItemArtistBinding
) : RecyclerView.ViewHolder(binding.root) {
// Bind the view w/new data
fun bind(artist: Artist) {
binding.artist = artist
binding.root.setOnClickListener { listener.onClick(artist, binding) }
// Update the transition name with the new artist's ID.
binding.artistImage.transitionName = artist.id.toString()
binding.executePendingBindings()
}
}
}
EDIT: I used postponeEnterTransition and startPostponedEnterTransition like this:
// LibraryFragment
override fun onResume() {
super.onResume()
postponeEnterTransition()
// Refresh the parent adapter to make the image reappear
binding.libraryRecycler.adapter = artistAdapter
// Do the Pre-Draw listener
binding.libraryRecycler.viewTreeObserver.addOnPreDrawListener {
startPostponedEnterTransition()
true
}
}
This only makes the RecyclerView itself refresh however, the shared element still fades away instead of returning to the RecyclerView item.
Chris Banes says;
You may wonder why we set the OnPreDrawListener on the parent rather than the view itself. Well that is because your view may not actually be drawn, therefore the listener would never fire and the transaction would sit there postponed forever. To work around that we set the listener on the parent instead, which will (probably) be drawn.
https://chris.banes.dev/fragmented-transitions/
try this;
change
// LibraryFragment
override fun onResume() {
super.onResume()
postponeEnterTransition()
// Refresh the parent adapter to make the image reappear
binding.libraryRecycler.adapter = artistAdapter
// Do the Pre-Draw listener
binding.libraryRecycler.viewTreeObserver.addOnPreDrawListener {
startPostponedEnterTransition()
true
}
}
enter code here
to
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
postponeEnterTransition()
binding = FragmentLibraryBinding.inflate(inflater)
binding.libraryRecycler.adapter = ArtistAdapter(
musicModel.artists.value!!,
BindingClickListener { artist, itemBinding ->
navToArtist(artist, itemBinding)
}
)
(view?.parent as? ViewGroup)?.doOnPreDraw {
// Parent has been drawn. Start transitioning!
startPostponedEnterTransition()
}
return binding.root
}

How can I listen for onCheckedChangeListener in a RecyclerView using MVVM?

I'm attempting to write a basic Todo list in Kotlin but wanted to use the recommended best practices and Android architecture components. At this point I have the basic architecture set up and there is a RecyclerView list of items stored in the database with a checkbox on the left side, and a description to the right. So far the list automatically updates when new data is added (via the floating action button). Now I want to update the record immediately whenever the checkbox is clicked for a particular item.
I can't figure out how or where to set the checkbox listener in order to pass the checked state and the item id to the ViewModel in order to update the data in the database. I thought about defining the listener directly inside the adapter, but then I can't find any way to call my ViewModel's update method. But then if I set up the listener in the fragment and pass that to the adapter, I can't find a way to get the id of the item.
todo list screenshot
This is my current fragment:
class ChecklistFragment : Fragment() {
private lateinit var checklistViewModel: ChecklistViewModel
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_checklist, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Set up RecyclerView
items_list.layoutManager = LinearLayoutManager(activity)
val adapter = ToDoItemAdapter(object: CompoundButton.OnCheckedChangeListener {
override fun onCheckedChanged(buttonView: CompoundButton?, isChecked: Boolean) {
Toast.makeText(activity, "checked", Toast.LENGTH_SHORT).show()
}
})
items_list.adapter = adapter
// Set up ViewModel
checklistViewModel = ViewModelProviders.of(this).get(ChecklistViewModel::class.java)
checklistViewModel.allToDoItems.observe(this, Observer { toDoItems ->
toDoItems?.let { adapter.setToDoItems(it) }
})
// Set up fab
add_list_item_fab.setOnClickListener {
checklistViewModel.insert(ToDoItem(description = "Item ${Random.nextInt(1,999)}", checked = Random.nextBoolean()))
}
}
}
This is my current adapter:
class ToDoItemAdapter(val onCheckedChangeListener: CompoundButton.OnCheckedChangeListener) : RecyclerView.Adapter<ToDoItemAdapter.ViewHolder>() {
private var toDoItems = emptyList<ToDoItem>()
inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val checkbox: CheckBox = view.checkbox
val tvDescription: TextView = view.tv_description
fun bind(position: Int) {
checkbox.isChecked = toDoItems[position].checked
checkbox.setOnCheckedChangeListener(onCheckedChangeListener)
tvDescription.text = toDoItems[position].description
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.checklist_item, parent, false)
return ViewHolder(itemView)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(position)
}
override fun getItemCount() = toDoItems.size
internal fun setToDoItems(toDoItems: List<ToDoItem>) {
this.toDoItems = toDoItems
notifyDataSetChanged()
}
}
What is a best practice approach to listening to checked items and immediately storing those changes in the database using the MVVM architecture?
If you want to click or check something in RecyclerView Item,
There is a elegant way for that.
class MyAdapter(val viewModel : ViewModel) : RecyclerView.ViewModel<ViewHolder>{
fun onCreateViewModel(..){
val binding = ItemRecyclerViewBinding.inflate(LayoutInflater.from(parent.context), parent,false)
binding.vm = viewModel
}
}
in XML
<data>
<variable name="vm" type="YourViewModel"/
<variable name="yourItem" type="YourItem"/
</data>
<FrameLayout or something
android:onClick = "#{(view) -> vm.onClickItem(view, yourItem)}">
</FrameLayout>
in ViewModel Class,
fun onClickItem(view : View, yourItem : YourItem){
Log.e(TAG,"$view is clicked with $yourItem")
}
I write about how to listen click or check event from recyclerview items
If you want how to store these datas to DB, you can use your own routine for that in ViewModel Class
Happy coding!

Categories

Resources