【问题标题】:Livedata doesn't update data in fragment correctlyLivedata 无法正确更新片段中的数据
【发布时间】:2021-01-24 13:52:53
【问题描述】:

我在单独的标签中有 tablayout 和 2 fragments。 Fragment A 有一个被覆盖的方法,当 Activity(从 Fragment A 开始)在其销毁时返回数据时返回数据:

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);
    }
    }

在同样的方法中,我调用 ViewModel 来观察 TransactionNewItem 对象:

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 返回数据后,使用新值创建一个新 POJO,并将存储在此 POJO 中的数据发送到 Fragment B,其中基于来自 Fragment 的数据创建 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();
            });
            }
}

但是,它只会在 RecyclerView 中添加 1 个项目,并在 Activity 返回新数据时替换它。如果用户没有至少一次切换到 Fragment B,就会发生这种情况,因为在用户切换到 Fragment B 之前不会调用 onViewCreated。

如果用户之前从未切换到Fragment B,如何让ViewModel观察Fragment A的数据,并在每次Activity返回新数据时在Fragment B Recyclerview中创建新的TransActionItem?

提前致谢

【问题讨论】:

    标签: android android-fragments mvvm android-recyclerview android-livedata


    【解决方案1】:

    编辑:我设法以另一种方式做我想做的事:
    第 1 步。我使用 POJO 将 ViewModel 从 POJO 更改为 Arraylist - 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 返回结果后创建并添加对象,而不是在 Fragment 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);
        }
        }
    

    必须注意,我在 TransactionItem 构造函数中添加了 transactionID,这就是我们需要它的原因。
    第 3 步我创建了下一个扩展 DiffUtil.Callback 的 TransactionDiffUtilCallback 类:

    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 中的项,然后通知适配器 Fragment A 的 ArrayList 不同,这个数据应该放在 Fragment B 的 ArrayList 中,视图应该更新。

    第 5 步在 Fragment 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 来保持交易记录。我将继续测试此解决方案。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-12-15
      • 1970-01-01
      • 2018-05-05
      • 1970-01-01
      • 2023-03-03
      相关资源
      最近更新 更多