Android
Modifiche all'orientamento
Ricerca…
Osservazioni
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)