Els Fragment ens permetran disposar de diverses Activity alhora en la mateixa pantalla, a mode de pestanyes. Podrem navegar amb una NavigationBar que acostuma a tenir disposicions diferents segons la grandària i orientació de la pantalla, o del tipus de dispositiu.
Referències:
App amb fragments:
Bottom Navigation Activity.Fragment buit. Tria bé el nom (posarem d'exemple NewFragment) Botó dret -> New -> Fragment -> Fragment (Blank)
Fragment a la navegació (via XML):res -> navigation -> mobile_navigation.xml
Fragment al menú de la NavigationBar:res -> menu -> bottom_nav_menu.xml
MainActivity.java i afegir R.id.navigation_XXX al onCreate:AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder( R.id.navigation_home, R.id.navigation_dashboard, R.id.navigation_notifications, R.id.navigation_XXX) .build();
ActivityMain i sol deixar buida la part de dalt. Podeu mirar aquest post per eliminar-la (simplement eliminant el padding o posar-lo a 0).
Haureu vist que el NewFragment té només 1 sol arxiu, mentre que els Fragment creats a la plantilla (Home, Dashboard i Notifications) en tenen 2. Això és degut a que, acompanyant el Fragment, també tenim un ViewModel. Aquest objecte ViewModel ens serveix per facilitar la persistència dels canvis en el GUI sense haver d'utilitzar accés a fitxers o altres sistemes d'emmagatzemament.
No és necessari utilitzar el ViewModel, tot i que pot facilitar molta gestió, com per exemple quan girem (rotate) el mòbil de format portrait a landscape, moment en el qual es destrueixen les views i es tornen a crear de nou amb les noves mides i, per tant, perdem les dades del GUI.
Un ViewModel ens permetrà connectar la View amb el ViewModel mitjançant la sentència observe, que prové d'un patró de disseny Observer. Aquest és el codi que ve per defecte a la plantilla:
homeViewModel.text.observe(viewLifecycleOwner) { textView.text = it }
homeViewModel.getText().observe(getViewLifecycleOwner(), textView::setText);
Afegeix un Button i un TextView al HomeFragment. Hauriem de tenir 2 TextView, un AMB ViewModel (el què ja existia) i un sense.
Implementa aquesta funcionalitat simple: quan premem el botó, canvia el text dels dos TextView, però cal fer-ho diferent en l'un i l'altre:
TextView AMB ViewModel cal modificar el ViewModel.TextView SENSE ViewModel el modifiquem directament.Algo així:
_binding!!.button.setOnClickListener(View.OnClickListener { homeViewModel.setText("yeah") })
Cal afegir setText al ViewModel. Per canviar el valor del model _text caldrà fer un postValue(), el qual s'encarregarà de notificar els observer que hi hagi subscrits:
class HomeViewModel : ViewModel() { private val _text = MutableLiveData<String>().apply { value = "This is home Fragment" } var text: LiveData<String> = _text fun setText(newtext: String) { _text.postValue(newtext) } }
homeButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { // Modifiquem objecte sense ViewModel directament TextView directTextView = root.findViewById(R.id.directTextView); directTextView.setText("[NOT ViewModel] canvi de text"); // Modifiquem contingut ViewModel (es reflaxarà a la View) homeViewModel.setText("[WITH ViewModel] canvi de text"); } });
Ara posa l'app en marxa, habilita el autorotate de l'emulador i observa què passa.
Com expliques aquest comportament?