web-dev-qa-db-fra.com

ViewModel onchange est appelé plusieurs fois à son retour de Fragment

Je travaille avec des composants d'architecture Android. Ce que je veux, c’est lorsque l’utilisateur tape "0" dans Edittext et clique sur le bouton pour remplacer Fragment par un nouveau, et si vous tapez autre chose un message d’erreur post Toast. Dans Problème, c’est quand je reviens d’un nouveau fragment (BlankFragment) et que je clique à nouveau sur le bouton et qu’on tape à nouveau "0" et qu’on clique sur onchange () est appelé plusieurs fois pour que le fragment soit créé plusieurs fois.

FragmentExample.class:

 @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    manager = getActivity().getSupportFragmentManager();
    viewmModel = ViewModelProviders.of(getActivity(), viewModelFactory)
            .get(VModel.class);

    View v = inflater.inflate(R.layout.fragment_list, container, false);   
    b = (Button) v.findViewById(R.id.b);
    et = (EditText) v.findViewById(R.id.et);

    viewmModel.observeData().observe(getActivity(), new Observer<String>() {
        @Override
        public void onChanged(@Nullable String s) {

            if(s.equals("0")) {
                BlankFragment fragment = (BlankFragment) manager.findFragmentByTag(DETAIL_FRAG);
                if (fragment == null) {
                    fragment = BlankFragment.newInstance();
                }
                addFragmentToActivity(manager,
                        fragment,
                        R.id.root_activity_detail,
                        DETAIL_FRAG
                );
            } else {
                Toast.makeText(getContext(), "Wrong text", Toast.LENGTH_SHORT).show();
            }
        }
    });

    b.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            viewmModel.setData(et.getText().toString());
        }
    });
    return v;
}
private void addFragmentToActivity(FragmentManager fragmentManager, BlankFragment fragment, int root_activity_detail, String detailFrag) {
    Android.support.v4.app.FragmentTransaction transaction = fragmentManager.beginTransaction();
    transaction.replace(root_activity_detail, fragment, detailFrag).addToBackStack(detailFrag);
    transaction.commit();
}

Classe de ré-histoire:

public class Repository {
MutableLiveData<String> dataLive = new MutableLiveData<>();  

public Repository() {

}

public void setListData(String data) {
   dataLive.setValue(data);
}

public MutableLiveData<String> getData() {
    return dataLive;
}

}

BlankFragment.class:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {

    listItemViewModel = ViewModelProviders.of(this, viewModelFactory)
            .get(VModel.class);
    listItemViewModel.setData("");
    return inflater.inflate(R.layout.fragment_blank, container, false);
}
4
Nikolas Bozic

Le problème ici est que lorsque vous séparez le fragment de l'activité, le fragment et son modèle de vue ne sont pas détruits. Lorsque vous revenez, vous ajoutez un nouvel observateur à la livedata lorsque l'ancien observateur est toujours présent dans le même fragment (Si vous ajoutez l'observateur dans onCreateView(). Il y a un article (même un fil SO en fait) en parle (avec la solution).

Le moyen le plus simple de résoudre ce problème (également dans l'article) consiste à supprimer tout observateur des données vivantes avant d'y ajouter l'observateur.

Mise à jour: dans la bibliothèque de support v28, un nouveau LifeCycleOwner appelé ViewLifeCycleOwner devrait corriger cela plus d’informations dans ici

8
Julien Neo

Au lieu d'utiliser getActivity en tant que LifecycleOwner, vous devez utiliser fragment.

Changement

viewmModel.observeData().observe(getActivity(), new Observer<String>() {

à

viewmModel.observeData().removeObservers(this);
viewmModel.observeData().observe(this, new Observer<String>() {
4
Cheok Yan Cheng

Vous ne devez pas créer votre viewmModel dans onCreateView, mais plutôt dans onCreate afin de ne pas ajouter d'écouteur à vos données chaque fois que la vue est créée.

1
Samuel Eminet
viewmModel = ViewModelProviders.of(getActivity(), viewModelFactory)
            .get(VModel.class);

Comme votre viewmModel's LifecycleOwner est une activité, l'observateur ne sera automatiquement supprimé que lorsque l'état du cycle de vie est Lifecycle.State.DESTROYED .
Dans votre cas, l'observateur ne sera pas automatiquement supprimé. Vous devez donc supprimer manuellement l'observateur précédent ou transmettre la même instance d'observateur à chaque fois.

0
wangqi060934