I followed this documentation: Transformations | Android Developers
The idea is that when the value of currentList
changes, the data of activeTasks
, completedTasks
and importantTasks
will be automatically updated and then the observers will update the UI.
The problem I'm having is that when the value of currentList
changes, the switchMaps that I have assigned to the LiveData are not triggered at all.
I tried to find the solution but i don't know exactly how to google about this problem.
here's my ViewModel:
class AppViewModel(application: Application) : AndroidViewModel(application) {
companion object {
const val TAG: String = "AppViewModel"
}
private val dataSource: TaskDao
val lists: LiveData<List<TaskList>>
var lastAction: Int = 1
val currentList = MutableLiveData(1)
var currentIndex: Int = -1
set(value) {
field = value
Log.d(TAG, "currentIndex setter: index = $field")
}
init {
val db = AppDatabase.getInstance(application)
dataSource = db.taskDao()
lists = dataSource.getAllLists()
}
fun importantTasks(): LiveData<List<Task>> = currentList.switchMap {
// this line for tracking changes
// and this how i know it not getting triggered
Log.d(TAG, "importantTasks: updated")
dataSource.getImportant(it)
}
fun activeTasks(): LiveData<List<Task>> = currentList.switchMap {
Log.d(TAG, "activeTasks: updated")
dataSource.getActiveTasks(it)
}
fun completedTasks(): LiveData<List<Task>> = currentList.switchMap {
Log.d(TAG, "completedTasks: updated")
dataSource.getCompletedTasks(it)
}
fun setCurrentList(id: Int){
currentList.value = id
}
fun upsert(task: Task) = viewModelScope.launch(Dispatchers.IO) {
task.listId = currentList.value!!
dataSource.upsert(task)
Log.d(TAG, "upsert: task upserted at ${task.listId}")
}
fun delete(task: Task) = viewModelScope.launch(Dispatchers.IO) {
task.listId = currentList.value!!
dataSource.delete(task)
Log.d(TAG, "delete: task deleted at ${task.listId}")
}
fun upsertList(list: TaskList) = viewModelScope.launch(Dispatchers.IO) {
dataSource.upsertList(list)
Log.d(TAG, "upsertList: updated: id = ${list.id} , name = ${list.name}")
}
}
here's how i change the value of currentList
:
override fun onItemClicked(id: Int) {
viewModel.setCurrentList(id)
dismiss()
}
UPDATE
I tried this on a new project to test if the problem was me but it works perfectly fine.
the viewmodel:
class AppViewModel : ViewModel() {
val listId = MutableLiveData(0)
fun getUsers(): LiveData<List<User>> = listId.switchMap {
Log.d("AppViewModel", "getUsers: updated")
data(it)
}
fun data(id: Int): LiveData<List<User>> {
val users = if (id == 0) List(30) { i -> User(i, "User$i", "password$i") }
else List(25) { i -> User(i, "User$i", "password$i") }
return MutableLiveData(users)
}
}
how i changed the value of listId
:
class MainActivity : ComponentActivity() {
val viewModel: AppViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val text = findViewById<TextView>(R.id.textCounter)
val button = findViewById<Button>(R.id.buttonSwitch)
viewModel.getUsers().observeForever{
Log.d("MainActivity", "onCreate: observer triggered")
text.text = "${it.size}"
}
button.setOnClickListener {
viewModel.listId.value = if (viewModel.listId.value == 1) 0 else 1
}
}
}
I'm using RoomDatabase for the first project, maybe that the cause of my problem(not sure about that) because in the second one I use a specific data set and it works ok.
I figured it out!
The problem cause by define 2 different instance of viewmodels in 2 different places (MainActivity and a fragment inside ViewPager2 in this case) How i define the viewmodels:
val viewModel: AppViewModel by viewModels()
and i thought that they used the same context
and when i change the value in MainActivity, the fragment will notice that but no. Even though the fragment is inside the Activity, they used different context(i think). Here how i solved the problem:
val viewModel: AppViewModel by viewModels()
lateinit var viewModel: AppViewModel
override fun onCreateView(): View {
viewModel = ViewModelProvider(requireActivity()).get(AppViewModel::class.java)
}
Have a nice day!!