Je crée une Android utilisant MVVM et DataBinding. Et j'ai une fonction dans mon ViewModel qui démarre une activité. Est-ce correct d'avoir un appel onClick dans un ViewModel?
Comme ça.
public class MyViewModel {
public void onClick(View view, long productId) {
Context context = view.getContext();
Intent intent = new Intent(context, ProductDetailActivity.class);
intent.putExtra("productId", productId);
context.startActivity(intent);
}
}
Et dans mon XML:
...
Android:onClick="@{(v) -> viewModel.onClick(v, viewModel.product.id)}">
Ou serait-ce une meilleure pratique de le déplacer vers la vue et de l'appeler depuis EventBus ou Rx et d'avoir uniquement POJO dans mon ViewModel?
La réponse à votre question est quel est votre objectif?
Si vous souhaitez utiliser MVVM pour la séparation des problèmes afin que vous puissiez tester à l'unité votre Viewmodel
, vous devriez essayer de conserver tout ce qui nécessite un Context
distinct de votre Viewmodel
. Viewmodel
contient la logique métier de base de votre application et ne doit pas avoir de dépendances externes.
Cependant, j'aime où vous allez :) Si la décision de l'ouverture de l'activité réside dans la vue, il est très difficile d'écrire un test JUnit pour cela. Cependant, vous pouvez passer un objet dans Viewmodel
qui exécute l'appel startActivity()
. Maintenant dans votre Test unitaire vous pouvez simplement vous moquer de cet objet et vérifier que le bon Activity
est ouvert
C'est absolument parfait pour le mettre dans ViewModel
, mais vous devez définir votre ViewModel
à partir de Activity
/Fragment
.
Voici quelques liens que vous pouvez suivre pour apprendre l'architecture MVVM.
Approchant Android avec MVVM
Android MVVM
https://github.com/ivacf/archi
People-MVVM
MVVM sur Android: ce que vous devez savoir
La façon dont je le fais est, dans votre ViewModel:
val activityToStart = MutableLiveData<Pair<KClass<*>, Bundle?>>()
Cela vous permet de vérifier la classe d'activité démarrée et les données transmises dans le bundle. Ensuite, dans votre activité, vous pouvez ajouter ce code:
viewModel.activityToStart.observe(this, Observer { value ->
val intent = Intent(this, value.first.Java)
if(value.second != null)
intent.putExtras(value.second)
startActivity(intent)
})
Comme le principe de MVVM souligne que seule la vue (activité/fragment) contient une référence au ViewModel et que le ViewModel ne doit contenir aucune référence à une vue.
Dans votre cas, pour démarrer une activité, je vais faire comme ceci:
MyViewModel.class
public class MyViewModel {
public static final int START_SOME_ACTIVITY = 123;
@Bindable
private int messageId;
public void onClick() {
messageId = START_SOME_ACTIVITY;
notifyPropertyChanged(BR.messageId); //BR class is automatically generated when you rebuild the project
}
public int getMessageId() {
return messageId;
}
public void setMessageId(int message) {
this.messageId = messageId;
}
}
Et dans votre MainActivity.class
@BindingAdapter({"showMessage"})
public static void runMe(View view, int messageId) {
if (messageId == Consts.START_SOME_ACTIVITY) {
view.getContext().startActivity(new Intent(view.getContext(), SomeActivity.class));
}
}
@Override
protected void onPause() {
super.onPause();
finish(); //only call if you want to clear this activity after go to other activity
}
enfin, dans votre activity_main.xml
<Button
Android:onClick="@{()-> myViewModel.onClick()}"
bind:showMessage="@{myViewModel.messageId}" />
Selon la documentation de liaison de données. Il y a 2 façons de procéder:
1- MethodReferences : Vous devez passer la vue en tant que paramètre à la fonction, sinon vous obtiendrez une erreur de temps de compilation.
Si vous utilisez cette méthode, faites ici une classe séparée comme exemple qui gère de tels événements.
MyHandler
public class MyHandler {
public void onClick(View view, long productId) {
Context context = view.getContext();
Intent intent = new Intent(context, ProductDetailActivity.class);
intent.putExtra("productId", productId);
context.startActivity(intent);
}
}
XML
<data>
<variable
name="viewModel"
type="com.example.ViewModel"
<variable
name="myHandler"
type="com.example.MyHandler" />
</data>Android:onClick="@{myHandler.onClick(viewModel.product.id)}">
2- liaisons d'écoute : vous n'avez pas besoin de passer la vue comme exemple ici.
Mais si vous voulez démarrerActivity, faites que votre viewModel étend AndroidViewModel et vous utiliserez l'objet applicaion.
ViewModel
public class MyViewModel extends AndroidViewModel {
public void onClick(long productId) {
Intent intent = new Intent(getApplication(), ProductDetailActivity.class);
intent.putExtra("productId", productId);
context.startActivity(intent);
}
}
XML
Android:onClick="@{() -> viewModel.onClick(viewModel.product.id)}">