Я следовал этой документации: Преобразования | Android-разработчики
Идея состоит в том, что при изменении значения currentList данные activeTasks, completedTasks и importantTasks будут автоматически обновляться, а затем наблюдатели будут обновлять пользовательский интерфейс.
Проблема, с которой я сталкиваюсь, заключается в том, что при изменении значения currentList карты switchMaps, которые я назначил LiveData, вообще не запускаются.
Я пытался найти решение, но я точно не знаю, как погуглить об этой проблеме.
вот моя 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}")
}
}
вот как я могу изменить значение currentList:
override fun onItemClicked(id: Int) {
viewModel.setCurrentList(id)
dismiss()
}
ОБНОВЛЯТЬ
Я попробовал это в новом проекте, чтобы проверить, была ли проблема во мне, но он отлично работает.
модель просмотра:
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)
}
}
как я изменил значение 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
}
}
}
Я использую RoomDatabase для первого проекта, возможно, это причина моей проблемы (не уверен в этом), потому что во втором я использую определенный набор данных, и он работает нормально.
Я помещаю наблюдателя во фрагмент для каждого, switchMap срабатывает при инициализации currentList, но больше не срабатывает, когда я изменяю значение currentList.
Проблема здесь в том, что вы определяете importantTasks(), activeTasks() и completedTasks() как функции, а не LiveData свойства. Функция switchMap вызывается только один раз, когда вы впервые вызываете эти функции, а затем она не вызывается снова, потому что функции больше не вызываются.
Вместо функций вы должны определить importantTasks, activeTasks и completedTasks как LiveData свойства в вашей ViewModel. Вот как это сделать:
class AppViewModel(application: Application) : AndroidViewModel(application) {
// ...
val importantTasks: LiveData<List<Task>> = currentList.switchMap { id ->
Log.d(TAG, "importantTasks: updated")
dataSource.getImportant(id)
}
val activeTasks: LiveData<List<Task>> = currentList.switchMap { id ->
Log.d(TAG, "activeTasks: updated")
dataSource.getActiveTasks(id)
}
val completedTasks: LiveData<List<Task>> = currentList.switchMap { id ->
Log.d(TAG, "completedTasks: updated")
dataSource.getCompletedTasks(id)
}
// ...
}
Теперь каждый раз, когда currentList изменяется, importantTasks, activeTasks и completedTasks будут автоматически обновляться, потому что они являются LiveData свойствами, которые наблюдают currentList через функцию switchMap.
Хотя это правда, что они должны изменить свой код на то, что есть у вас, чтобы предотвратить возможность возникновения этой проблемы после воссоздания действия/фрагмента, это, вероятно, не единственная проблема, потому что они не упомянули проблему, возникающую только после поворота экрана. Если функция вызывается один раз, а затем наблюдаются возвращенные оперативные данные функции, они будут обновляться по мере изменения исходных оперативных данных, а не только при повторном вызове функции. Проблема возникает, когда функция вызывается снова и наблюдается новый экземпляр.
я переключаю эти живые данные с функций на свойства, как вы сказали, но проблема остается прежней. Я застрял с этой проблемой уже 2 дня, это мой первый раз, когда я имею дело с viewmodel + livedata, поэтому у меня мало знаний. может быть, вы хотите взглянуть на мой проект: Todo. Спасибо за помощь!
Я понял!
Проблема вызвана определением 2 разных экземпляров моделей представления в 2 разных местах (MainActivity и фрагмент внутри ViewPager2 в этом случае) Как я определяю модели просмотра:
val viewModel: AppViewModel by viewModels()
и я думал, что они используют один и тот же context, и когда я изменю значение в MainActivity, фрагмент заметит это, но нет. Несмотря на то, что фрагмент находится внутри действия, они использовали другой контекст (я думаю). Вот как я решил проблему:
val viewModel: AppViewModel by viewModels()
lateinit var viewModel: AppViewModel
override fun onCreateView(): View {
viewModel = ViewModelProvider(requireActivity()).get(AppViewModel::class.java)
}
Хорошего дня!!
Вы должны
observeLiveData, возвращенныеswitchMap, чтобы он действительно что-то делал. Где вы наблюдаете каждую из этих LiveData?