0

我试图弄清楚为什么getAllGoals()当我更新记录时 LiveData 观察者不会立即在片段中触发。但是,只有在使用底部选项卡导航切换到另一个片段然后返回到原始片段后,才会调用观察者。

有问题的片段:MyGoalsFragment.java


public class MyGoalsFragment extends Fragment implements MyGoalsAdapter.MyGoalsCallback {

    FragmentMyGoalsBinding myGoalsBinding;
    private MyGoalsViewModel myGoalsViewModel;
    MyGoalsAdapter myGoalsAdapter;
    ConstraintSet smallConstraintSet = new ConstraintSet();

    public View onCreateView(@NonNull LayoutInflater inflater,
                             ViewGroup container, Bundle savedInstanceState) { 
                             
        myGoalsViewModel = new ViewModelProvider(getActivity(), ViewModelProvider.AndroidViewModelFactory.getInstance(getActivity().getApplication())).get(MyGoalsViewModel.class);
        myGoalsBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_my_goals, container, false);

        myGoalsBinding.recyclerView2.setLayoutManager(new LinearLayoutManager(getActivity()));
        DrawerLayout drawerLayout = (DrawerLayout) getActivity().findViewById(R.id.drawer_layout);
        myGoalsBinding.menu.setOnClickListener(v -> {
            drawerLayout.openDrawer(GravityCompat.START);
        });

        TransitionManager.beginDelayedTransition(myGoalsBinding.recyclerView2);
        myGoalsAdapter = new MyGoalsAdapter();
        myGoalsAdapter.setCallback(this);
        myGoalsAdapter.setContext(getActivity());
        myGoalsAdapter.setRecyclerView(myGoalsBinding.recyclerView2);

        myGoalsBinding.recyclerView2.setAdapter(myGoalsAdapter);


        myGoalsBinding.floatingActionButton.setOnClickListener(v -> {
            startActivity(new Intent(getActivity(), CreateGoalActivity.class));
            getActivity().finish();

        });

        enableSwipeToDeleteAndUndo();

        myGoalsBinding.recyclerView2.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                if (dy > 0 && myGoalsBinding.floatingActionButton.getVisibility() == View.VISIBLE) {
                    myGoalsBinding.floatingActionButton.hide();
                } else if (dy < 0 && myGoalsBinding.floatingActionButton.getVisibility() != View.VISIBLE) {
                    myGoalsBinding.floatingActionButton.show();
                }
            }
        });

        myGoalsViewModel.getAllGoals().observe(getViewLifecycleOwner(), new Observer<List<Goal>>() {
            @Override
            public void onChanged(List<Goal> goals) {
                myGoalsAdapter.submitList(goals); // This observer is not called even after updating a record
            }
        });

        return myGoalsBinding.getRoot();
    }

    @Override
    public void editGoalCallback(Goal goal) {
        Intent intent = new Intent(getActivity(), CreateGoalActivity.class);
        Bundle bundle = new Bundle();
        bundle.putSerializable("goal", goal);
        intent.putExtras(bundle);
        startActivity(intent);
    }

    @Override
    public void goalCheckBoxCallback(Goal goal) {
        myGoalsViewModel.updateGoal(goal); 
    }

    private void enableSwipeToDeleteAndUndo() {
        SwipeToDeleteCallback swipeToDeleteCallback = new SwipeToDeleteCallback(getActivity()) {
            @Override
            public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int i) {


                if(i==ItemTouchHelper.LEFT) {
                    Goal tempGoal = myGoalsAdapter.getGoalAt(viewHolder.getAdapterPosition());
                    myGoalsViewModel.deleteGoal(tempGoal);
                    Snackbar.make(myGoalsBinding.rootConstraintLayout, "Goal Deleted", Snackbar.LENGTH_LONG)
                            .setAction("Undo", v -> {
                                myGoalsViewModel.insertGoal(tempGoal);
                            })
                            .setActionTextColor(getActivity().getResources().getColor(R.color.arcticLimeGreen))
                            .show();

                }else if(i==ItemTouchHelper.RIGHT){
                    Goal tempGoal = myGoalsAdapter.getGoalAt(viewHolder.getAdapterPosition());

                    if(tempGoal.isCompleted())
                        tempGoal.setCompleted(false);
                    else
                        tempGoal.setCompleted(true);

                    TransitionManager.beginDelayedTransition(myGoalsBinding.recyclerView2);
                    
                    myGoalsViewModel.updateGoal(tempGoal); // This is where the update is called
                }
            }
        };

        ItemTouchHelper itemTouchhelper = new ItemTouchHelper(swipeToDeleteCallback);
        itemTouchhelper.attachToRecyclerView(myGoalsBinding.recyclerView2);
    }

}


MyGoals 视图模型:


public class MyGoalsViewModel extends AndroidViewModel {

    private NoteRepository repository;
    private LiveData<List<Goal>> allGoals;


    public MyGoalsViewModel(@NonNull Application application) {
        super(application);
        repository = new NoteRepository(application);
        allGoals = repository.getAllGoals();

    }

    public LiveData<List<Goal>> getAllGoals(){
        return allGoals;
    }

    public void deleteGoal(Goal goal){repository.deleteGoal(goal);}

    public void insertGoal(Goal goal){repository.insertGoal(goal);}

    public void updateGoal(Goal goal){repository.updateGoal(goal);}

}

存储库:

public class NoteRepository {

    private String DB_NAME = "db_task";

    Context context;
    private GoalDao goalDao;
    private LiveData<List<Goal>> allGoals;

    private NoteDatabase noteDatabase;
    public NoteRepository(Context context) {
        noteDatabase = NoteDatabase.getInstance(context);
        goalDao = noteDatabase.goalDao();
        allGoals = goalDao.getAllGoals();
        this.context = context;
    }

    public void insertGoal(Goal goal){
        new InsertGoalAsyncTask(goalDao).execute(goal);
    }

    public void deleteGoal(Goal goal){
        new DeleteGoalAsyncTask(goalDao).execute(goal);
    }

    public void updateGoal(Goal goal){
        new UpdateGoalAsyncTask(goalDao).execute(goal);
    }

    public void deleteAllGoals(){
        new DeleteAllGoalAsyncTask(goalDao).execute();
    }

    public LiveData<List<Goal>> getAllGoals(){
        return allGoals;

    }

    private static class InsertGoalAsyncTask extends AsyncTask<Goal,Void,Void>{
        private GoalDao goalDao;

        private InsertGoalAsyncTask(GoalDao goalDao){
            this.goalDao = goalDao;
        }

        @Override
        protected Void doInBackground(Goal... goals) {
            goalDao.insert(goals[0]);
            return null;
        }
    }

    private static class DeleteGoalAsyncTask extends AsyncTask<Goal,Void,Void>{
        private GoalDao goalDao;

        private DeleteGoalAsyncTask(GoalDao goalDao){
            this.goalDao = goalDao;
        }

        @Override
        protected Void doInBackground(Goal... goals) {
            goalDao.delete(goals[0]);
            return null;
        }
    }

    private static class UpdateGoalAsyncTask extends AsyncTask<Goal,Void,Void>{
        private GoalDao goalDao;

        private UpdateGoalAsyncTask(GoalDao goalDao){
            this.goalDao = goalDao;
        }

        @Override
        protected Void doInBackground(Goal... goals) {
            goalDao.update(goals[0]);
            return null;
        }
    }

    private static class DeleteAllGoalAsyncTask extends AsyncTask<Void,Void,Void>{
        private GoalDao goalDao;

        private DeleteAllGoalAsyncTask(GoalDao goalDao){
            this.goalDao = goalDao;
        }

        @Override
        protected Void doInBackground(Void... voids) {
            goalDao.deleteAllGoals();
            return null;
        }
    }
}

DAO 类:

@Dao
public interface GoalDao {

    @Insert
    void insert(Goal goal);

    @Update
    void update(Goal goal);

    @Delete
    void delete(Goal goal);

    @Query("DELETE from goal_table")
    void deleteAllGoals();

    @Query("Select * from goal_table order by end_date")
    LiveData<List<Goal>> getAllGoals();

}

我在 2 个片段中有这个问题,还有 2 个其他片段在完全相同的实现中没有这个问题。为什么在我更新 MyGoals 片段中的记录后没有立即调用观察者?

4

1 回答 1

0

我找到了解决方案,问题不在 LiveData 代码中,而是在停止触发 LiveData 更改的 Recyclerview ListAdapter & DiffUtil 实现中。

在 MyGoalsAdapter 中,我使用了 DiffUtil 和 ListAdapter 来获得流畅的动画并提高性能。为了让它正常工作,我们需要将新列表与旧列表进行比较。问题是对象的内容实际上不同时被标记为相等的地方。我通过在我的模型类中添加一个日期字段modifiedAt 并在更新该对象之前更新了该字段来解决这个问题。这是代码片段,可以更好地解释它。

我的目标适配器:

public class MyGoalsAdapter extends ListAdapter<Goal, MyGoalsAdapter.MyGoalsViewHolder> {
    private Context context;

    public MyGoalsAdapter() {
        super(DIFF_CALLBACK);
    }

    private static final DiffUtil.ItemCallback<Goal> DIFF_CALLBACK = new DiffUtil.ItemCallback<Goal>() {
        @Override
        public boolean areItemsTheSame(@NonNull Goal oldItem, @NonNull Goal newItem) {
            return oldItem.getId() == newItem.getId();
        }

        @Override
        public boolean areContentsTheSame(@NonNull Goal oldItem, @NonNull Goal newItem) { //Here we check if the objects in the list have changed fields.
            boolean id,desc,iscomp,edate,etime,sdate,stime,title, naya, purana, createdAt, modifiedAt;

            id = oldItem.getId() == newItem.getId();
            desc = oldItem.getDescription().equals(newItem.getDescription());
            purana = oldItem.isCompleted();
            naya = newItem.isCompleted();
            iscomp = purana && naya;
            edate =  oldItem.getEnd_date().equals(newItem.getEnd_date());
            etime = oldItem.getEnd_time().equals(newItem.getEnd_time());
            sdate = oldItem.getStart_date().equals(newItem.getStart_date());
            stime = oldItem.getStart_time().equals(newItem.getStart_time());
            title = oldItem.getTitle().equals(newItem.getTitle());
            createdAt = oldItem.getCreatedAt().equals(newItem.getCreatedAt());
            modifiedAt = oldItem.getModifiedAt().equals(newItem.getModifiedAt()); //This will return false for the object that is changed 

            return id &&
                    desc &&
                    iscomp &&
                    edate &&
                    etime &&
                    sdate &&
                    stime &&
                    title &&
                    createdAt &&
                    modifiedAt
                    ;
        }
    };
}

当我更新时,我modifiedAt使用当前日期和时间设置对象字段。

Goal tempGoal = myGoalsAdapter.getGoalAt(viewHolder.getAdapterPosition()); //Get the object to make change to it

//make change to the object's field

tempGoal.setModifiedAt(Calendar.getInstance().getTime()); //set the modified date with Current date
myGoalsViewModel.updateGoal(tempGoal); //Update the object to the database


更改modifiedAt字段将告诉适配器何时有更新的对象,触发动画并立即在列表中显示更新的对象。

我希望这会对某人有所帮助。

于 2020-08-07T08:05:32.953 回答