从不同的 LiveData 源填充适配器
Filling an adapter from different LiveData sources
我正在玩 LiveData,想了解它的功能。
我想通过开关(如果你愿意,可以使用过滤器)用来自不同来源的数据填充我的 RecyclerView。
过滤适配器内的值不是一个选项。
所以,我决定在我的视图模型中使用 MediatorLiveData。
道:
@Query("SELECT * FROM tasks WHERE completed = 0")
LiveData<List<Task>> getActiveTasksLiveData();
@Query("SELECT * FROM tasks")
LiveData<List<Task>> getAllTasksLiveData();
@Query("SELECT * FROM tasks WHERE completed = 1")
LiveData<List<Task>> getClosedTasksLiveData();
回购:
public LiveData<List<Task>> getActiveTasks() {
return mTaskDao.getActiveTasksLiveData();
}
public LiveData<List<Task>> getAllTasks() {
return mTaskDao.getAllTasksLiveData();
}
public LiveData<List<Task>> getClosedTasks() {
return mTaskDao.getClosedTasksLiveData();
}
视图模型
public class MainViewModel extends AndroidViewModel {
private final String TAG = "MainViewModel";
private final AppDataRepository mData;
private MediatorLiveData<List<Task>> mMediatorTasks;
public MainViewModel(@NonNull Application application) {
super(application);
mData = AppDataInjector.getDataRepository(application.getApplicationContext());
mMediatorTasks = new MediatorLiveData<>();
mMediatorTasks.setValue(null);
}
public LiveData<List<Task>> getTasks(){
return mMediatorTasks;
}
public void changeTasksOption(int index){
mMediatorTasks.removeSource(mData.getAllTasks());
mMediatorTasks.removeSource(mData.getActiveTasks());
mMediatorTasks.removeSource(mData.getClosedTasks());
if (index == R.id.navigation_all){
Log.i(TAG, "Add source: all");
mMediatorTasks.addSource(mData.getAllTasks(), new Observer<List<Task>>() {
@Override
public void onChanged(List<Task> tasks) {
Log.i(TAG, "Add source: all - setValue");
mMediatorTasks.setValue(tasks);
}
});
} else if (index == R.id.navigation_closed){
Log.i(TAG, "Add source closed");
mMediatorTasks.addSource(mData.getClosedTasks(), new Observer<List<Task>>() {
@Override
public void onChanged(List<Task> tasks) {
Log.i(TAG, "Add source: closed - setValue");
mMediatorTasks.setValue(tasks);
}
});
} else {
Log.i(TAG, "Add source active");
mMediatorTasks.addSource(mData.getActiveTasks(), new Observer<List<Task>>() {
@Override
public void onChanged(List<Task> tasks) {
Log.i(TAG, "Add source: active - setValue");
mMediatorTasks.setValue(tasks);
}
});
}
}
}
片段
public View onCreateView(@NonNull LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_main, container, false);
mNavigationView = view.findViewById(R.id.navigation);
mFab = view.findViewById(R.id.fabMain);
mRecyclerView = view.findViewById(R.id.mainRecyclerView);
tasksAdapterLive = new TasksAdapterLive(mAdapterCallback);
RecyclerView.LayoutManager manager = new GridLayoutManager(getContext(), 1);
mRecyclerView.setLayoutManager(manager);
mRecyclerView.setAdapter(tasksAdapterLive);
// set up bottom navigation listener
mNavigationView.setOnNavigationItemSelectedListener(item -> {
mViewModel.changeTasksOption(item.getItemId());
return true;
});
return view;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mViewModel = ViewModelProviders.of(this).get(MainViewModel.class);
mViewModel.getTasks().observe(this, tasks -> {
if (tasks != null) {
tasksAdapterLive.setTasks(tasks);
tasksAdapterLive.notifyDataSetChanged();
}
});
mViewModel.changeTasksOption(mNavigationView.getSelectedItemId());
}
如您所见,我决定在我的视图模型中使用 MediatorLiveData。
我的主要目标 - 在从片段调用 changeTasksOption() 时更改适配器内的数据。
我使用 removeSource()
,因为我的理解是从观察中删除了 LiveData
来源。
但是,就我而言,它没有。
当我启动应用程序时,日志是:
MainViewModel: Add source active
MainViewModel: Add source: active - setValue
当我尝试切换到另一个来源时 - 日志是
MainViewModel: Add source: all
MainViewModel: Add source: all - setValue
MainViewModel: Add source: active - setValue
MainViewModel: Add source: all - setValue
MainViewModel: Add source: active - setValue
*** repeats about 100 times
RecyclerView 正在闪烁
所以,我请教一下。
我究竟做错了什么?
我误解了文档吗?
removeSourse() 的真正作用是什么?
因为在我的情况下它不会删除源。
如果我的实现方法是错误的,你建议我怎么做?
谢谢!
已编辑:
经过几个小时的试验,我找到了解决方案。是的,这很糟糕(或者可能不是?)。但是显然这不是通用的,因为我们没有使用Romm + LiveData
创建 return List
的普通 Room 函数
@Query("SELECT * FROM tasks WHERE completed = 0")
List<Task> getActiveTasks();
@Query("SELECT * FROM tasks")
List<Task> getAllTasks();
@Query("SELECT * FROM tasks WHERE completed = 1")
List<Task> getClosedTasks();
在 repo 中创建了 MutableLiveData
private MutableLiveData<List<Task>> mTasksTestActive, mTasksTestAll, mTasksTestClosed;
将这些函数添加到 repo
public LiveData<List<Task>> getActiveTasksTest() {
Executors.newSingleThreadExecutor().execute(() -> {
List<Task> taskList = mTaskDao.getActiveTasks();
mTasksTestActive.postValue(taskList);
});
return mTasksTestActive;
}
public LiveData<List<Task>> getAllTasksTest() {
Executors.newSingleThreadExecutor().execute(() -> {
List<Task> taskList = mTaskDao.getAllTasks();
mTasksTestAll.postValue(taskList);
});
return mTasksTestAll;
}
public LiveData<List<Task>> getClosedTasksTest() {
Executors.newSingleThreadExecutor().execute(() -> {
List<Task> taskList = mTaskDao.getClosedTasks();
mTasksTestClosed.postValue(taskList);
});
return mTasksTestClosed;
}
视图模型更改:
public void changeTasksOption(int index) {
mMediatorTasks.removeSource(mData.getAllTasksTest());
mMediatorTasks.removeSource(mData.getActiveTasksTest());
mMediatorTasks.removeSource(mData.getClosedTasksTest());
if (index == R.id.navigation_all) {
Log.i(TAG, "Add source: all");
mMediatorTasks.addSource(mData.getAllTasksTest(), tasks -> {
Log.i(TAG, "Add source: all - postValue");
mMediatorTasks.postValue(tasks);
});
} else if (index == R.id.navigation_closed) {
Log.i(TAG, "Add source closed");
mMediatorTasks.addSource(mData.getClosedTasksTest(), tasks -> {
Log.i(TAG, "Add source: closed - postValue");
mMediatorTasks.postValue(tasks);
});
} else {
Log.i(TAG, "Add source active");
mMediatorTasks.addSource(mData.getActiveTasksTest(), tasks -> {
Log.i(TAG, "Add source: active - postValue");
mMediatorTasks.postValue(tasks);
});
}
}
现在,通过切换 UI,我得到了结果。没有更多的循环,一切似乎都正常。
但还是!这是一个糟糕的解决方案。也许房间有问题?
您在之前的回购代码中同步从回购中返回值 -
public LiveData<List<Task>> getActiveTasks() {
return mTaskDao.getActiveTasksLiveData();
}
public LiveData<List<Task>> getAllTasks() {
return mTaskDao.getAllTasksLiveData();
}
public LiveData<List<Task>> getClosedTasks() {
return mTaskDao.getClosedTasksLiveData();
}
因此,当您调用 removeSource(mData.getAllTasksTest())
时,它会同步从存储库中获取数据,这就是您从所有存储库接收数据的原因。
在您编辑的代码中,您正在使用工作线程来获取数据,这意味着您的源实时数据在回购 returns 任何值之前从中介实时数据中删除。
public void changeTasksOption(int index){
mMediatorTasks.removeSource(mData.getAllTasks());
mMediatorTasks.removeSource(mData.getActiveTasks());
mMediatorTasks.removeSource(mData.getClosedTasks());
不,这不应该是这样的!
selected 选项应该在 LiveData 中。然后,您可以针对该 LiveData 使用 Transformations.switchMap {
来 select 正确的 LiveData<List<Task>>
.
private MutableLiveData<Integer> mSelectedIndex = new MutableLiveData<>();
private final LiveData<List<Task>> mMediatorTasks = Transformations.switchMap(mSelectedIndex, (index) -> {
if (index == R.id.navigation_all) {
return mData.getAllTasksTest();
} else if (index == R.id.navigation_closed) {
return mData.getClosedTasksTest();
} else {
return mData.getActiveTasksTest();
}
});
public void changeTasksOption(int index) {
mSelectedIndex.setValue(index);
}
public LiveData<List<Task>> getTasks(){
return mMediatorTasks;
}
此外,您应该再次将您的 mData.get*()
方法从 DAO 带到 return LiveData<List<Task>>
,这是一个更好的解决方案。
我正在玩 LiveData,想了解它的功能。 我想通过开关(如果你愿意,可以使用过滤器)用来自不同来源的数据填充我的 RecyclerView。
过滤适配器内的值不是一个选项。 所以,我决定在我的视图模型中使用 MediatorLiveData。
道:
@Query("SELECT * FROM tasks WHERE completed = 0")
LiveData<List<Task>> getActiveTasksLiveData();
@Query("SELECT * FROM tasks")
LiveData<List<Task>> getAllTasksLiveData();
@Query("SELECT * FROM tasks WHERE completed = 1")
LiveData<List<Task>> getClosedTasksLiveData();
回购:
public LiveData<List<Task>> getActiveTasks() {
return mTaskDao.getActiveTasksLiveData();
}
public LiveData<List<Task>> getAllTasks() {
return mTaskDao.getAllTasksLiveData();
}
public LiveData<List<Task>> getClosedTasks() {
return mTaskDao.getClosedTasksLiveData();
}
视图模型
public class MainViewModel extends AndroidViewModel {
private final String TAG = "MainViewModel";
private final AppDataRepository mData;
private MediatorLiveData<List<Task>> mMediatorTasks;
public MainViewModel(@NonNull Application application) {
super(application);
mData = AppDataInjector.getDataRepository(application.getApplicationContext());
mMediatorTasks = new MediatorLiveData<>();
mMediatorTasks.setValue(null);
}
public LiveData<List<Task>> getTasks(){
return mMediatorTasks;
}
public void changeTasksOption(int index){
mMediatorTasks.removeSource(mData.getAllTasks());
mMediatorTasks.removeSource(mData.getActiveTasks());
mMediatorTasks.removeSource(mData.getClosedTasks());
if (index == R.id.navigation_all){
Log.i(TAG, "Add source: all");
mMediatorTasks.addSource(mData.getAllTasks(), new Observer<List<Task>>() {
@Override
public void onChanged(List<Task> tasks) {
Log.i(TAG, "Add source: all - setValue");
mMediatorTasks.setValue(tasks);
}
});
} else if (index == R.id.navigation_closed){
Log.i(TAG, "Add source closed");
mMediatorTasks.addSource(mData.getClosedTasks(), new Observer<List<Task>>() {
@Override
public void onChanged(List<Task> tasks) {
Log.i(TAG, "Add source: closed - setValue");
mMediatorTasks.setValue(tasks);
}
});
} else {
Log.i(TAG, "Add source active");
mMediatorTasks.addSource(mData.getActiveTasks(), new Observer<List<Task>>() {
@Override
public void onChanged(List<Task> tasks) {
Log.i(TAG, "Add source: active - setValue");
mMediatorTasks.setValue(tasks);
}
});
}
}
}
片段
public View onCreateView(@NonNull LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_main, container, false);
mNavigationView = view.findViewById(R.id.navigation);
mFab = view.findViewById(R.id.fabMain);
mRecyclerView = view.findViewById(R.id.mainRecyclerView);
tasksAdapterLive = new TasksAdapterLive(mAdapterCallback);
RecyclerView.LayoutManager manager = new GridLayoutManager(getContext(), 1);
mRecyclerView.setLayoutManager(manager);
mRecyclerView.setAdapter(tasksAdapterLive);
// set up bottom navigation listener
mNavigationView.setOnNavigationItemSelectedListener(item -> {
mViewModel.changeTasksOption(item.getItemId());
return true;
});
return view;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mViewModel = ViewModelProviders.of(this).get(MainViewModel.class);
mViewModel.getTasks().observe(this, tasks -> {
if (tasks != null) {
tasksAdapterLive.setTasks(tasks);
tasksAdapterLive.notifyDataSetChanged();
}
});
mViewModel.changeTasksOption(mNavigationView.getSelectedItemId());
}
如您所见,我决定在我的视图模型中使用 MediatorLiveData。 我的主要目标 - 在从片段调用 changeTasksOption() 时更改适配器内的数据。
我使用 removeSource()
,因为我的理解是从观察中删除了 LiveData
来源。
但是,就我而言,它没有。
当我启动应用程序时,日志是:
MainViewModel: Add source active
MainViewModel: Add source: active - setValue
当我尝试切换到另一个来源时 - 日志是
MainViewModel: Add source: all
MainViewModel: Add source: all - setValue
MainViewModel: Add source: active - setValue
MainViewModel: Add source: all - setValue
MainViewModel: Add source: active - setValue
*** repeats about 100 times
RecyclerView 正在闪烁
所以,我请教一下。 我究竟做错了什么? 我误解了文档吗? removeSourse() 的真正作用是什么? 因为在我的情况下它不会删除源。
如果我的实现方法是错误的,你建议我怎么做?
谢谢!
已编辑:
经过几个小时的试验,我找到了解决方案。是的,这很糟糕(或者可能不是?)。但是显然这不是通用的,因为我们没有使用Romm + LiveData
创建 return List
的普通 Room 函数@Query("SELECT * FROM tasks WHERE completed = 0")
List<Task> getActiveTasks();
@Query("SELECT * FROM tasks")
List<Task> getAllTasks();
@Query("SELECT * FROM tasks WHERE completed = 1")
List<Task> getClosedTasks();
在 repo 中创建了 MutableLiveData
private MutableLiveData<List<Task>> mTasksTestActive, mTasksTestAll, mTasksTestClosed;
将这些函数添加到 repo
public LiveData<List<Task>> getActiveTasksTest() {
Executors.newSingleThreadExecutor().execute(() -> {
List<Task> taskList = mTaskDao.getActiveTasks();
mTasksTestActive.postValue(taskList);
});
return mTasksTestActive;
}
public LiveData<List<Task>> getAllTasksTest() {
Executors.newSingleThreadExecutor().execute(() -> {
List<Task> taskList = mTaskDao.getAllTasks();
mTasksTestAll.postValue(taskList);
});
return mTasksTestAll;
}
public LiveData<List<Task>> getClosedTasksTest() {
Executors.newSingleThreadExecutor().execute(() -> {
List<Task> taskList = mTaskDao.getClosedTasks();
mTasksTestClosed.postValue(taskList);
});
return mTasksTestClosed;
}
视图模型更改:
public void changeTasksOption(int index) {
mMediatorTasks.removeSource(mData.getAllTasksTest());
mMediatorTasks.removeSource(mData.getActiveTasksTest());
mMediatorTasks.removeSource(mData.getClosedTasksTest());
if (index == R.id.navigation_all) {
Log.i(TAG, "Add source: all");
mMediatorTasks.addSource(mData.getAllTasksTest(), tasks -> {
Log.i(TAG, "Add source: all - postValue");
mMediatorTasks.postValue(tasks);
});
} else if (index == R.id.navigation_closed) {
Log.i(TAG, "Add source closed");
mMediatorTasks.addSource(mData.getClosedTasksTest(), tasks -> {
Log.i(TAG, "Add source: closed - postValue");
mMediatorTasks.postValue(tasks);
});
} else {
Log.i(TAG, "Add source active");
mMediatorTasks.addSource(mData.getActiveTasksTest(), tasks -> {
Log.i(TAG, "Add source: active - postValue");
mMediatorTasks.postValue(tasks);
});
}
}
现在,通过切换 UI,我得到了结果。没有更多的循环,一切似乎都正常。
但还是!这是一个糟糕的解决方案。也许房间有问题?
您在之前的回购代码中同步从回购中返回值 -
public LiveData<List<Task>> getActiveTasks() {
return mTaskDao.getActiveTasksLiveData();
}
public LiveData<List<Task>> getAllTasks() {
return mTaskDao.getAllTasksLiveData();
}
public LiveData<List<Task>> getClosedTasks() {
return mTaskDao.getClosedTasksLiveData();
}
因此,当您调用 removeSource(mData.getAllTasksTest())
时,它会同步从存储库中获取数据,这就是您从所有存储库接收数据的原因。
在您编辑的代码中,您正在使用工作线程来获取数据,这意味着您的源实时数据在回购 returns 任何值之前从中介实时数据中删除。
public void changeTasksOption(int index){ mMediatorTasks.removeSource(mData.getAllTasks()); mMediatorTasks.removeSource(mData.getActiveTasks()); mMediatorTasks.removeSource(mData.getClosedTasks());
不,这不应该是这样的!
selected 选项应该在 LiveData 中。然后,您可以针对该 LiveData 使用 Transformations.switchMap {
来 select 正确的 LiveData<List<Task>>
.
private MutableLiveData<Integer> mSelectedIndex = new MutableLiveData<>();
private final LiveData<List<Task>> mMediatorTasks = Transformations.switchMap(mSelectedIndex, (index) -> {
if (index == R.id.navigation_all) {
return mData.getAllTasksTest();
} else if (index == R.id.navigation_closed) {
return mData.getClosedTasksTest();
} else {
return mData.getActiveTasksTest();
}
});
public void changeTasksOption(int index) {
mSelectedIndex.setValue(index);
}
public LiveData<List<Task>> getTasks(){
return mMediatorTasks;
}
此外,您应该再次将您的 mData.get*()
方法从 DAO 带到 return LiveData<List<Task>>
,这是一个更好的解决方案。