Ricerca…


Osservazioni

Riferimento: https://guides.codepath.com/android/Handling-Configuration-Changes#references

Salvataggio e ripristino dello stato delle attività

Quando l'attività inizia a fermarsi, il sistema chiama onSaveInstanceState() modo che l'attività possa salvare le informazioni di stato con un insieme di coppie chiave-valore. L'implementazione predefinita di questo metodo salva automaticamente le informazioni sullo stato della gerarchia di visualizzazione dell'attività, ad esempio il testo in un widget EditText o la posizione di scorrimento di un controllo ListView .

Per salvare informazioni di stato aggiuntive per la tua attività, devi implementare onSaveInstanceState() e aggiungere coppie chiave-valore all'oggetto Bundle. Per esempio:

public class MainActivity extends Activity {
    static final String SOME_VALUE = "int_value";
    static final String SOME_OTHER_VALUE = "string_value";

    @Override
    protected void onSaveInstanceState(Bundle savedInstanceState) {
        // Save custom values into the bundle
        savedInstanceState.putInt(SOME_VALUE, someIntValue);
        savedInstanceState.putString(SOME_OTHER_VALUE, someStringValue);
        // Always call the superclass so it can save the view hierarchy state
        super.onSaveInstanceState(savedInstanceState);
    }
}

Il sistema chiamerà quel metodo prima che un'Attività venga distrutta. Successivamente il sistema chiamerà onRestoreInstanceState dove possiamo ripristinare lo stato dal bundle:

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
    // Always call the superclass so it can restore the view hierarchy
    super.onRestoreInstanceState(savedInstanceState);
    // Restore state members from saved instance
    someIntValue = savedInstanceState.getInt(SOME_VALUE);
    someStringValue = savedInstanceState.getString(SOME_OTHER_VALUE);
}

Lo stato di istanza può anche essere ripristinato nel metodo di attività # onCreate standard, ma è conveniente farlo in onRestoreInstanceState che assicura che tutta l'inizializzazione sia stata eseguita e consente alle sottoclassi di decidere se utilizzare l'implementazione predefinita. Leggi questo post StackOverflow per i dettagli.

Notare che onSaveInstanceState e onRestoreInstanceState non sono garantiti per essere chiamati insieme. Android invoca onSaveInstanceState() quando esiste la possibilità che l'attività possa essere distrutta. Tuttavia, ci sono casi in cui viene chiamato onSaveInstanceState ma l'attività non viene distrutta e come risultato non viene richiamato onRestoreInstanceState .

Salvataggio e ripristino dello stato dei frammenti

I frammenti hanno anche un metodo onSaveInstanceState() che viene chiamato quando il loro stato deve essere salvato:

public class MySimpleFragment extends Fragment {
    private int someStateValue;
    private final String SOME_VALUE_KEY = "someValueToSave";
   
    // Fires when a configuration change occurs and fragment needs to save state
    @Override
    protected void onSaveInstanceState(Bundle outState) {
        outState.putInt(SOME_VALUE_KEY, someStateValue);
        super.onSaveInstanceState(outState);
    }
}

Quindi possiamo estrarre i dati da questo stato salvato in onCreateView :

public class MySimpleFragment extends Fragment {
   // ...

   // Inflate the view for the fragment based on layout XML
   @Override
   public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.my_simple_fragment, container, false);
        if (savedInstanceState != null) {
            someStateValue = savedInstanceState.getInt(SOME_VALUE_KEY);
            // Do something with value if needed
        }
        return view;
   }
}

Affinché lo stato dei frammenti sia salvato correttamente, dobbiamo essere sicuri che non stiamo ricreando inutilmente il frammento sulle modifiche di configurazione. Ciò significa fare attenzione a non reinizializzare i frammenti esistenti quando già esistono. Eventuali frammenti inizializzati in un'attività devono essere ricercati per tag dopo una modifica alla configurazione:

public class ParentActivity extends AppCompatActivity {
    private MySimpleFragment fragmentSimple;
    private final String SIMPLE_FRAGMENT_TAG = "myfragmenttag";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        if (savedInstanceState != null) { // saved instance state, fragment may exist
           // look up the instance that already exists by tag
           fragmentSimple = (MySimpleFragment)  
              getSupportFragmentManager().findFragmentByTag(SIMPLE_FRAGMENT_TAG);
        } else if (fragmentSimple == null) { 
           // only create fragment if they haven't been instantiated already
           fragmentSimple = new MySimpleFragment();
        }
    }
}

Questo ci impone di fare attenzione a includere un tag per la ricerca ogni volta che si inserisce un frammento nell'attività all'interno di una transazione:

public class ParentActivity extends AppCompatActivity {
    private MySimpleFragment fragmentSimple;
    private final String SIMPLE_FRAGMENT_TAG = "myfragmenttag";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // ... fragment lookup or instantation from above...
        // Always add a tag to a fragment being inserted into container
        if (!fragmentSimple.isInLayout()) {
            getSupportFragmentManager()
                .beginTransaction()
                .replace(R.id.container, fragmentSimple, SIMPLE_FRAGMENT_TAG)
                .commit();
        }
    }
}

Con questo semplice schema, possiamo riutilizzare correttamente i frammenti e ripristinare il loro stato attraverso le modifiche alla configurazione.

Frammenti di sostegno

In molti casi, possiamo evitare problemi quando un'attività viene ricreata semplicemente usando i frammenti. Se le viste e lo stato sono all'interno di un frammento, possiamo facilmente conservare il frammento quando l'attività viene ricreata:

public class RetainedFragment extends Fragment {
    // data object we want to retain
    private MyDataObject data;

    // this method is only called once for this fragment
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // retain this fragment when activity is re-initialized
        setRetainInstance(true);
    }

    public void setData(MyDataObject data) {
        this.data = data;
    }

    public MyDataObject getData() {
        return data;
    }
}

Questo approccio impedisce che il frammento venga distrutto durante il ciclo di vita dell'attività. Sono invece trattenuti all'interno di Fragment Manager. Vedi i documenti ufficiali di Android per ulteriori informazioni .

Ora puoi verificare se il frammento esiste già per tag prima di crearne uno e il frammento manterrà lo stato tra le modifiche di configurazione. Per ulteriori dettagli, consultare la guida Gestione delle modifiche di runtime.

Orientamento dello schermo di blocco

Se vuoi bloccare il cambio di orientamento dello schermo di qualsiasi schermo (attività) della tua applicazione Android devi solo impostare la proprietà android:screenOrientation di una <activity> all'interno di AndroidManifest.xml :

<activity
    android:name="com.techblogon.screenorientationexample.MainActivity"
    android:screenOrientation="portrait"
    android:label="@string/app_name" >
    <!-- ... -->
</activity>

Ora quell'attività è obbligata a essere sempre visualizzata in modalità " ritratto ".

Gestire manualmente le modifiche di configurazione

Se l'applicazione non ha bisogno di aggiornare le risorse durante una specifica modifica alla configurazione e si dispone di una limitazione delle prestazioni che richiede di evitare il riavvio delle attività, è possibile dichiarare che la propria attività gestisce la stessa modifica della configurazione, impedendo al sistema di riavviare il proprio attività.

Tuttavia, questa tecnica deve essere considerata l'ultima risorsa quando è necessario evitare riavvii a causa di una modifica della configurazione e non è consigliata per la maggior parte delle applicazioni. Per adottare questo approccio, dobbiamo aggiungere il nodo android:configChanges all'attività all'interno di AndroidManifest.xml :

<activity android:name=".MyActivity"
          android:configChanges="orientation|screenSize|keyboardHidden"
          android:label="@string/app_name">

Ora, quando una di queste configurazioni cambia, l'attività non si riavvia ma riceve una chiamata a onConfigurationChanged() :

// Within the activity which receives these changes
// Checks the current device orientation, and toasts accordingly
@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);

    // Checks the orientation of the screen
    if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
        Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show();
    } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
        Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show();
    }
}

Vedi la gestione dei documenti di modifica . Per ulteriori informazioni su quali modifiche alla configurazione è possibile gestire nella propria attività, consultare la documentazione di android: configChanges e la classe Configuration .

Gestire AsyncTask

Problema:

  • Se dopo l'avvio di AsyncTask c'è una rotazione dello schermo, l'attività proprietaria viene distrutta e ricreata.
  • Quando termina AsyncTask , vuole aggiornare l'interfaccia utente che potrebbe non essere più valida.

Soluzione:

Usando i caricatori , si può facilmente superare l'attività di distruzione / ricreazione.

Esempio:

Attività principale:

public class MainActivity extends AppCompatActivity 
        implements LoaderManager.LoaderCallbacks<Bitmap> {

    //Unique id for the loader
    private static final int MY_LOADER = 0; 

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        LoaderManager loaderManager = getSupportLoaderManager();

        if(loaderManager.getLoader(MY_LOADER) == null) {
            loaderManager.initLoader(MY_LOADER, null, this).forceLoad();
        }
    }

    @Override
    public Loader<Bitmap> onCreateLoader(int id, Bundle args) {
        //Create a new instance of your Loader<Bitmap>
        MyLoader loader = new MyLoader(MainActivity.this);
        return loader;
    }

    @Override
    public void onLoadFinished(Loader<Bitmap> loader, Bitmap data) {
        // do something in the parent activity/service
        // i.e. display the downloaded image
        Log.d("MyAsyncTask", "Received result: ");
    }

    @Override
    public void onLoaderReset(Loader<Bitmap> loader) {

    }
}

AsyncTaskLoader:

public class MyLoader extends AsyncTaskLoader<Bitmap> {
    private WeakReference<Activity> motherActivity;

    public MyLoader(Activity activity) {
        super(activity);
        //We don't use this, but if you want you can use it, but remember, WeakReference
        motherActivity = new WeakReference<>(activity);
    }

    @Override
    public Bitmap loadInBackground() {
        // Do work. I.e download an image from internet to be displayed in gui.
        // i.e. return the downloaded gui
        return result;
    }
}

Nota:

È importante utilizzare o meno la libreria di compatibilità v4, ma non utilizzare parte di una parte e una parte dell'altro, poiché ciò porterà a errori di compilazione. Per controllare puoi guardare le importazioni per android.support.v4.content e android.content (non dovresti averle entrambe).

Blocca la rotazione dello schermo a livello di programmazione

È molto comune che durante lo sviluppo, si possa trovare molto utile per bloccare / sbloccare lo schermo del dispositivo durante specifiche parti del codice .

Ad esempio, mentre mostra una finestra di dialogo con informazioni, lo sviluppatore potrebbe voler bloccare la rotazione dello schermo per impedire che la finestra di dialogo venga chiusa e l'attività corrente venga ricostruita per sbloccarla di nuovo quando la finestra di dialogo viene chiusa.

Anche se possiamo ottenere il blocco della rotazione dal manifest facendo:

<activity
    android:name=".TheActivity"
    android:screenOrientation="portrait"
    android:label="@string/app_name" >
</activity>

Si può farlo anche programmaticamente facendo quanto segue:

public void lockDeviceRotation(boolean value) {
    if (value) {
        int currentOrientation = getResources().getConfiguration().orientation;
        if (currentOrientation == Configuration.ORIENTATION_LANDSCAPE) {
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
        } else {
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);
        }
    } else {
        getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_USER);
        } else {
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);
        }
    }
}

E poi chiamando il seguente, rispettivamente per bloccare e sbloccare la rotazione del dispositivo

lockDeviceRotation(true)

e

lockDeviceRotation(false) 


Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow