Livedata 没有正确更新片段中的数据
Livedata doesn't update data in fragment correctly
我在不同的选项卡中有 tablayout
和 2 个 fragments
。
片段 A 有一个覆盖方法,当 Activity(从片段 A 开始)return 上的数据被销毁时 returns 数据:
public class Fragment A extends Fragment {
...
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
if(resultCode != RESULT_CANCELED) {
assert data != null;
String accountTransaction = data.getStringExtra("Account");
String categoryTransaction = data.getStringExtra("Category");
Double getDouble = data.getDoubleExtra("Value", 0);
TransactionNewItem item = new TransactionNewItem(String.valueOf(getDouble),accountTransaction,categoryTransaction);
model.setSelected(item);
}
super.onActivityResult(requestCode, resultCode, data);
}
}
在同一个方法中,我调用了应该观察 TransactionNewItem 对象的 ViewModel:
public class TransactionViewModel extends ViewModel {
private final MutableLiveData<TransactionNewItem> selected = new MutableLiveData<>();
public void setSelected (TransactionNewItem item){
selected.setValue(item);
}
public LiveData<TransactionNewItem> getSelected() {
return selected;
}
}
在来自 Activity 的 return 的数据之后,它使用新值创建一个新的 POJO 并将存储在该 POJO 中的数据发送到 Fragment B,其中基于来自 Fragment A 的新项目的数据将创建 RecyclerView
public class Fragment B extends Fragment {
...
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
initObserve();
initRecView();
super.onViewCreated(view, savedInstanceState);
}
//init RecyclerView
private void initRecView(){
binding.transactionView.setLayoutManager(new LinearLayoutManager(requireContext()));
adapter = new TransactionRecViewAdapter(listContentArr);
adapter.setListContent(listContentArr);
binding.transactionView.setAdapter(adapter);
}
//observe data from Fragment A and create object based on it
private void initObserve(){
model = new ViewModelProvider(requireActivity()).get(TransactionViewModel.class);
model.getSelected().observe(getViewLifecycleOwner(), item -> {
TransactionItem newAccountItem = new TransactionItem() ;
newAccountItem.setTransactionValue(item.getTransactionValue());
newAccountItem.setTransactionCategory(item.getTransactionCategory());
newAccountItem.setTransactionAccount(item.getTransactionAccount());
listContentArr.add(0,newAccountItem);
adapter.notifyDataSetChanged();
});
}
}
但是,它只会将 1 个项目添加到 RecyclerView 中,并将在 Activity returns 新数据时替换它。如果用户至少有一次没有切换到 Fragment B,就会发生这种情况,因为直到用户切换到 Fragment B 才会调用 onViewCreated。
如果用户之前从未切换到 Fragment B,如何让 ViewModel 观察来自 Fragment A 的数据,并在每次 Activity returns 新数据时在 Fragment B Recyclerview 中创建新的 TransActionItem?
提前致谢
编辑:我设法用下一种方式做我想做的事:
第 1 步。我将 ViewModel 从 POJO 更改为 Arraylist with POJO - ArrayList:
public class TransactionViewModel extends ViewModel {
private final MutableLiveData<ArrayList<TransactionItem>> selected = new MutableLiveData<>();
public void setSelected (ArrayList<TransactionItem> arrayList){
selected.setValue(arrayList);
}
public LiveData<ArrayList<TransactionItem>> getSelected() {
return selected;
}
}
第 2 步。在 Fragment A 中,我在 onActivityResult 中添加了具有相同 POJO 类型的 ArrayList。我更改了代码,现在将在 Activity 之后创建和添加对象,结果将 return 不在片段 B:
中
public class Fragment A extends Fragment {
ArrayList<TransactionItem> listTransactions = new ArrayList<>();
…
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
if(resultCode != RESULT_CANCELED) {
...
//Create TransactionItem and use setSelected method from ViewModel
TransactionItem item = new TransactionItem(accountTransaction,
String.valueOf(getDouble),categoryAccount),transactionID);
listTransactions.add(0,item);
model.setSelected(listTransactions);
}
super.onActivityResult(requestCode, resultCode, data);
}
}
必须注意到我将 transactionID 添加到 TransactionItem 构造函数中,这就是我们需要它的原因。
第 3 步我创建了下一个扩展 DiffUtil.Callback 的 TransactionDiffUtilCallback class :
public class TransactionDiffUtilCallback extends `DiffUtil.Callback` {
public TransactionDiffUtilCallback(ArrayList<TransactionItem> oldList, ArrayList<TransactionItem> newList) {
this.oldList = oldList;
this.newList = newList;
}
ArrayList<TransactionItem> newList;
@Override
public int getOldListSize() {
return oldList.size();
}
@Override
public int getNewListSize() {
return newList.size();
}
@Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
return oldList.get(oldItemPosition).getItemIID() == newList.get(newItemPosition).getItemIID();
}
@Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
return oldList.get(oldItemPosition).equals(newList.get(newItemPosition));
}
@Nullable
@Override
public Object getChangePayload(int oldItemPosition, int newItemPosition) {
return super.getChangePayload(oldItemPosition, newItemPosition);
}
}
我使用 POJO 中的 getItemIID()
来通知 ArrayList 中的新项目不同。
步骤 4 在 recyclerview 适配器中我创建了 updateItemList(list):
public void updateItemList(ArrayList<TransactionItem> items){
final TransactionDiffUtilCallback diffCallback = new TransactionDiffUtilCallback(this.pad_list, items);
final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(diffCallback);
this.pad_list.clear();
this.pad_list.addAll(items);
diffResult.dispatchUpdatesTo(this);
}
所以这个方法使用DiffUtil.CallBack比较Fragment A中的ArrayList和Fragment B中的ArrayList中的项目,然后通知adapter Fragment A中的ArrayList不同,这个数据应该放在Fragment B中的ArrayList中, 并且应该更新视图。
第 5 步在片段 B 中重写了 OnViewCreated() 代码以永远观察 Arraylist :
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
initRecView();
model = new ViewModelProvider(requireActivity()).get(TransactionViewModel.class);
Observer<ArrayList<TransactionItem>> observer = (Observer<ArrayList<TransactionItem>>) this::initObserve;
model.getSelected().observeForever(observer);
super.onViewCreated(view, savedInstanceState);
}
并且 initObserve() 现在有下一个代码:
private void initObserve(ArrayList<TransactionItem> list){
adapter.updateItemList(list);
}
目前这个方案是可行的,用户不需要切换到Fragment B来保持交易记录。我将继续测试此解决方案。
我在不同的选项卡中有 tablayout
和 2 个 fragments
。
片段 A 有一个覆盖方法,当 Activity(从片段 A 开始)return 上的数据被销毁时 returns 数据:
public class Fragment A extends Fragment {
...
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
if(resultCode != RESULT_CANCELED) {
assert data != null;
String accountTransaction = data.getStringExtra("Account");
String categoryTransaction = data.getStringExtra("Category");
Double getDouble = data.getDoubleExtra("Value", 0);
TransactionNewItem item = new TransactionNewItem(String.valueOf(getDouble),accountTransaction,categoryTransaction);
model.setSelected(item);
}
super.onActivityResult(requestCode, resultCode, data);
}
}
在同一个方法中,我调用了应该观察 TransactionNewItem 对象的 ViewModel:
public class TransactionViewModel extends ViewModel {
private final MutableLiveData<TransactionNewItem> selected = new MutableLiveData<>();
public void setSelected (TransactionNewItem item){
selected.setValue(item);
}
public LiveData<TransactionNewItem> getSelected() {
return selected;
}
}
在来自 Activity 的 return 的数据之后,它使用新值创建一个新的 POJO 并将存储在该 POJO 中的数据发送到 Fragment B,其中基于来自 Fragment A 的新项目的数据将创建 RecyclerView
public class Fragment B extends Fragment {
...
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
initObserve();
initRecView();
super.onViewCreated(view, savedInstanceState);
}
//init RecyclerView
private void initRecView(){
binding.transactionView.setLayoutManager(new LinearLayoutManager(requireContext()));
adapter = new TransactionRecViewAdapter(listContentArr);
adapter.setListContent(listContentArr);
binding.transactionView.setAdapter(adapter);
}
//observe data from Fragment A and create object based on it
private void initObserve(){
model = new ViewModelProvider(requireActivity()).get(TransactionViewModel.class);
model.getSelected().observe(getViewLifecycleOwner(), item -> {
TransactionItem newAccountItem = new TransactionItem() ;
newAccountItem.setTransactionValue(item.getTransactionValue());
newAccountItem.setTransactionCategory(item.getTransactionCategory());
newAccountItem.setTransactionAccount(item.getTransactionAccount());
listContentArr.add(0,newAccountItem);
adapter.notifyDataSetChanged();
});
}
}
但是,它只会将 1 个项目添加到 RecyclerView 中,并将在 Activity returns 新数据时替换它。如果用户至少有一次没有切换到 Fragment B,就会发生这种情况,因为直到用户切换到 Fragment B 才会调用 onViewCreated。
如果用户之前从未切换到 Fragment B,如何让 ViewModel 观察来自 Fragment A 的数据,并在每次 Activity returns 新数据时在 Fragment B Recyclerview 中创建新的 TransActionItem?
提前致谢
编辑:我设法用下一种方式做我想做的事:
第 1 步。我将 ViewModel 从 POJO 更改为 Arraylist with POJO - ArrayList:
public class TransactionViewModel extends ViewModel {
private final MutableLiveData<ArrayList<TransactionItem>> selected = new MutableLiveData<>();
public void setSelected (ArrayList<TransactionItem> arrayList){
selected.setValue(arrayList);
}
public LiveData<ArrayList<TransactionItem>> getSelected() {
return selected;
}
}
第 2 步。在 Fragment A 中,我在 onActivityResult 中添加了具有相同 POJO 类型的 ArrayList。我更改了代码,现在将在 Activity 之后创建和添加对象,结果将 return 不在片段 B:
中public class Fragment A extends Fragment {
ArrayList<TransactionItem> listTransactions = new ArrayList<>();
…
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
if(resultCode != RESULT_CANCELED) {
...
//Create TransactionItem and use setSelected method from ViewModel
TransactionItem item = new TransactionItem(accountTransaction,
String.valueOf(getDouble),categoryAccount),transactionID);
listTransactions.add(0,item);
model.setSelected(listTransactions);
}
super.onActivityResult(requestCode, resultCode, data);
}
}
必须注意到我将 transactionID 添加到 TransactionItem 构造函数中,这就是我们需要它的原因。
第 3 步我创建了下一个扩展 DiffUtil.Callback 的 TransactionDiffUtilCallback class :
public class TransactionDiffUtilCallback extends `DiffUtil.Callback` {
public TransactionDiffUtilCallback(ArrayList<TransactionItem> oldList, ArrayList<TransactionItem> newList) {
this.oldList = oldList;
this.newList = newList;
}
ArrayList<TransactionItem> newList;
@Override
public int getOldListSize() {
return oldList.size();
}
@Override
public int getNewListSize() {
return newList.size();
}
@Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
return oldList.get(oldItemPosition).getItemIID() == newList.get(newItemPosition).getItemIID();
}
@Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
return oldList.get(oldItemPosition).equals(newList.get(newItemPosition));
}
@Nullable
@Override
public Object getChangePayload(int oldItemPosition, int newItemPosition) {
return super.getChangePayload(oldItemPosition, newItemPosition);
}
}
我使用 POJO 中的 getItemIID()
来通知 ArrayList 中的新项目不同。
步骤 4 在 recyclerview 适配器中我创建了 updateItemList(list):
public void updateItemList(ArrayList<TransactionItem> items){
final TransactionDiffUtilCallback diffCallback = new TransactionDiffUtilCallback(this.pad_list, items);
final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(diffCallback);
this.pad_list.clear();
this.pad_list.addAll(items);
diffResult.dispatchUpdatesTo(this);
}
所以这个方法使用DiffUtil.CallBack比较Fragment A中的ArrayList和Fragment B中的ArrayList中的项目,然后通知adapter Fragment A中的ArrayList不同,这个数据应该放在Fragment B中的ArrayList中, 并且应该更新视图。
第 5 步在片段 B 中重写了 OnViewCreated() 代码以永远观察 Arraylist :
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
initRecView();
model = new ViewModelProvider(requireActivity()).get(TransactionViewModel.class);
Observer<ArrayList<TransactionItem>> observer = (Observer<ArrayList<TransactionItem>>) this::initObserve;
model.getSelected().observeForever(observer);
super.onViewCreated(view, savedInstanceState);
}
并且 initObserve() 现在有下一个代码:
private void initObserve(ArrayList<TransactionItem> list){
adapter.updateItemList(list);
}
目前这个方案是可行的,用户不需要切换到Fragment B来保持交易记录。我将继续测试此解决方案。