Autore Topic: [facile] Mantenere un ProgressDialog dopo la rotazione del dispositivo  (Letto 5991 volte)

Offline zipgenius

  • Utente junior
  • **
  • Post: 80
  • Respect: +17
    • matteoriso
    • Mostra profilo
    • ZipGenius
  • Dispositivo Android:
    Samsung Galaxy S
  • Play Store ID:
    Wininizio.it Software
  • Sistema operativo:
    Windows 7
Livello di difficoltà: facile
Target SDK: 4
Min SDK: 4
Link al file compresso del progetto eclipse: file in allegato

Uno dei problemi più grossi per chi sviluppa applicazioni Android è il dover gestire la possibilità che l'utente finale ruoti il proprio dispositivo con l'intenzione di usare un'applicazione in modalità "landscape" piuttosto che in modalità "portrait" e viceversa. Per quanto semplice possa apparire la programmazione per Android, il dover affrontare questo problema spesso induce alcuni sviluppatori ad adottare soluzioni di fortuna come la forzatura dell'orientamento dell'activity, in modo che questa sia visualizzata solo in verticale o solo in orizzontale. Una soluzione del genere non è affatto la migliore poiché i dispositivi Android sono per la maggior parte dotati di schermi che, ruotati in orizzontale, offrono un'ampiezza di visualizzazione simile ai monitor "wide" per PC desktop, quindi anche un maggior spazio per il programmatore e per la UI delle applicazioni.
Tuttavia la gestione della rotazione dello schermo pone alcuni problemi sia di ordine tecnico che di ordine estetico. Nel momento in cui l'orientamento dello schermo cambia da portrait a landscape, l'activity viene distrutta e ricreata nella nuova modalità e non è un caso se le applicazioni Android possono sfruttare un layout specifico per la modalità portrait e uno specifico per la modalità landscape.
La distruzione e ricostruzione dell'activity ha come conseguenza principale la distruzione di tutto ciò che all'activity è connesso: ecco, quindi, che se un EditText conteneva un testo, questo apparirà vuoto dopo la rotazione; tuttavia la condizione della UI prima della rotazione può essere mantenuta facilmente usando la persistenza dei dati (esiste un tutorial specifico che illustra come fare), mentre si pone un enorme problema nel momento in cui si desidera far proseguire un'operazione durante il processo di rotazione del dispositivo.
Normalmente si è soliti mostrare l'avanzamento di un'operazione attraverso un ProgressDialog che il più delle volte è associato ad un AsyncTask per l'esecuzione effettiva dell'operazione. Purtroppo la distruzione dell'activity comporta anche la distruzione del ProgressDialog e dell'AsyncTask: l'utente dopo la rotazione vedrà ancora il ProgressDialog ma dopo qualhe attimo potrebbe incorrere nel blocco dell'applicazione se non addirittura nel "force close" della stessa.
Eppure la soluzione è affatto complicata e il mantenimento di un ProgressDialog dopo la rotazione del dispositivo può essere ottenuto molto facilmente.
Il trucco è nel dichiarare un ProgressDialog come private static e crearlo attraverso la funzione "protected Dialog onCreateDialog". E' ben noto, infatti, che un ProgressDialog può essere costruito anche direttamente nell'onClickListener di un bottone però, per l'illustrato processo di distruzione dell'activity, alla rotazione dello schermo il ProgressDialog verrà distrutto come codice e non come elemento della UI: da ciò ne deriveranno gli effetti indesiderati e imprevedibili come - appunto - il blocco dell'applicazione o il crash.

In questo esempio vedremo come affrontare il problema. Simuleremo una lunga operazione e faremo in modo che la rotazione del dispositivo non comporti l'interruzione del ProgressDialog. Il codice seguente usa due modalità per simulare l'operazione: nel primo caso ci serviremo dell'esecuzione via Handler (che si occuperà anche di aggiornare la barra di progressione), mentre nel secondo caso useremo il comodissimo metodo dell'AsyncTask.

Il codice è commentato quasi riga per riga al fine di spiegare meglio cosa fa quest'applicazione ma bisogna ricordare che la chiave di tutto risiede proprio nella funzione "onCreateDialog": senza questa non avremo mai un ProgressDialog capace di adeguarsi al cambio di orientamento.

Sorgenti:
ProgressDialogDemo.java
Codice (Java): [Seleziona]
package com.wisoft.pddemo;

import android.app.Activity;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;

public class ProgressDialogDemo extends Activity {

    private static ProgressDialog dialog; // Questo è il nostro "ProgressDialog"
    private static int mProgress; // Variabile contenente la progressione
    private static Handler mProgressHandler; // Handler per il thread che aggiornerà la progressbar nel primo esempio
   
    /** NOTA BENE!
     * Se non dichiaramo come STATIC il ProgressDialog, l'Handler e la variabile mProgress,
     * alla rotazione del dispositivo la progressione si bloccherà e il ProgressDialog
     * potrebbe avere reazioni inaspettate (come il crash).
     */

   
    // Vogliamo creare due dialoghi diversi, quindi due ID diversi
    private static final int DIALOG1_KEY = 0; // Questo identificherà il ProgressDialog guidato via Handler    
    private static final int DIALOG2_KEY = 1; // Questo identificherà il ProgressDialog guidato dall'AsyncTask

    // Scriviamo il codice per far creare il dialogo corrispondente
    // in funzione dell'ID inviato alla pressione di ciascun tasto.
    protected Dialog onCreateDialog(int id) {
       
        switch (id) {
            case DIALOG1_KEY: {
                dialog = new ProgressDialog(this);
                dialog.setTitle("Attendere prego..."); // Inseriamo un titolo
                dialog.setMessage("Caricamento in corso."); // Scriviamo un messaggio
                dialog.setIndeterminate(false); // Vogliamo una progressbar definita
                dialog.setMax(50); // Indichiamo il valore massimo della progressbar
                dialog.setProgress(0); // Per sicurezza...
                // Impostiamo lo stile della progressbar
                dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);                
                dialog.setCancelable(true); // Il ProgressDialog sarà cancellabile              
                return dialog;
            }
           
            case DIALOG2_KEY: {
                dialog = new ProgressDialog(this);
                // Creiamo il secondo tipo di dialogo, senza titolo nè tasto "Annulla"
                dialog.setMessage("Caricamento in corso.");
                dialog.setIndeterminate(false);
                dialog.setCancelable(true);
                dialog.setMax(50);
                dialog.setProgress(0); // Per sicurezza...
                dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
                return dialog;
            }
        }
        return null;
    }
   
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        Button button = (Button) findViewById(R.id.showIndeterminate);
        button.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {                
                showDialog(DIALOG1_KEY); // Vogliamo usare il dialog con titolo e bottone "Annulla"
                mProgress = 0; // Azzeriamo la progressbar
                dialog.setProgress(0); // ... e comunichiamoglielo!
                mProgressHandler.sendEmptyMessage(0); // Quindi inviamo un messaggio vuoto (iniziale) all'Handler
            }
        });
     
       
        button = (Button) findViewById(R.id.showIndeterminateNoTitle);
        button.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {            
                showDialog(DIALOG2_KEY);  // Come sopra, ma qui avremo il progressdialog senza titolo e non cancellabile
                mProgress = 0; // Azzeriamo la progressbar
                dialog.setProgress(0); // ... e comunichiamoglielo!
               
                // Creiamo il riferimento all'AsyncTask ed eseguiamolo
                MyAsyncTask doTask = new MyAsyncTask();
                        doTask.execute("","",""); // Non inseriamo alcun parametro perché nel nostro esempio non ne abbiamo bisogno
            }
        });
       
       
        mProgressHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
               
                // Verifichiamo se il nostro contatore ha raggiunto 50.
                // Se è inferiore, aggiorniamo il ProgressDialog con il nuovo valore e
                // notifichiamo all'Handler l'aggiornamento.
                // Al contrario, se mProgress ha raggiunto quota 50,
                // il ProgressDialog verrà distrutto.
                if (mProgress >= 50) {
                    dialog.dismiss();
                } else {
                    mProgress++;
                    dialog.incrementProgressBy(1); // Incrementiamo di 1 la posizione della ProgressBar
                                        try {
                                                /** Questa pausa ci serve solo per simulare
                                                 * l'esecuzione di un'operazione complessa.
                                                 * Nella pratica non avremo bisogno di questa istruzione
                                                 * poiché è qui che scriveremo il codice collegato al
                                                 * funzionamento del ProgressDialog.
                                                 * Per esempio: se dovviamo generare una serie di files,
                                                 * è qui che inseriremo il codice necessario.
                                                 */

                                                Thread.sleep(1000);
                                               
                                        } catch (InterruptedException e) {
                                                e.printStackTrace();
                                        }
                    mProgressHandler.sendEmptyMessage(0);
                }
            }
        };
   
    }
    private class MyAsyncTask extends AsyncTask<String, String, String> {
       
        @Override
        protected String doInBackground(String... params) {            
                /** Verifichiamo se il contatore è inferiore a quota 50.
                         * Se lo è eseguiremo il codice che vogliamo
                         * far eseguire in background.
                         * In questo esempio facciamo soltanto una pausa di 1 secondo che,
                         * nella realtà pratica, non ci servirà affatto, mentre qui lo facciamo
                         * proprio per simulare l'esecuzione di un'operazione complessa.
                         */
           
                while (mProgress<50) {
                        try {
                                        Thread.sleep(1000);
                                } catch (InterruptedException e) {
                                        e.printStackTrace();
                                }
                        mProgress++; // Incrementiamo il contatore                     
                        dialog.setProgress(mProgress); // Aggiorniamo il ProgressDialog con il nuovo valore
                }
                return null;
        }
        @Override              
        protected void onProgressUpdate(String... values) {
               
        }

        @Override
        protected void onPostExecute(String result) {
                dialog.dismiss(); // Questo codice verrà eseguito quando mProgress giungerà a 50.
        }                              
        }
 
}

main.xml
Codice (XML): [Seleziona]
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="fill_parent"
   android:layout_height="wrap_content" android:orientation="vertical">

    <Button android:id="@+id/showIndeterminate"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content" android:text="Progressdialog, Handler"/>

    <Button android:id="@+id/showIndeterminateNoTitle"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content" android:text="Progressdialog, AsyncTask"/>

</LinearLayout>

Bibliografia:
« Ultima modifica: 18 Febbraio 2011, 10:44:20 CET da JD »

Offline JD

  • Amministratore
  • Utente storico
  • *****
  • Post: 1600
  • Respect: +232
    • leinardi
    • Mostra profilo
  • Dispositivo Android:
    LG Nexus 5
  • Sistema operativo:
    L'ultima Ubuntu
Re:Mantenere un ProgressDialog dopo la rotazione del dispositivo
« Risposta #1 il: 16 Gennaio 2011, 11:53:25 CET »
0
Ottimo tutorial, grazie per averlo condiviso :)
È stata trovata una soluzione al tuo problema?
Evidenzia il post più utile premendo . È un ottimo modo per ringraziare chi ti ha aiutato ;).
E se hai aperto tu il thread marcalo come risolto cliccando !

Offline Qlimax

  • Moderatore globale
  • Utente senior
  • *****
  • Post: 757
  • Respect: +203
    • 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:Mantenere un ProgressDialog dopo la rotazione del dispositivo
« Risposta #2 il: 16 Gennaio 2011, 18:52:17 CET »
0
Complimenti, davvero un bel tutorial !

Offline zipgenius

  • Utente junior
  • **
  • Post: 80
  • Respect: +17
    • matteoriso
    • Mostra profilo
    • ZipGenius
  • Dispositivo Android:
    Samsung Galaxy S
  • Play Store ID:
    Wininizio.it Software
  • Sistema operativo:
    Windows 7
Re:Mantenere un ProgressDialog dopo la rotazione del dispositivo
« Risposta #3 il: 17 Gennaio 2011, 09:35:33 CET »
0
Grazie :)

Offline Alessio_roma™

  • Utente junior
  • **
  • Post: 50
  • Respect: 0
    • Mostra profilo
    • www.blogarm.tk
  • Dispositivo Android:
    LG Nexus 4
  • Sistema operativo:
    Windows 7 ultimate x64, Ubuntu 14.04
Re:[facile] Mantenere un ProgressDialog dopo la rotazione del dispositivo
« Risposta #4 il: 01 Novembre 2011, 16:46:53 CET »
0
se ho ben capito,in questo modo,ruotando il dispositivo non si interrompe il progress dialog? mentre tradizionalmente avverrebbe un blocco dell'applicazione? Sto provando il tutorial con eclipse e l'emulatore android,però se passo da modalità portrait a modalità landscape e poi viceversa,una volta tornato in portrait anche se lo schermo in verticale il contenuto resta capovolto
I MIEI SITI:
RPG & Fantasy -> La Fucina di Harad
Pagina personale -> Il Web di Alessio_roma™