Autore Topic: [medio] Rotazione dello schermo e onCreate: problema e soluzioni  (Letto 21968 volte)

Offline Qlimax

  • Moderatore globale
  • Utente senior
  • *****
  • Post: 757
  • Respect: +202
    • Google+
    • _Qlimax
    • Mostra profilo
    • www.egsolutions.ch
  • Dispositivo Android:
    Galaxy Nexus - Nexus One - Wildfire - Magic
  • Play Store ID:
    egsolutions.ch
  • Sistema operativo:
    Ubuntu 12.04, Windows 7
+6
Livello di difficoltà: medio
Versione SDK utilizzata: >1.5
Link al file compresso del progetto eclipse: file in allegato

Nella realizzazione di interfacce su android vi sarete sicuramente accorti che, quando ruotiamo di 90° il nostro dispositivo, l'activity viene distrutta e successivamente ricreata.

Quando si ruota lo schermo il comportamento di default del sistema operativo android è quello di chiamare in successione onSaveInstanceState, onPause, onStop, onDestroy (insomma, un normale shutdown dell'activity) ed infine viene chiamato onCreate per ricreare l'activity.
(ActivityLifecycle)

Come è facile dedurre, il metodo onCreate non è il migliore punto per eseguire le nostre inizializzazioni, in quanto probabilmente non vorremo che i nostri dati vengano reinizializzati ogni volta che ruotiamo lo schermo.

Lo scopo di questo tutorial è quello di fornire delle indicazioni su come e in quale modo possiamo eseguire le operazioni di inizializzazione senza aver problemi al momento della rotazione dello schermo.

Esistono varie soluzioni al problema:
1)
La prima possibilità che abbiamo è quella di rimuovere il problema alla radice:
Facciamo in modo che quando ruotiamo lo schermo, il nostro layout non reagisca e rimanga portrait o landscape. Un orientamento fisso.
Diciamo che questa non è proprio una soluzione :P ma ad ogni modo, se vogliamo implementarla, dobbiamo aggiungere l'attributo screenOrientation all'elemento activity (ogni activity ha la sua configurazione):
Codice (XML): [Seleziona]
...
<activity
android:name=".Demo"
android:label="@string/app_name"
android:screenOrientation="portrait">
...
In questo modo l'activity è forzata ad usare soltanto l'orientamento portrait. Chiaramente possiamo impostare l'activity in modo da restare landscape.



Veniamo a soluzioni un po' più raffinate :)
Prendiamo un esempio pratico: vogliamo che quando la nostra applicazione parte, una variabile "data" viene inizializzata e una textView viene impostata con il suo valore.
Quando giriamo lo schermo vogliamo che questo valore rimanga lo stesso e sia sempre presente a schermo.

Ok, volendo possiamo dare un valore di default alla variabile inizializzandola a livello di dichiarazione della variabile come field della classe. Ma questo non è lo scopo... la maggior parte delle inizializzazioni non possono essere eseguite fuori da un metodo (ad esempio l'aggiunta di un elemento un un arraylist).

2)
Il framework ci da la possibilità di rendere sensibile un'activity ad un cambio di configurazione, in pratica ad ogni cambio di configurazione (tipo di cambio impostabile) il metodo onConfiguratioChanged verrà chiamato al posto della "sequenza di shutdown" citata in precendeza.
Procediamo modificando il manifest aggiungento un attributo configChange alle activity sulle quali vogliamo applicare questo metodo:

Codice (XML): [Seleziona]
...
<activity
android:name=".Demo"
android:label="@string/app_name"
android:configChanges="orientation|keyboardHidden">
...
Nota: a partire dalle API Level 13, va aggiunto anche screenSize alle configChanges

In questo modo il metodo onConfigurationChanged verrà chiamato quando l'orientamento del nostro dispositivo cambierà oppure quando estraiamo/nascondiamo la tastiera fisica (se presente ovviamente). All'interno del nostro codice potremmo lavorare nel modo seguente:

Codice (Java): [Seleziona]
public class Demo extends Activity {
        TextView tv;
        GregorianCalendar date;
        SimpleDateFormat sdf;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.v("Demo", "onCreate");            
        //facciamo le nostre inizializzazioni
        date=new GregorianCalendar();  
        sdf=new SimpleDateFormat("dd/MM/yy HH:mm:ss");
       
        //impostiamo layout e views
        setUpViews();
             
    }
   
    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        Log.v("Demo", "onConfigurationChange");
        //impostiamo layout e views
        setUpViews();
    }
   
    private void setUpViews(){
        //tramite la struttura a cartelle layout/layout-land verrà impostato il layout opportuno
        setContentView(R.layout.main);
        tv=(TextView)findViewById(R.id.myTv);
        tv.setText("Applicazione partita al: "+sdf.format(date.getTime()));
       
        //qui andrebbero impostati anche i vari listener
    }
}



Vediamo ora invece un altra possibilità che abbiamo per gestire questo tipo di situazioni.
A mio modo di vedere questa è la migliore :)

3)
Creiamo una classe che estende Application, facciamo un override del metodo onCreate e aggiungiamo questa classe al manifest.
Il metodo onCreate sulla classe appena creata, sarà il primo metodo chiamato quando facciamo partire la nostra applicazione.
Questo risulta essere il punto adatto per eseguire tutte le inizializzazioni di cui necessitiamo. Esponiamo questa classe tramite un Singleton, di modo da poter accedere ai nostri dati. Incapsuliamo questi dati tramite dei "getters" e dei "setters":

Codice (Java): [Seleziona]
public class MyApplication extends Application {
       
       
        private GregorianCalendar date;
       
       
        //singleton design pattern
        static MyApplication instance; 
        public static MyApplication getInstance(){
                if(instance==null){
                        Log.v("MyApplication", "instance created");
                        instance=new MyApplication();
                }
                Log.v("MyApplication", "instance returned");
                return instance;
        }
       
       
        @Override
        public void onCreate() {
                super.onCreate();
                Log.v("MyApplication", "onCreate");
               
                MyApplication myApp=getInstance();
                myApp.setDate(new GregorianCalendar());
               
        }
       
        public void setDate(GregorianCalendar date) {
                this.date = date;
        }
       
        public GregorianCalendar getDate() {
                return date;
        }

}

Codice (XML): [Seleziona]
...
<application
android:name=".MyApplication"
android:icon="@drawable/icon"
android:label="@string/app_name">
...

ora nella nostra applicazione non dovremo piu' preoccuparci degli inconvenienti legati alla rotazione e al richiamo di onCreate, in quanto l'accesso ai dati viene fatto tramite dei "getters" sul modello dati creato nella classe MyApplication. Questi dati potranno essere stati appena inizializzati dal onCreate di Application oppure già modificati tramite i "setters".

Codice (Java): [Seleziona]
public class Demo extends Activity {
       
        MyApplication myApp=MyApplication.getInstance();
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.v("Demo", "onCreate");              
        setContentView(R.layout.main);
       
       
       
        GregorianCalendar date=myApp.getDate();
        SimpleDateFormat sdf=new SimpleDateFormat("dd/MM/yy HH:mm:ss");
        TextView tv=(TextView)findViewById(R.id.myTv);
        tv.setText("Applicazione partita al: "+sdf.format(date.getTime()));
    }
}



Spero che tutto ciò vi sia utile :)

Un Saluto.
Qlimax
« Ultima modifica: 09 Gennaio 2013, 12:42:42 CET da Qlimax »

Offline PreStiige

  • Nuovo arrivato
  • *
  • Post: 43
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Nexus One, Motorola Milestone.
  • Sistema operativo:
    Snow Leopard 10.6
0
good job!!
"You think, I dev" [.cit PreStiige]

;)

Offline blackgin

  • Moderatore globale
  • Utente storico
  • *****
  • Post: 1387
  • Respect: +164
    • Google+
    • blackgins
    • blackginsoft
    • Mostra profilo
  • Dispositivo Android:
    Galaxy Nexus
  • Sistema operativo:
    Mac OSX 10.8
Re:[medio] Rotazione dello schermo e onCreate: problema e soluzioni
« Risposta #2 il: 21 Marzo 2010, 21:43:35 CET »
0
Ottimo, mi hai risolto un problema ;)
Postate il LogCat LogCat LogCat LogCat LogCat

Offline pivot

  • Nuovo arrivato
  • *
  • Post: 49
  • Respect: +1
    • Mostra profilo
  • Dispositivo Android:
    HTC Desire
  • Sistema operativo:
    Windows
Re:[medio] Rotazione dello schermo e onCreate: problema e soluzioni
« Risposta #3 il: 22 Marzo 2010, 10:24:53 CET »
0
Due domande e una considerazione da niubbo.

  • Relativamente al secondo metodo: ci sono anche altri eventi che distruggono e ricreano l'activity? Se così fosse dovrei gestire tutti gli eventi.
  • La soluzione con la classe Application non l'avevo ancora considerata. Quindi questa non viene mai distrutta? Quindi funziona anche non singleton?
  • La considerazione è: perché non usare semplicemente onSaveInstanceState, onPause, onStop, onDestroy che a me sembra più semplice e pulito e funziona per tutti gli eventi? Mi sa che mi sfugge qualcosa.

Bel Tutorial comunque, utilissimo!!

Offline icobasco

  • Nuovo arrivato
  • *
  • Post: 21
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Motorola Milestone
  • Play Store ID:
    Zirak
  • Sistema operativo:
    Windows XP
Re:[medio] Rotazione dello schermo e onCreate: problema e soluzioni
« Risposta #4 il: 22 Marzo 2010, 14:15:08 CET »
0
Grazie Qlimax ;)

Aggiungo che nella soluzione 1 non viene gestita l'apertura della tastiera (come invece fatto nel 2), che è evento diverso.
Quindi è vero che se volete gestire "solo" l'orientazione (orientamento?!? :P) dello schermo va bene la 1, però se sempre in portrait il vostro utente vi apre la tastiera (es: Milestone), la vostra Activity viene ricreata da zero.

@pivot
  • Direi solo rotazione e cambiamenti di configurazione...se non ricordo male.
  • La classe Application sarà la tua miglior amica quando devi tenerti a mente un sacco di cose e gestire l'orientazione. Nasce quando nasce l'app, muore con lei. Non ho capito l'ultima frase....
  • Come pulizia son d'accordo, ma quando hai a che fare con tanti parametri e anche tra activity non direttamente collegate, ti trovi nell'impossibilità di utilizzare questo metodo.

Offline Qlimax

  • Moderatore globale
  • Utente senior
  • *****
  • Post: 757
  • Respect: +202
    • Google+
    • _Qlimax
    • Mostra profilo
    • www.egsolutions.ch
  • Dispositivo Android:
    Galaxy Nexus - Nexus One - Wildfire - Magic
  • Play Store ID:
    egsolutions.ch
  • Sistema operativo:
    Ubuntu 12.04, Windows 7
Re:[medio] Rotazione dello schermo e onCreate: problema e soluzioni
« Risposta #5 il: 22 Marzo 2010, 14:25:39 CET »
0
pivot per rispondere brevemente alle tue domande:

Ci sono altre situazioni in cui la tua activity viene distrutta, ad esempio quando si trova in background e il sistema ha assolutamente bisogno di memoria.
è buona cosa tenersi sempre pronti ad un destroy dell'activity.

No la classe application non viene distrutta, solo le activity vengono distrutte.
è una mia scelta quella di esporla con un singleton, in questo modo i dati sono dispobilili anche all'asterno delle activity
se vuoi puoi recuperare la tua application con
Codice (Java): [Seleziona]
MyApplication myApp=(MyApplication)getApplication();ma il metodo getApplication appartiene alla classe activity. questo limita.


Certo hai ragione, puoi usare onSaveInstanceState e ricuperare il bundle come parametro di onCreate, solo che sei limitato con i Bundle -> tipi di dati primitivi
Nell' esempio ho usato GregorianCalendar e già questo non potrei salvarlo...ma penso a classi come ArrayList o altre classi molto usate.


Ad ogni modo apprezzo il tuo spirito costruttivo  :)




Offline Qlimax

  • Moderatore globale
  • Utente senior
  • *****
  • Post: 757
  • Respect: +202
    • Google+
    • _Qlimax
    • Mostra profilo
    • www.egsolutions.ch
  • Dispositivo Android:
    Galaxy Nexus - Nexus One - Wildfire - Magic
  • Play Store ID:
    egsolutions.ch
  • Sistema operativo:
    Ubuntu 12.04, Windows 7
Re:[medio] Rotazione dello schermo e onCreate: problema e soluzioni
« Risposta #6 il: 22 Marzo 2010, 14:32:55 CET »
0
Grazie Qlimax ;)

Aggiungo che nella soluzione 1 non viene gestita l'apertura della tastiera (come invece fatto nel 2), che è evento diverso.
Quindi è vero che se volete gestire "solo" l'orientazione (orientamento?!? :P) dello schermo va bene la 1, però se sempre in portrait il vostro utente vi apre la tastiera (es: Milestone), la vostra Activity viene ricreata da zero.


Ciao icobasco, mi fa piacere trovarti anche qui :)

Giusto, non avendo un telefono con tastiera fisica non ho notato questo particolare, aggiorno il tutorial :)


Offline icobasco

  • Nuovo arrivato
  • *
  • Post: 21
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Motorola Milestone
  • Play Store ID:
    Zirak
  • Sistema operativo:
    Windows XP
Re:[medio] Rotazione dello schermo e onCreate: problema e soluzioni
« Risposta #7 il: 22 Marzo 2010, 14:45:51 CET »
0
Ciao icobasco, mi fa piacere trovarti anche qui :)

Giusto, non avendo un telefono con tastiera fisica non ho notato questo particolare, aggiorno il tutorial :)

Ecco che si svela uno dei compagni frequentatori di anddev.org ;) Fa piacere anche a me trovarti qui.

Offline daniele

  • Nuovo arrivato
  • *
  • Post: 17
  • Respect: +1
    • Mostra profilo
Re:[medio] Rotazione dello schermo e onCreate: problema e soluzioni
« Risposta #8 il: 26 Marzo 2010, 11:46:03 CET »
0
Interessantissima guida, ora la provo subito.
Ragazzi continuate così.
Ciao.

Offline BobArctor

  • Nuovo arrivato
  • *
  • Post: 36
  • Respect: +2
    • Mostra profilo
  • Dispositivo Android:
    HTC Hero, Galaxy S4, GP50
  • Play Store ID:
    Michele Valentini
  • Sistema operativo:
    Windows 8
Re:[medio] Rotazione dello schermo e onCreate: problema e soluzioni
« Risposta #9 il: 13 Aprile 2010, 13:54:25 CEST »
0
fantastico!!! :D
ma questa classe Application posso usarla come "FacadeController"???
oltre che alla gestione dell'orientamento... :D

Offline Qlimax

  • Moderatore globale
  • Utente senior
  • *****
  • Post: 757
  • Respect: +202
    • Google+
    • _Qlimax
    • Mostra profilo
    • www.egsolutions.ch
  • Dispositivo Android:
    Galaxy Nexus - Nexus One - Wildfire - Magic
  • Play Store ID:
    egsolutions.ch
  • Sistema operativo:
    Ubuntu 12.04, Windows 7
Re:[medio] Rotazione dello schermo e onCreate: problema e soluzioni
« Risposta #10 il: 03 Maggio 2010, 17:21:50 CEST »
0
puoi usarla come modello dati o come meglio ti aggrada :P

io personalmente se dovessi implementare un Facade pattern utilizzerei una classe separata. per dividere un attimo le responsabilità tra classi.
Ad esempio se volessi un Facade per la gestione dei dati nel mio database, realizzerei una classe di facciata chiamandola DbUtilities ad esempio .

Ciao,
Qlimax

Offline zerocool87

  • Utente junior
  • **
  • Post: 131
  • Respect: +4
    • Mostra profilo
  • Dispositivo Android:
    Htc Legend
  • Sistema operativo:
    Ubuntu 10.04
Re:[medio] Rotazione dello schermo e onCreate: problema e soluzioni
« Risposta #11 il: 26 Agosto 2010, 16:20:39 CEST »
0
non mi è chiara una cosa.se mentre lo schermo ruota l'applicazione sta facendo delle operazioni (tipo un download da internet, un ciclo for, o quant'altro) queste operazioni vengono interrotte? esiste un modo che permette di ripartire dallo stesso punto? grazie

P.s. ottimo tutorial :D

Offline zerocool87

  • Utente junior
  • **
  • Post: 131
  • Respect: +4
    • Mostra profilo
  • Dispositivo Android:
    Htc Legend
  • Sistema operativo:
    Ubuntu 10.04
Re:[medio] Rotazione dello schermo e onCreate: problema e soluzioni
« Risposta #12 il: 26 Agosto 2010, 23:23:24 CEST »
0
non mi è chiara una cosa.se mentre lo schermo ruota l'applicazione sta facendo delle operazioni (tipo un download da internet, un ciclo for, o quant'altro) queste operazioni vengono interrotte? esiste un modo che permette di ripartire dallo stesso punto? grazie

P.s. ottimo tutorial :D


Modifico la domanda. se la mia activity sta mostrando una listview(creata dinamicamente) e ruota lo schermo?
L'activity riparte da zero. Io non ho capito come salvare la configurazione attuale dell'activity e quindi rimostrarla identica senza dover rifare tutte le operazioni che mi hanno permesso di ottenere i dati della listview.
Presumo che debba salvarmi i dati(metodo 3) e alla ricreazione dell'activity controllare se avevo già ricavato dati utilizzabili e inserirli nella listview. giusto???
« Ultima modifica: 26 Agosto 2010, 23:37:07 CEST da zerocool87 »

Offline zerocool87

  • Utente junior
  • **
  • Post: 131
  • Respect: +4
    • Mostra profilo
  • Dispositivo Android:
    Htc Legend
  • Sistema operativo:
    Ubuntu 10.04
Re:[medio] Rotazione dello schermo e onCreate: problema e soluzioni
« Risposta #13 il: 27 Agosto 2010, 01:19:27 CEST »
0
Ho risolto, ma non se è perfettamente giusto :P in pratica ho mischiato il secondo e il terzo metodo.
Quando ottengo dei dati li salvo tramite l'application e quando cambia la configurazione li recupero e ricreo gli elementi (ad esempio la listview) con i dati recuperati :)
è corretto oppure ho fatto una forzatura?
c'è solo il problema che non modifica l'aspetto in base all'orientazione (ho creato 2 versioni differenti di ogni xml, uno portrait ed uno landscape con dimensioni differenti). se nel metodo onConfigurationChanged inserisco setContentView(R.layout.act2); allora rimane lo schermo vuoto, se non lo inserisco mi compare la listview, ma con le dimensioni della configurazione portrait (anche se lo schermo è in orizzontale)
« Ultima modifica: 27 Agosto 2010, 01:48:46 CEST da zerocool87 »

Offline Qlimax

  • Moderatore globale
  • Utente senior
  • *****
  • Post: 757
  • Respect: +202
    • Google+
    • _Qlimax
    • Mostra profilo
    • www.egsolutions.ch
  • Dispositivo Android:
    Galaxy Nexus - Nexus One - Wildfire - Magic
  • Play Store ID:
    egsolutions.ch
  • Sistema operativo:
    Ubuntu 12.04, Windows 7
Re:[medio] Rotazione dello schermo e onCreate: problema e soluzioni
« Risposta #14 il: 27 Agosto 2010, 15:19:54 CEST »
0
Dopo setcontentview, devi ancora fare findviewbyid della listview, e reinizializzarla.