J'ai une activité avec 3 fragments, actuellement j'utilise ViewPager. Je veux implémenter MVP et communiquer entre le présentateur d'activité et les présentateurs de fragments, à savoir:
Mais je ne sais pas comment le faire de manière officielle. Je peux utiliser BusEvent mais je ne pense pas que ce soit une bonne pratique.
La communication entre les fragments et l'activité ou vice-versa peut être effectuée en utilisant réponse de nnn ou vous pouvez utiliser ViewModel
et LiveData
qui fournit un moyen plus propre et respecte le cycle de vie à partir de fragments
et activities
qui peuvent éviter d'écrire quelques lignes de code afin d'empêcher un fragment
non visible de recevoir des données en arrière-plan.
D'abord, vous étendez la classe ViewModel
, initialisez la Livedata
et certaines méthodes d'assistance.
public class MyViewModel extends ViewModel {
private MutableLiveData<String> toFragmentA, toFragmentB;
private MutableLiveData<List<String>> toAllFragments;
public MyViewModel() {
toFragmentA = new MutableLiveData<>();
toFragmentB = new MutableLiveData<>();
toAllFragments = new MutableLiveData<>();
}
public void changeFragmentAData(String value){
toFragmentA.postValue(value);
}
public void changeFragmentBData(String value){
toFragmentB.postValue(value);
}
public void changeFragmentAllData(List<String> value){
toAllFragments.postValue(value);
}
public LiveData<String> getToFragmentA() {
return toFragmentA;
}
public LiveData<List<String>> getToAllFragments() {
return toAllFragments;
}
public LiveData<String> getToFragmentB() {
return toFragmentB;
}
}
Ensuite, vous initialisez le ViewModel
sur votre activité.
public class MainActivity extends AppCompatActivity {
private ViewPager viewPager;
private TabLayout tabLayout;
MyViewModel mViewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mViewModel = ViewModelProviders.of(this)
.get(MyViewModel.class);
viewPager.setAdapter(new Adapter(getSupportFragmentManager()));
}
}
lire les données dans les fragments:
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mViewModel = ViewModelProviders.of(getActivity()).get(MyViewModel.class);
mViewModel.getToAllFragments().observe(this, new Observer<List<String>>() {
@Override
public void onChanged(List<String> s) {
myList.addAll(s);
//do something like update a RecyclerView
}
});
mViewModel.getToFragmentA().observe(this, new Observer<String>() {
@Override
public void onChanged(String s) {
mytext = s;
//do something like update a TextView
}
});
}
pour modifier les valeurs de l'une des données en direct, vous pouvez utiliser l'une des méthodes dans l'un des fragments ou dans l'activité:
changeFragmentAData();
changeFragmentBData();
changeFragmentAllData();
Qu'est-ce qui se passe dans les coulisses:
lorsque vous utilisez mViewModel = ViewModelProviders.of(this).get(MyViewModel.class)
vous créez une instance de ViewModel
et la liez au cycle de vie de l'activité donnée du fragment de sorte que le modèle de vue est destroid uniquement le activity
ou fragement
est arrêté. si vous utilisez mViewModel = ViewModelProviders.of (getActivity ()). get (MyViewModel.class) you are bindig it to the lifecycle if the parent
activity`
lorsque vous utilisez mViewModel.getToFragmentA().observe()
ou mViewModel.getToFragmentB().observe()
ou mViewModel.getToAllFragments().observe()
vous connectez la classe LiveData
in MyViewModel
au fragment ou à l'activité donnée et la valeur de la méthode onChange()
est mise à jour dans toutes les classes qui observent la méthode.
Je recommande par expérience personnelle un peu de recherche sur Livedata
end ViewModel
que vous pouvez sur youtube ou ce lien
Selon ma compréhension, pour votre UseCase, supposons que ActivityA ait un viewPager ayant 3 Fragments (FragmentA, FragmentB, FragmentC).
ActivityA have ActivityPresenterA
FragmentA have FragmentPresenterA
Selon MVP, FragmentPresenterA devrait être responsable de tous les flux logiques et commerciaux de FragmentA uniquement et devrait communiquer avec FragmentA uniquement. Par conséquent, FragmentPresenterA ne peut pas communiquer directement avec ActivityPresenterA.
Pour la communication de Fragment à Activity, le présentateur ne devrait pas être impliqué et cela devrait être fait comme nous communiquerions dans une architecture non MVP, c'est-à-dire à l'aide de l'interface.
Il en va de même pour la communication de l'activité au fragment.
Pour la communication entre l'activité et le fragment, lisez ici
Vous pouvez utiliser un présentateur pour ce cas.
Utilisez votre présentateur d'activité pour obtenir toutes les données dont vos fragments ont besoin. puis créez une classe d'interface et implémentez-la dans vos fragments.
Par exemple:
Créez une interface publique pour votre PageAFragment (cette interface fera le pont de vos données de l'activité au fragment). et utilisez la méthode de votre interface pour gérer le résultat de votre présentateur à afficher.
Voici l'exemple de classe d'interface que j'ai créé pour les données reçues. Pour le paramètre, vous pouvez choisir ce que vous voulez, cela dépend de vos besoins, mais pour moi, je choisis le modèle.
public interface CallbackReceivedData {
void onDataReceived(YourModel model);
}
Dans MainActivity Class , vérifiez l'instance de fragment qui s'est attachée à votre activité. placez votre instance de vérification après avoir validé le fragment.
public class MainActivity extends AppCompatActivity{
private CallbackReceivedData callbackReceivedData;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//after commit the fragment
if (fragment instanceof PageAFragment){
callbackReceivedData = (CallbackReceivedData)fragment;
}
}
//this is the example method of MainActivity Presenter,
//Imagine it, as your view method.
public void receivedDataFromPresenter(YourModel model){
callbackReceivedData.onDataReceived(model);
}
}
J'ai supposé que le receivedDataFromPresenter est la méthode reçue de notre vue et obtenir des données au présentateur.
Et maintenant, nous allons passer les données du présentateur à callbackReceivedData
Dans PageAFragment implémentez le CallbackReceivedData et remplacez le méthode onDataReceived . Vous pouvez maintenant transmettre les données de l'activité à votre fragment.
public class PageAFragment extends Fragment implements CallbackReceivedData{
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public void onDataReceived(YourModel model) {
}
}
Remarque: Vous pouvez également utiliser Bundle et transmettre les données à l'aide de setArguments.
Si vous souhaitez envoyer un événement du fragment à l'activité , vous pouvez suivre cette idée.
Créez une classe Interface et implémentez-la dans votre MainActivity et remplacez la méthode de l'interface à votre activité, pour mon cas, je le fais quelque chose comme ça.
Voici ma classe CallbackSendData .
public interface CallbackSendData {
void sendDataEvent(String event);
}
Implémentez l'interface CallbackSendData à votre MainActivity et remplacez le méthode sendDataEvent .
public class MainActivity extends AppCompatActivity implements CallbackSendData{
private CallbackReceivedData callbackReceivedData;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//after commit the fragment
if (fragment instanceof PageAFragment){
callbackReceivedData = (CallbackReceivedData)fragment;
}
}
//this is the example method of MainActivity Presenter,
//Imagine it, as your view method.
public void receivedDataFromPresenter(YourModel model){
callbackReceivedData.onDataReceived(model);
}
@Override
public void sendDataEvent(String event){
//You can now send the data to your presenter here.
}
}
Et à votre PageAFragment , vous devez utiliser la méthode attach pour convertir votre interface. La méthode attach est appelée une fois que le fragment est associé à son activité. Si vous voulez comprendre le cycle de vie d'un fragment, cliquez simplement sur ce lien: https://developer.Android.com/reference/Android/app/Fragment.html .
public class PageAFragment extends Fragment implements CallbackReceivedData{
private CallbackSendData callbackSendData;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public void onDataReceived(YourModel model) {
//Received the data from Activity to Fragment here.
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup
container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.PagerAFragment, container,
false);
}
@Override
public void onViewCreated(View view, @Nullable Bundle
savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Button Eventbutton;
Eventbutton = view.findViewById(R.id.event_button);
Eventbutton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
callbackSendData.sendDataEvent("send Data sample");
}
});
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
try{
callbackSendData = (CallbackSendData) context;
}catch (ClassCastException e){
e.printStackTrace();
}
}
}
Et maintenant, vous pouvez utiliser le CallbackSendData pour envoyer les données de l'activité au fragment.
Remarque: C'est beaucoup plus facile si vous utilisez l'injection de dépendances dans votre projet, vous pouvez utiliser la bibliothèque Dagger2.
Bonne chance.
Pour communiquer entre un Fragment
et un Activity
(que ce soit entre leurs présentateurs ou leurs classes), vous avez besoin d'une interface que votre activité implémente (comme ShoppingInteractor
).
De cette façon, vous pouvez appeler ((ShoppingInteractor)getActivity()).doSomething()
Dans les fragments. Si vous souhaitez que le présentateur de votre activité gère la tâche, vous devez appeler le présentateur dans le doSomething
à l'intérieur de l'activité.
Vous pouvez faire de même avec les fragments avec une autre interface et appeler l'interacteur du fragment à l'intérieur de l'activité.
Vous pouvez même avoir une Presenter getPresenter()
à l'intérieur de ces interfaces pour avoir accès au présentateur réel. (((ShoppingInteractor)getActivity()).getPresenter().sendData(data)
). Il en va de même pour les fragments.
Données dynamiques:
Voici un exemple utilisant rxjava2, dagger2 et moxy.
Conditionnalités:
La solution est similaire à l'EventBus, mais utilise à la place Subject avec une durée de vie limitée. C'est dans le composant qui est créé au début de l'activité et qui est détruit à la fin. L'activité et les fragments y ont un accès implicite, ils peuvent changer la valeur et y répondre à leur manière.
Exemple de projet: https://github.com/Anrimian/ViewPagerMvpExample
Données statiques:
Utilisez simplement des arguments dans le fragment et c'est tout.
Si vous souhaitez utiliser MVP, la première étape consiste à créer un présentateur pour chaque vue, je veux dire, si vous avez 3 fragments, alors il y aurait 3 présentateurs. Je pense que c'est une mauvaise idée de créer un présentateur pour 4 vues (activité et 3 fragments).