{"id":12861,"date":"2018-05-21T16:33:11","date_gmt":"2018-05-21T14:33:11","guid":{"rendered":"http:\/\/www.devapp.it\/wordpress\/?p=12861"},"modified":"2018-05-21T16:33:11","modified_gmt":"2018-05-21T14:33:11","slug":"android-fragments-sperimentiamo-il-ciclo-di-vita","status":"publish","type":"post","link":"https:\/\/www.devapp.it\/wordpress\/android-fragments-sperimentiamo-il-ciclo-di-vita\/","title":{"rendered":"Android Fragments: sperimentiamo il ciclo di vita"},"content":{"rendered":"<p>I Fragments, come abbiamo gi\u00e0 ricordato, sono una caratteristica potente di Android per la creazione di interfacce utente dinamiche e riutilizzabili ma spesso nascondono delle insidie. Buona parte di queste trappole si celano nel ciclo di vita, articolato ma non difficile da padroneggiare se approcciato con la necessaria cura: di questo tratta il nostro post.<\/p>\n<h2>Le fasi del ciclo di vita<\/h2>\n<p>Il diagramma che segue mostra le fasi del ciclo di vita di un Fragment messe a confronto con quelle dell&#8217;Activity:<\/p>\n<p><a href=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2017\/02\/android-fragments-sperimentiamo-il-ciclo-di-vita.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-12886\" src=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2017\/02\/android-fragments-sperimentiamo-il-ciclo-di-vita.png\" alt=\"\" width=\"340\" height=\"675\" srcset=\"https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2017\/02\/android-fragments-sperimentiamo-il-ciclo-di-vita.png 340w, https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2017\/02\/android-fragments-sperimentiamo-il-ciclo-di-vita-151x300.png 151w\" sizes=\"auto, (max-width: 340px) 100vw, 340px\" \/><\/a><\/p>\n<p>&nbsp;<\/p>\n<p>Quello che \u00e8 mostrato \u00e8 tutta l&#8217;esistenza di un Fragment da quando \u00e8 collegato all&#8217;interfaccia a quando viene (eventualmente) distrutto.Al centro (tra i metodi <em>onResume<\/em> e <em>onPause<\/em>) si inserisce la fase <em>running<\/em>, l&#8217;interazione con l&#8217;utente.<\/p>\n<p><strong>Un fragment pu\u00f2 esistere solo all&#8217;interno di una Activity<\/strong>\u00a0per questo il ciclo di vita dell&#8217;uno e dell&#8217;altra si innestano.<\/p>\n<p>Un&#8217;Activity, da quando viene creata a quando diventa <em>running<\/em>, passa per tre metodi di callback (<em>onCreate<\/em>, <em>onStart<\/em>, <em>onResume<\/em>) e, se contiene fragments, la creazione della sua interfaccia utente consiste proprio nell&#8217;istanziazione di questi. I cicli di vita andranno di pari passo:\u00a0parallelamente, anche il fragment invocher\u00e0 <em>onCreate<\/em>, <em>onStart<\/em>, <em>onResume<\/em>. La differenza rispetto all&#8217;Activity \u00e8 che il fragment invoca altri suoi metodi specifici:<\/p>\n<ul>\n<li><strong>onAttach<\/strong>: segna il momento in cui il fragment \u00e8 collegato all&#8217;Activity, questa \u00a0viene passata al metodo come Context. E&#8217; il punto ideale per salvare un riferimento al Context valido da usare durante la vita del Fragment;<\/li>\n<li><strong>onCreateView<\/strong>: viene qui prodotto il layout dell&#8217;interfaccia che verr\u00e0 restituito. Tra i parametri in ingresso riceve un LayoutInflater che servir\u00e0 proprio all&#8217;elaborazione di un layout XML;<\/li>\n<li><strong>onActivityCreated<\/strong> annuncia che la creazione dell&#8217;Activity \u00e8 completa, da questo momento in poi il Fragment potr\u00e0 interagire con essa.<\/li>\n<\/ul>\n<p>Quando un&#8217;Activity smette di interagire con l&#8217;utente verr\u00e0 invocato <em>onPause<\/em>, quando non \u00e8 pi\u00f9 visibile <em>onStop<\/em> fino alla sua (eventuale) distruzione che verr\u00e0 segnalata da <em>onDestroy<\/em>. I metodi omonimi dei fragment hanno gli stessi ruoli mentre i due specifici <strong>onDestroyView<\/strong> e <strong>onDetach<\/strong> segnalano, rispettivamente, la rimozione dell&#8217;interfaccia utente e lo scollegamento dall&#8217;Activity.<\/p>\n<h2>L&#8217;esempio<\/h2>\n<p>L&#8217;esempio che presentiamo ha la finalit\u00e0 principale di stimolare la riflessione sul ciclo di vita dei Fragments. Abbiamo <strong>un&#8217;Activity contenente\u00a0due Fragments.<\/strong><\/p>\n<p>Il <strong>primo Fragment<\/strong> ha nell&#8217;interfaccia un pulsante che serve ad aprire il secondo:<a href=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2017\/02\/android-fragments-lifecycle-activity_img_2.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-12865\" src=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2017\/02\/android-fragments-lifecycle-activity_img_2.jpg\" alt=\"\" width=\"370\" height=\"403\" srcset=\"https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2017\/02\/android-fragments-lifecycle-activity_img_2.jpg 370w, https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2017\/02\/android-fragments-lifecycle-activity_img_2-275x300.jpg 275w\" sizes=\"auto, (max-width: 370px) 100vw, 370px\" \/><\/a><\/p>\n<p>Il secondo Fragment\u00a0mostra un contatore numerico ed un pulsante che serve per tornare al primo.<\/p>\n<p><a href=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2017\/02\/android-fragments-lifecycle-activity_img_3.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-12866\" src=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2017\/02\/android-fragments-lifecycle-activity_img_3.jpg\" alt=\"\" width=\"365\" height=\"426\" srcset=\"https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2017\/02\/android-fragments-lifecycle-activity_img_3.jpg 365w, https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2017\/02\/android-fragments-lifecycle-activity_img_3-257x300.jpg 257w\" sizes=\"auto, (max-width: 365px) 100vw, 365px\" \/><\/a><\/p>\n<p>Ogni volta che viene aperto il secondo Fragment il contatore viene incrementato di uno: su questo verte buona parte dell&#8217;esempio in quanto solo il corretto riutilizzo dei Fragment permetter\u00e0 al contatore di essere incrementato e di non ri-inizializzarsi ogni volta mostrando sempre il numero 1.<\/p>\n<h2>Il codice<\/h2>\n<p>I Fragment si alterneranno in un FrameLayout che costituisce il<strong> layout dell&#8217;Activity<\/strong>:<\/p>\n<pre class=\"lang:xhtml decode:true\">&lt;FrameLayout\r\n    xmlns:android=\"http:\/\/schemas.android.com\/apk\/res\/android\"\r\n    android:id=\"@+id\/activity_main\"\r\n    android:layout_width=\"match_parent\"\r\n    android:layout_height=\"match_parent\"\/&gt;<\/pre>\n<p>Nel metodo <em>onCreate<\/em> dell&#8217;Activity viene caricato il layout e con la\u00a0FragmentTransaction viene aggiunto il primo Fragment:<\/p>\n<pre class=\"lang:java decode:true \"> protected void onCreate(Bundle savedInstanceState) {\r\n        super.onCreate(savedInstanceState);\r\n        setContentView(R.layout.activity_main);\r\n\r\n        getSupportFragmentManager()\r\n                .beginTransaction()\r\n                .add(R.id.activity_main, new MainFragment(), MainFragment.TAG)\r\n                .commit();\r\n    }<\/pre>\n<p>Il codice del primo Fragment \u00e8 molto semplice, crea solo l&#8217;interfaccia in <em>onCreateView<\/em>. Se non sono richieste funzionalit\u00e0 particolari, ci\u00f2 pu\u00f2 bastare anche in Fragment pi\u00f9 complessi:<\/p>\n<pre class=\"lang:java decode:true \">public class MainFragment extends Fragment {\r\n\r\n    public final static String TAG=\"LC_PRIMO_FRAGMENT\";\r\n\r\n    public MainFragment() {\r\n        \/\/ Required empty public constructor\r\n    }\r\n\r\n    @Override\r\n    public View onCreateView(LayoutInflater inflater, ViewGroup container,\r\n                             Bundle savedInstanceState) {\r\n        return inflater.inflate(R.layout.fragment_main, container, false);\r\n    }\r\n\r\n}<\/pre>\n<p>Il<strong> layout del primo Fragment<\/strong> contiene un solo pulsante:<\/p>\n<pre class=\"lang:xhtml decode:true\">&lt;RelativeLayout\r\n    xmlns:android=\"http:\/\/schemas.android.com\/apk\/res\/android\"\r\n    android:layout_width=\"match_parent\"\r\n    android:layout_height=\"match_parent\"&gt;\r\n\r\n    &lt;Button\r\n        android:layout_width=\"wrap_content\"\r\n        android:layout_height=\"wrap_content\"\r\n        android:layout_centerInParent=\"true\"\r\n        android:text=\"Passa al secondo Fragment\"\r\n        android:onClick=\"passaAlSecondo\"\/&gt;\r\n\r\n&lt;\/RelativeLayout&gt;<\/pre>\n<p>il suo click invocher\u00e0 il metodo <em>passaAlSecondo<\/em> dell&#8217;Activity:<\/p>\n<pre class=\"lang:java decode:true\">public void passaAlSecondo(View v)\r\n    {\r\n        SecondFragment sf= (SecondFragment) getSupportFragmentManager().findFragmentByTag(SecondFragment.TAG);\r\n        if (sf==null)\r\n            sf=new SecondFragment();\r\n\r\n        getSupportFragmentManager()\r\n                .beginTransaction()\r\n                .addToBackStack(null)\r\n                .replace(R.id.activity_main, sf, SecondFragment.TAG)\r\n                .commit();\r\n    }<\/pre>\n<p>Piuttosto che crearlo da zero, prima <strong>verifichiamo se\u00a0un&#8217;istanza del secondo fragment \u00e8 conservata nel FragmentManager<\/strong>. Lo possiamo riconoscere dal TAG associatogli durante il <em>replace<\/em> della FragmentTransaction.<\/p>\n<p>ll SecondFragment ha un layout che non stupisce (file \/res\/layout\/fragment_second.xml):<\/p>\n<pre class=\"lang:xhtml decode:true\">&lt;RelativeLayout\r\n    xmlns:android=\"http:\/\/schemas.android.com\/apk\/res\/android\"\r\n    android:layout_width=\"match_parent\"\r\n    android:layout_height=\"match_parent\"&gt;\r\n\r\n    &lt;LinearLayout\r\n        android:layout_width=\"wrap_content\"\r\n        android:layout_height=\"wrap_content\"\r\n        android:padding=\"10dp\"\r\n        android:layout_centerInParent=\"true\"\r\n        android:orientation=\"vertical\"&gt;\r\n\r\n        &lt;TextView\r\n            android:layout_width=\"wrap_content\"\r\n            android:layout_height=\"wrap_content\"\r\n            android:text=\"0\"\r\n            android:layout_gravity=\"center_horizontal\"\r\n            android:textStyle=\"bold\"\r\n            android:textSize=\"25sp\"\r\n            android:id=\"@+id\/conteggio\"\/&gt;\r\n\r\n        &lt;Button\r\n            android:layout_width=\"wrap_content\"\r\n            android:layout_height=\"wrap_content\"\r\n            android:layout_marginTop=\"30dp\"\r\n            android:onClick=\"tornaAlPrimo\"\r\n            android:text=\"Torna al primo Fragment\" \/&gt;\r\n\r\n    &lt;\/LinearLayout&gt;\r\n\r\n\r\n&lt;\/RelativeLayout&gt;<\/pre>\n<p>e nella classe Java che lo rappresenta definiamo il layout in <em>onCreateView<\/em>\u00a0mentre in <em>onResume<\/em> incrementiamo la variabile che tiene il conto delle visualizzazioni e ne inserisce il valore nella TextView.<\/p>\n<pre class=\"lang:java decode:true\">public class SecondFragment extends Fragment {\r\n\r\n    public final static String TAG=\"LC_SECONDO_FRAGMENT\";\r\n\r\n    private Integer conta=0;\r\n\r\n    public SecondFragment() {\r\n        \/\/ Required empty public constructor\r\n    }\r\n\r\n    @Override\r\n    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\r\n        return inflater.inflate(R.layout.fragment_second, container, false);\r\n    }\r\n\r\n    @Override\r\n    public void onResume() {\r\n        super.onResume();\r\n        TextView txt= (TextView) getView().findViewById(R.id.conteggio);\r\n        conta++;\r\n        txt.setText(conta.toString());\r\n    }\r\n}<\/pre>\n<p><strong>Senza riciclare il Fragment, il conteggio ripartirebbe sempre da zero,<\/strong>\u00a0non mostrando mai incrementi.<\/p>\n<p>Cliccando il pulsante presente nel secondo fragment viene invocato il metodo <em>tornaAlPrimo<\/em> dell&#8217;Activity:<\/p>\n<pre class=\"lang:java decode:true \">public void tornaAlPrimo(View v)\r\n    {\r\n        MainFragment sf= (MainFragment) getSupportFragmentManager().findFragmentByTag(MainFragment.TAG);\r\n        if (sf==null)\r\n            sf=new MainFragment();\r\n        getSupportFragmentManager()\r\n                .beginTransaction()\r\n                .addToBackStack(null)\r\n                .replace(R.id.activity_main, sf, MainFragment.TAG)\r\n                .commit();\r\n    }<\/pre>\n<p>Anche in questo caso <strong>tentiamo di riciclare il Fragment gi\u00e0 usato\u00a0<\/strong>(anche se non si devono conservare dati \u00e8 comunque una buona prassi) e poi eseguiamo il <em>replace<\/em> nella FragmentTransaction. Notare che visto che la transazione con cui viene caricato il secondo fragment \u00e8 caricato nel BackStack sarebbe stato sufficiente in questo metodo chiamare <em>popBackStack<\/em> che inverte l&#8217;ultima transazione eseguita. Ci\u00f2 per\u00f2 comporterebbe che il Fragment dismesso (il secondo in questo caso) verrebbe del tutto distrutto fino alla chiamata del metodo <em>onDetach<\/em>: la conseguenza sarebbe una creazione da zero alla riapertura del secondo fragment con il conteggio che\u00a0ricomincerebbe.<\/p>\n<h2>Ricapitolando&#8230;<\/h2>\n<p>In questo articolo, abbiamo visto il ciclo di vita dei Fragments ma non ci siamo limitati a elencare le fasi che lo compongono. L&#8217;esempio che abbiamo trattato utilizza l&#8217;espediente del conteggio\u00a0allo scopo di invitare il programmatore al riciclo dei Fragments. Come \u00e8 stato svolto &#8211; utilizzando sempre <em>replace<\/em>\u00a0nonch\u00e8 l&#8217;impiego dei tag per il recupero di Fragment gi\u00e0 creati ma evitando l&#8217;uso di <em>popBackStack<\/em> \u00a0&#8211; <strong>il ciclo di vita nella fase che segue il running non va mai oltre il metodo <em>onDestroyView<\/em><\/strong>, smantellando quindi l&#8217;interfaccia utente ma senza distruggere il componente. Conseguenza di ci\u00f2, al momento di ripristinare un fragment gi\u00e0 visitato non ripartiamo da <em>onAttach<\/em> bens\u00ec da <em>onCreateView<\/em> ed il nostro conteggio sar\u00e0 salvo.<\/p>\n<p>I Fragment vanno usati con accortezza, solo in questo modo potremo sprigionare le loro potenzialit\u00e0 senza incappare in errori: la tematica \u00e8 interessante e piena di implicazioni, non finiremo certo qui di trattarla.<\/p>\n<p>Alla prossima!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I Fragments, come abbiamo gi\u00e0 ricordato, sono una caratteristica potente di Android per la creazione di interfacce&#8230;<\/p>\n","protected":false},"author":561,"featured_media":13569,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1682,1],"tags":[1278,1569,1897],"class_list":["post-12861","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-android","category-tutorial-pratici","tag-android","tag-design-user-interface-android","tag-fragmets"],"acf":[],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts\/12861","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/users\/561"}],"replies":[{"embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/comments?post=12861"}],"version-history":[{"count":10,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts\/12861\/revisions"}],"predecessor-version":[{"id":13570,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts\/12861\/revisions\/13570"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/media\/13569"}],"wp:attachment":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/media?parent=12861"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/categories?post=12861"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/tags?post=12861"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}