ViewModel 数据不更新

ViewModel datas don't update

我有一个 UpdateFragment,它使用 RecyclerView 和 ViewModel 按标签分组显示卡片。在 UpdateFragment 中,我还可以修改标签的名称,但我不知道为什么在我重新创建片段之前 UpdateFragment 未检测到更改,因此 UI 不更新。奇怪的是,MainActivity 反而检测到列表的更新和代码相等。

UpdateFragment.class

public class UpdateFragment extends BaseCardFragment {

// TODO show total cards number

private static final String TAG = "UpdateFragment";

private RecyclerView recyclerView;

public UpdateFragment(List<CardWithTags> cardList, List<Tag> tagList) {
    super(cardList, tagList);
}

@Override
public void updateUI(List<CardWithTags> cardListWithTags, List<Tag> tagList) {

    RecyclerViewAdapterUpdate adapter = (RecyclerViewAdapterUpdate) recyclerView.getAdapter();
    Log.d(TAG, "Adapter:" + adapter);
    Log.d(TAG, "Cards:" + cardListWithTags);
    Log.d(TAG, "Tags:" + tagList);
    if (adapter != null && cardListWithTags != null && tagList != null) {

        adapter.setCardList(cardListWithTags);
        adapter.setTagList(tagList);
        adapter.notifyDataSetChanged();
        Log.d(TAG, ">>Update, Total cards: " + adapter.getItemCount());
        Log.d(TAG, ">>Update, Total tags: " + tagList.size());

    }
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    return inflater.inflate(R.layout.fragment_update, container, false);
}

@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);

    setViewModel();

    setRecyclerView();
}

private void setViewModel() {

    Log.d(TAG, ">>SetViewModel()");

    viewModel = new ViewModelProvider(this).get(ViewModelMainActivity.class);
    viewModel.getAllCards().observe(getActivity(), cardListLoaded -> {
        cardList = cardListLoaded;
        Log.d(TAG, ">>CardList updated:" + cardList);
        updateUI(cardList, tagList);
    });

    viewModel.getAllTags().observe(getActivity(), tagListLoaded -> {
        tagList = tagListLoaded;
        Log.d(TAG, ">>TagList updated:" + tagList);
        updateUI(cardList, tagList);
    });

}

private void setRecyclerView() {

    Log.d(TAG, ">>SetRecyclerView()");
    recyclerView = getView().findViewById(R.id.recycler_view_list);

    RecyclerViewAdapterUpdate recyclerViewAdapter = new RecyclerViewAdapterUpdate(cardList, tagList,
                                                            getContext(), getLayoutInflater(),
                                                            viewModel);
    RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getContext(),
                                                        RecyclerView.VERTICAL, false);
    recyclerView.setLayoutManager(layoutManager);

    recyclerView.setAdapter(recyclerViewAdapter);

}
}

ViewModelMainActivity.class

public class ViewModelMainActivity extends AndroidViewModel {

    private static final String TAG = "ViewModelMainActivity";
    private static boolean cardListIsUpdatedWithDb = false;
    private static boolean tagListIsUpdatedWithDb = false;

    private LiveData<List<CardWithTags>> cardList = new MutableLiveData<>();
    private LiveData<List<Tag>> tagList = new MutableLiveData<>();
    private final CardDAO cardDAO;
    private final ExecutorService executor;

    public ViewModelMainActivity(@NonNull Application application) {
        super(application);

        this.cardDAO = DatabaseTaboom.getDatabase(application).cardDao();
        this.executor = Executors.newSingleThreadExecutor();
    }

    public LiveData<List<CardWithTags>> getAllCards() {

        Log.d(TAG, ">>GetAllCards()");

        if (!cardListIsUpdatedWithDb) {
            cardList = cardDAO.getAllCards();
            cardListIsUpdatedWithDb = true;
        }

        return cardList;

    }

    public LiveData<List<Tag>> getAllTags() {

        Log.d(TAG, ">>GetAllTags()");

        if (!tagListIsUpdatedWithDb) {
            tagList = cardDAO.getAllTags();
            tagListIsUpdatedWithDb = true;
        }

        return tagList;

    }

    public void insertCard(CardWithTags card) {

        Log.d(TAG, ">>InsertCard(): " + card);

        executor.execute(() -> {

            long idCard = cardDAO.insertCard(card.getCard());

            if (idCard >= 1) {
                cardListIsUpdatedWithDb = false;
                Log.d(TAG, ">>Inserted Card: " + card.getCard().getTitle());
            }

            for (Tag t: card.getTagList()) {
                long idTag = cardDAO.insertTag(t);

                CardTagCrossRef cardTagCrossRef = new CardTagCrossRef();
                cardTagCrossRef.idCard = idCard;

                if (idTag < 1) {
                    // If Tag already exist, retrieve that to get id to try insert cwt
                    idTag = cardDAO.getTag(t.getTag()).getIdTag();
                } else {
                    // New Tag
                    tagListIsUpdatedWithDb = false;
                    Log.d(TAG, ">>Inserted Tag: " + t.getTag());
                }

                cardTagCrossRef.idTag = idTag;
                long idCWT = cardDAO.insertCardWithTags(cardTagCrossRef);
                if (idCWT >= 1) {
                    Log.d(TAG, ">>Inserted CWT: [" + idCard + "," + idTag + "]");
                    // Tag linked to a card
                    cardListIsUpdatedWithDb = false;
                }
            }

        });

    }

    public void shuffle(List<CardWithTags> list) {
        if (list != null) {
            Collections.shuffle(list);
            cardList = new MutableLiveData<>(list);
            Log.d(TAG, ">>List shuffled: " + list);
        } else {
            Log.d(TAG, ">>List is null");
        }
    }

    public void updateTag(Tag tag) {

        executor.execute(() -> {
            cardDAO.updateTag(tag);
            tagListIsUpdatedWithDb = false;
            cardListIsUpdatedWithDb = false;
        });
    }

}

RecyclerViewAdapterUpdate.class

public class RecyclerViewAdapterUpdate extends RecyclerView.Adapter<RecyclerViewAdapterUpdate.ViewHolder> {

    private static final String TAG = "RecyclerViewAdapterUpdate";

    private List<CardWithTags> cardList;
    private List<Tag> tagList;
    private Context context;
    private LayoutInflater layoutInflater;
    private ViewModelMainActivity viewModelFragment;

    public RecyclerViewAdapterUpdate(List<CardWithTags> cardList, List<Tag> tagList, Context context,
                                     LayoutInflater layoutInflater, ViewModelMainActivity viewModelFragment) {
        this.cardList = cardList;
        this.tagList = tagList;
        this.context = context;
        this.layoutInflater = layoutInflater;
        this.viewModelFragment = viewModelFragment;
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {

        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_update, parent, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {

        if (position == 0) {
            holder.numberOfItems.setText(String.valueOf(cardList.size()));
            holder.tagName.setText(R.string.all_cards_tag);
            holder.clearTag.setVisibility(View.GONE);
        } else {

            Tag tag = tagList.get(position - 1);
            List<CardWithTags> listOfSingleTag = new ArrayList<>();

            for (CardWithTags cwt: cardList) {
                for (Tag t: cwt.getTagList()) {
                    if (t.getTag().equals(tag.getTag()))
                        listOfSingleTag.add(cwt);
                }
            }

            holder.numberOfItems.setText(String.valueOf(listOfSingleTag.size()));
            holder.tagName.setText(tag.getTag());

            View viewDialogTag = layoutInflater.inflate(R.layout.dialog_modify_tag, null);
            EditText tagNameEditText = viewDialogTag.findViewById(R.id.tag_name_dialog);
            tagNameEditText.setText(tag.getTag());

            // In this way the dialog is created only one time
            AlertDialog dialog = new AlertDialog.Builder(context)
                    .setView(viewDialogTag)
                    .setTitle(R.string.title_modify_tag)
                    .setPositiveButton(R.string.modify, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialogInterface, int i) {

                            String newTag = tagNameEditText.getText().toString();
                            if (!newTag.isEmpty()&& !newTag.equalsIgnoreCase(tag.getTag())) {
                                tag.setTag(newTag);
                                viewModelFragment.updateTag(tag);
                                Log.d(TAG, ">>Update TAG: " + tag.getTag() +  "->" + newTag);
                            }
                        }
                    })
                    .create();

            holder.tagName.setOnLongClickListener( view -> {
                dialog.show();
                return true;
            });

            holder.clearTag.setOnClickListener( v -> Log.d(TAG, ">>CLICKED"));
        }

    }

    @Override
    public int getItemCount() {
        if (tagList == null)
            return 1;
        return tagList.size() + 1;
    }

    public class ViewHolder extends RecyclerView.ViewHolder {

        private static final String TAG = "ViewHolder";

        private final TextView numberOfItems;
        private final TextView tagName;
        private final Button clearTag;
        private final CheckBox checkBox;

        public ViewHolder(@NonNull View itemView) {
            super(itemView);

            numberOfItems = itemView.findViewById(R.id.number_of_items);
            tagName = itemView.findViewById(R.id.tag_name);
            clearTag = itemView.findViewById(R.id.clear_tag);
            checkBox = itemView.findViewById(R.id.check_box);

        }

    }

    public void setCardList(List<CardWithTags> cardList) {
        this.cardList = cardList;
    }

    public void setTagList(List<Tag> tagList) {
        this.tagList = tagList;
    }
}

我不明白为什么不调用这段代码:

 viewModel.getAllCards().observe(getActivity(), cardListLoaded -> {
        cardList = cardListLoaded;
        Log.d(TAG, ">>CardList updated:" + cardList);
        updateUI(cardList, tagList);
    });

    viewModel.getAllTags().observe(getActivity(), tagListLoaded -> {
        tagList = tagListLoaded;
        Log.d(TAG, ">>TagList updated:" + tagList);
        updateUI(cardList, tagList);
    });

MainActivity.class 方法被调用,即使它等于 UpdateFragment 中的那个方法(activity 是相同的):

private void firstLoadCardList() {

        ConstraintLayout fragmentContainer = findViewById(R.id.fragment_container);
        ConstraintLayout progressBarContainer = findViewById(R.id.loading_container);
        fragmentContainer.setVisibility(View.GONE);
        progressBarContainer.setVisibility(View.VISIBLE);

        Log.d(TAG, ">>FirstLoadCardList()");

        viewModel = new ViewModelProvider(this).get(ViewModelMainActivity.class);

        checkIfDatabaseAlreadyExist();

        viewModel.getAllCards().observe(this, cardListLoaded -> {

            cardList = cardListLoaded;

            Log.d(TAG, ">>Total cards found: " + cardList.size());

            if (tagList != null /*&& appJustOpened*/) {
                appJustOpened = false;
                progressBarContainer.setVisibility(View.GONE);
                fragmentContainer.setVisibility(View.VISIBLE);
                bottomNav.setSelectedItemId(R.id.play_nav);
            }
        });

        viewModel.getAllTags().observe(this, tagListLoaded -> {

            tagList = tagListLoaded;
            Log.d(TAG, ">>Total tags found: " + tagList.size());

            // If tagList not loaded before creation of BottomNav update Fragment list
            // and cardList already loaded
            if (cardList!= null /*&& appJustOpened*/) {
                appJustOpened = false;
                progressBarContainer.setVisibility(View.GONE);
                fragmentContainer.setVisibility(View.VISIBLE);
                bottomNav.setSelectedItemId(R.id.play_nav);
            }
        });

    }

请停止在评论中谈论 notifyDataSetChanged(),我知道它是如何工作的,我的问题是 ViewModel/observer。关于观察者的部分不应该在每次 ViewModel 列表更改时调用吗?谢谢。

我认为您只需要在 recycler 适配器中添加以下功能即可。

public void setCardList(List<CardWithTags> cardList){

this.cardList=new ArrayList<CardWithTags>()

this.cardList.addAll(cardList)

notifyDataSetChanged()

}

public void setTagList(List<Tag> tagList){

this.tagList=new ArrayList<Tag>();

this.tagList.addAll(tagList);

notifyDataSetChanged();

}

终于找到了答案:两个观察者方法不相等。事实上,在 Activity 和片段中我都写道: viewModel.getAllCards().observe(this, cardListLoaded -> {...} 但是 this 如果从片段中的 Activity 或片段调用则不同我必须改用 requireActivity()