Hello guys. I need advice. I have a `PagedListAdap...
# android
s
Hello guys. I need advice. I have a
PagedListAdapter
I populate it using
LivePagedListBuilder
. Each list item has
Favorite button
. How it would be better to handle
Favorite button
state? I need to know if an item is already added to favorites and when the button is pressed I need to update
Favorites database
to delete or add this item. (I'm using architecture components in my app). Thank you. My solution: Inside
onBindViewHolder
I call the callback to my activity and pass an item and its position. ViewModel checks if this item is favorited or not and returns a result back to the activity, the activity calls
adapter.notifyItemChanged(position)
. Same goes for
Favorite button
click listener. I need to pass an adapter position to ViewModel I don't like it. I think this solution is really bad but I can't come up with something better.
b
Do you need to pass the position around? Could you not just pass an ID and find it on both ends that way. List.indexOf { it.id == clickedId }
s
I need the position because I'm using
PagedListAdapter
b
to do the update right? You can find it by the ID again?
Copy code
val index = List.indexOf { it.id == clickedId } 
if(index != -1) {
   notifyItemUpdated(index)
}
add the id to the view holder when binding it, and use a custom click listener that returns that id?
s
Don't need to pass the position to the view holder. ViewHolder class has adapterPosition property.
b
What's your concern, you don't want to pass a position into the view model?
"I need to pass an adapter position to ViewModel I don't like it. I think this solution is really bad but I can't come up with something better."
s
I just want to know if there're any better solutions to achieve this
Not only about passing the position
The approach in general
b
I don't think it's bad overall. The click action needs to make it to the view model so it can do it's logic. It then needs to indicate the change so the list updates
s
and what about initial state?
before the button is pressed
b
set it on the row when you bind it
if you need to get data from 2 sources before binding that's okay
then in the onBind you'd just check against the favorite list and set it enabled/disabled
s
I don't have the favorite list. To know if an item is in that list I need to get this information from Room. Do you think I need to have a copy of the favorite list in memory?
b
I would probably do that ya
s
what if it has a lot of items?
b
Favourites or rows from the server. Whats a realistic expectation here for favorited items? Dozens, Hundreds, Thousands, Millions?
s
Favorites. Let's assume that thousands
b
Is the view model fetching the data for the paged adapter?
s
Yes
You're thinking about combining two requests?
So I will get a list with
isFavorited
info
b
Ya... get the rows, and then before you tell the adapter you have results, query room for each row
add a field to the viewholder with isFavorited
and if it doesn't have the ID of the record, add the ID as well
then you can just pass the ID around and avoid passing positions
I'm just wondering if precaching the favourites is a good or bad idea at that quantity vs how many hits to room
can see how much it slows down the update querying for each ID seperately
s
I like the approach with getting
isFavorited
before the list is passed to the adapter. In that case I don't need to have in memory list of favorites
b
no you wouldn't, but I'm just wondering about performance... every time the list gets updated you'd have to query each individual row for it's status. If it was precached, you'd only do it once, and never again while on that page... but then you'd have to update it if they toggled it
either way would work though
Ya, thinking more it's probably better not in memory and just get it each time
s
I think It can be achieved with only one query to Room.
SELECT * FROM MyTable WHERE id IN (1, 2, 3, 4)
where 1, 2, 3, 4 are ids
but after that I will need to map the result
@Ben Abramovitch thank you for your help
b
Ya that would be a good way too. No problem!
c
I agree that tracking adapter position isn't great - it's leaking UI impl details, and they're not guaranteed to be stable - if something is inserted at the top of the list, all the positions change. I'd suggest to first embed the favorite flag in the items coming out of your repository, so that each item will know if it's favorites by default. You can keep it only in the favorite table and reference both tables in your query, or store it additionally in the item table you're querying. Then when an item is favorited, tell the viewmodel the id and favorite status to update the DB asynchronously, and update the local item in the adapter at the same time. Your async message to the viewmodel will take a while to write and readback, but that's ok because you're immediately overriding the state in the adapter. You dont want to override the data in the PagedList though, because paging expects data to be immutable (so it can diff on a background thread). So what you do instead is have a map<> of local overrides, item ID -> favorite state, which you check in onbind. If the id is present in the map, the value overrides the favorite value you get out of your adapter's getItem. The adapter map based on unique id is a common way to do selection, and this is mostly just that.
s
@Chris Craik thank you for your answer
z
Why do you need to call
adapter.notifyItemChange∂
at all? You should be able to handle this just by writing to DB, and then
LivePagedListBuilder
would emit a new PagedList, and PagedListAdapter would handle the diff
s
I am getting that PagedList not from DB