web-dev-qa-db-fra.com

Partager ViewModel entre des fragments appartenant à des activités différentes

J'ai un ViewModel nommé SharedViewModel:

public class SharedViewModel<T> extends ViewModel {

    private final MutableLiveData<T> selected = new MutableLiveData<>();


    public void select(T item) {
        selected.setValue(item);
    }

    public LiveData<T> getSelected() {
        return selected;
    }
}

Je l'implémente en me basant sur l'exemple SharedViewModel de la page de référence de Google Arch ViewModel: 

https://developer.Android.com/topic/libraries/architecture/viewmodel.html#sharing_data_between_fragments

Il est très courant que deux ou plusieurs fragments d'une activité doivent communiquer l'un avec l'autre. Ce n'est jamais trivial comme les deux les fragments doivent définir une description de l'interface et le propriétaire l'activité doit lier les deux ensemble. De plus, les deux fragments doivent gérer le cas où l'autre fragment n'est pas encore créé ou non visible.

J'ai deux fragments, appelés ListFragment et DetailFragment

Jusqu'à présent, j'ai utilisé ces deux fragments à l'intérieur d'une MasterActivity. Et tout a bien fonctionné. 

J'ai le ViewModel dans ListFragment, sélectionné la valeur pour l'utiliser sur DetailFragment.

mStepSelectorViewModel = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);

Cependant, il me faut maintenant que, dans certains cas, ListFragment (une disposition d'une configuration de périphérique différente) soit ajoutée à une activité différente, appelée DetailActivity. Existe-t-il un moyen de faire de même avec l'exemple ci-dessus? 

12
alexpfx

Un peu tard, mais vous pouvez accomplir cela en utilisant une ViewModelStore partagée. Les fragments et les activités implémentent l'interface ViewModelStoreOwner. Dans ces cas, les fragments ont un magasin par instance et les activités l'enregistrent dans un membre statique (je suppose donc qu'il peut survivre aux changements de configuration).

Pour revenir à la ViewModelStore partagée, disons par exemple que vous voulez que ce soit votre instance d’application. Vous avez besoin de votre application pour implémenter ViewModelStoreOwner

class MyApp: Application(), ViewModelStoreOwner {
    private val appViewModelStore: ViewModelStore by lazy {
        ViewModelStore()
    }

    override fun getViewModelStore(): ViewModelStore {
        return appViewModelStore
    }
}

Ensuite, dans les cas où vous savez que vous devez partager ViewModels entre des limites d’activités, vous faites quelque chose comme ceci.

val viewModel = ViewModelProvider(myApp, viewModelFactory).get(CustomViewModel::class.Java)

Alors maintenant, il utilisera le magasin défini dans votre application. De cette façon, vous pouvez partager ViewModels.

Très important. Parce que dans cet exemple, la variable ViewModels réside dans votre instance d'application, elle ne sera pas détruite lorsque le fragment/l'activité qui les utilise est détruit. Vous devrez donc les lier au cycle de vie du dernier fragment/activité qui les utilisera ou les détruire manuellement.

8
mikehc

vous pouvez utiliser factory pour faire viewmodel et ce facteur renverra un seul objet de vue modèle.

class ViewModelFactory() : ViewModelProvider.Factory {

override fun create(modelClass: Class): T {
    if (modelClass.isAssignableFrom(UserProfileViewModel::class.Java)) {
    val key = "UserProfileViewModel"
    if(hashMapViewModel.containsKey(key)){
        return getViewModel(key) as T
    } else {
        addViewModel(key, UserProfileViewModel())
        return getViewModel(key) as T
    }
    }
    throw IllegalArgumentException("Unknown ViewModel class")
}

companion object {
    val hashMapViewModel = HashMap<String, ViewModel>()
    fun addViewModel(key: String, viewModel: ViewModel){
        hashMapViewModel.put(key, viewModel)
    }
    fun getViewModel(key: String): ViewModel? {
        return hashMapViewModel[key]
    }
}
}

En activité:

viewModelFactory = Injection.provideViewModelFactory(this)

// Initialize Product View Model
userViewModel = ViewModelProviders.of(this, viewModelFactory).get(
UserProfileViewModel::class.Java)`

Cela ne fournira qu'un seul objet UserProfileViewModel que vous pourrez partager entre les activités.

2
Munish Thakur

Si vous souhaitez un ViewModel partagé par toutes vos activités (par opposition à certaines), alors pourquoi ne pas stocker ce que vous voulez dans ce ViewModel Dans votre classe Application?

La tendance présentée lors du dernier Google I/O semble être d’abandonner le concept d’Activités au profit d’applications à activité unique qui comportent de nombreux fragments. une interface devait autrefois être mise en œuvre… Ainsi, cette approche ne permet plus des activités gigantesques et intangibles.

1
Marcus Wolschon

Je pense que nous sommes toujours confus avec le framework MVVM sur Android ..__ Pour une autre activité, ne vous perdez pas parce que ce doit nécessairement être la même chose, pourquoi?

Cela a du sens si la logique est la même (même si la logique peut toujours être abstraite dans d’autres classes utiles), ou si la vue dans le XML est presque identique.

Prenons un exemple rapide:

Je crée un ViewModel appelé vmA, et une activité appelée A et j'ai besoin des données de l'utilisateur, je vais insérer le référentiel dans vmA de l'utilisateur.

Maintenant, j'ai besoin d'une autre activité nécessitant la lecture des données utilisateur, Je crée un autre ViewModel appelé vmB et j'appellerai le référentiel d'utilisateurs . Comme décrit, le référentiel est toujours identique.

Un autre moyen déjà suggéré consiste à créer N instances du même ViewModel avec la mise en œuvre de la fabrique.

0
AlexPad