web-dev-qa-db-fra.com

Puis-je enregistrer MVP Presenter dans Fragment

J'ai suivi le modèle de conception MVP fourni par Google pour refactoriser mon application. J'ai une activité principale et de nombreux fragments et il semble peu compliqué pour moi de créer une activité pour chaque fragment, j'ai donc pensé à enregistrer le présentateur dans un fragment. Ce que je vois, c'est que chaque fragment enregistre son propre présentateur, mais je ne sais pas à quel point c'est faux ... :)

Voici donc mon présentateur:

public class FirstPresenter implements FirstContract.Presenter {
    private final FirstContract.View mView;

    public FirstPresenter(FirstContract.View view) {
        mView = view;
    }

    @Override
    public void start() {
        Log.e(TAG, "Start");
    }
}

Et voici mon fragment:

public class FirstFragment extends Fragment implements FirstContract.View {
    private FirstContract.Presenter mPresenter;

@Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container
            , Bundle savedInstanceState) {
...
// I register firstFragment's presenter here.
mPresenter = new FirstPresenter(this);
...

Donc ma question est, est-ce la bonne façon? Puis-je inscrire Presenter dans Fragment à la place dans Activity? Et si ce n'est pas la bonne façon, existe-t-il un bon exemple pour gérer MVP avec une seule activité et plusieurs fragments?

Merci les gars, BR!

12
MilanNz

Comme vous pouvez le voir dans les exemples de Google ( https://github.com/googlesamples/Android-architecture ), Activities créer Presenters. Views s'attache également à Activity et Presenters obtient des vues (Fragments) comme paramètre.

Après Fragment transaction validée ou Fragment (vue) état restauré Presenters se crée et prend Fragments (vues) comme paramètre que l'appel

view.setPresenter(T presenter); 

méthodes de vues et Presenters s'enregistrent pour voir.

Je pense que la création de Presenter dans Fragment n'est pas une bonne pratique. Ce sont d'abord des calques séparés . Ceci est illégal pour Séparation des préoccupations . Et deuxièmement, si vous créez un présentateur dans Fragment, vous liez la vie de votre présentateur à la vue LifeCycle et lorsque Fragment est détruit et recréé, vous créez un nouveau présentateur mais ils sont différentes couches.

Le modèle est une interface définissant les données à afficher ou à traiter dans l'interface utilisateur.

Le présentateur agit sur le modèle et la vue. Il récupère les données des référentiels (le modèle) et les formate pour les afficher dans la vue.

La vue est une interface passive qui affiche les données (le modèle) et achemine les commandes utilisateur (événements) au présentateur pour agir sur ces données.

Ainsi, Activity peut agir comme overall controller qui crée les Presenters et Views et les connecte.

enter image description here

Si nous parlons de votre question, oui, vous pouvez enregistrer le présentateur en fragment. Mais vous devez éviter de créer des présentateurs dans des fragments que vous utilisez comme vue.

Mais il y a beaucoup d'approches différentes sur le modèle MVP dans Android comme ci-dessous. https://plus.google.com/communities/114285790907815804707

Pourquoi les activités ne sont-elles pas des éléments d'interface utilisateur? http://www.techyourchance.com/activities-Android/

18
savepopulation

Si vous utilisez une seule activité pour héberger plusieurs fragments et que vous utilisez également Dagger 2 pour injecter le présentateur dont vous avez besoin, vous pouvez injecter chaque présentateur dans chaque fragment directement.

Mon cas d'utilisation

Je fais un projet avec l'architecture depuis quelques mois depuis que j'ai découvert Android Jetpack Navigation Component, j'ai commencé à migrer toutes mes vues d'application dans ce modèle.

Donc, je suis tombé sur beaucoup de refactoring faisant le processus et j'étais dans cette situation de ne pas savoir quoi faire avec cette situation.

Puisque j'ai utilisé Dagger 2 depuis le point de départ pour injecter mes présentateurs dans mes activités, cela ne changera pas grand-chose en faisant de même mais avec des fragments.

Je suis tombé sur le même référentiel pour vérifier comment l'architecture était censée suivre avec Fragments, ce qui est en effet un bon moyen de faire l'instanciation du présentateur dans l'activité hôte si vous n'avez qu'un seul fragment en tant qu'enfant.

Le fait est que si j'ai besoin de plusieurs fragments à l'intérieur d'une activité hôte, je devrais créer une instance de chaque présentateur et la passer par mon FragmentManager à l'intérieur de chaque fragment et que je pense que ce n'est pas ce que je regardais car il ajoute plusieurs instanciations de la présentateur de l'activité hôte.

Cela conduit à un cas, ayant plusieurs instances à l'intérieur de mon activité d'hôte pour tous les présentateurs, et également quelques interfaces pour gérer le détachement des travaux/vues si nécessaire.

Une façon simple de le faire avec plusieurs fragments est simplement de ne pas penser à l'activité de l'hôte et d'injecter les présentateurs à l'intérieur de chaque fragment lui-même.

Depuis que vous faites cela avec Dagger, cela rend l'injecteur plus propre.

Jetez un oeil à un exemple simple

class MainMenuActivity : BaseActivity(){

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        inflateMainFragment(savedInstanceState)
    }

      override fun getLayout(): Int {
        return R.layout.activity_main_menu
    }

    fun inflateMainFragment(savedInstanceState: Bundle?){
        if (savedInstanceState == null) {
            val fragment = MainMenuFragment()
            supportFragmentManager
                .beginTransaction()
                .add(R.id.nav_Host_fragment, fragment)
                .commit()
        }
    }
}

Comme vous pouvez le voir, ici, je n'ai aucune instanciation de présentateurs dont toute ma navigation devrait avoir besoin. Au lieu de cela, j'injecte simplement chaque présentateur dont j'ai besoin à l'intérieur de chaque fragment

class MapsFragment: BaseMapFragment(), MapContract.MapView {

    private lateinit var mMap: GoogleMap

    @Inject
    lateinit var presenter: MapsPresenter

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.fragment_paseo,container,false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        (requireActivity().application as YawpApplication).getAppComponent()?.inject(this)
        presenter.attachView(this)
        setupToolbar()
        setupMap()
    }
}

Et en utilisant le cycle de vie Fragments, vous pouvez détacher toutes vos vues Fragments dans la méthode onDestroyView(), et également économiser de l'espace mémoire lorsque le garbage collector s'exécute.

 override fun onDestroyView() {
        super.onDestroyView()
        presenter.detachView()
        presenter.detachJob()
    }

J'ai trouvé au repo officiel de Google une question qui m'a aidé à mieux la comprendre.

Vous pouvez le vérifier ici

1
Gastón Saillén