I am writing a simple to-do list style app for android and I am having problems with adding sorting functionality.
I have a screen with a RecyclerView
showing all lists that belong to the user, and I would like to add a functionality which allows the user to turn sorting on or off. The problem is that I cant seem to find a way to make the list displayed to the user stay consistently sorted after another change is made.
Lets say the lists are held in a Room
database.
The easiest and most consistent way to achieve what I want is to sort the data in the database itself, but this is probably not the way to go as it would be complicated to implement (if even possible).
Most likely I should sort the data locally in my view model. I figured I could do this using MediatorLiveData
which will observe the database while is itself observed by the host activity. Before updating the MediatorLiveData
, I would sort the list using a flag isSorting
to either return the data as-is or sort it before delivering the result.
The key to my question is that I want to be able to turn this functionality on and off, while still maintaining the order of the data afterwards (when another operation is performed). Here is an example of a typical events flow:
Before I get to the problem part, here is some code:
Repository (shortened for brevity):
class ListsRepository {
val db = Database().getInstance();
fun getAllUserLists(userId: Int): LiveData<List<UserList>>
}
Activity (shortened for brevity):
class UserListsActivity: Activity() {
val viewModel: UserListsViewModel
val adapter: UserListAdapter
fun onCreate(savedInstanceState: Bundle?) {
viewModel.allLists.observe(this, Observer { newData ->
adapter.setData(newData)
adapter.notifyDataSetChanged()
)
}
}
Viewmodel (shortened for brevity)
class UserListsViewModel(val userId: Int, val repository: ListsRepository ): ViewModel() {
val allLists = MediatorLiveData<List<UserList>>()
val isSorting = false
init {
allLists.addSource(repository.getAllUserLists(userId)) { userLists ->
val result = userLists
if(isSorting) {
result = result.sortedBy { it.name }
}
value = result
}
}
}
Now lets get to the actual issue:
As long as the sorting feature is turned on, everything is fine and the user will see "list 1", "list 2", "list 3", "list 4".
But now, lets say the user turns the sorting feature off, and adds another list, "list 0"
What happens now is:
MediatorLiveData
receives the update from the database as "list 3","list 1","list 2","list 4","list 0" (the original order of the database, plus the new lists)isSorting
flag is off, so it sets the value to the returned data as-isDesired result: "list 1","list 2","list 3","list 4","list 0" (unsorted, but previous order is kept)
Actual result: "list 3","list 1","list 2","list 4","list 0" (the previous sorting of lists 1,2,3 is now lost).
I hope I made myself clear. Is this possible to achieve without adding too much complexity? What is the "standard way" to provide sorting functionality in an android app?
Thanks
here is another problem i ran into, and the solution i decided to go with (hope this will help others):
first of all, i went with the suggested method of changing the sorting order as soon as the user clicks the "sort" button. meaning instead of thinking about it in terms of "now its sorting, now its not", i now think of it as "data is always sorted by something (even when its 'unsorted')".
i wanted the "unsorted" list (default sorting) to be the same order as the items appear in the database, however my ViewModel
was setup in such a way that i couldn't get that original database items directly.
for the sake of brevity, i have something like this:
//inside view model class
val allLists : LiveData<List<UserList>> = repository.getAllLists()
so when i wanted to use the default sorting i thought of 2 options:
getAllLists()
to itself, thus triggering the observer - but that didn't seem like a nice solutionViewModel
which is a copy of the one returned by the database, thus it would always be ordered the way the database retrieves it (and the "default sorting" would have the value of that list) - but thats a waste of memory and also extra complexity (not much more, but still)what i ended up doing is adding a field created
to my item which is simply set to System.currentTimeMillis()
whenever an instance of my item is created and use that as my "default sorting" - very minimal code addition (not even half a line), and added no complexity at all.