Riavvio dell'attività a rotazione Android
-
19-08-2019 - |
Domanda
Nella mia applicazione Android, quando ruoto il dispositivo (fai scorrere fuori la tastiera), my Activity
viene riavviato (onCreate
è chiamato).Ora, probabilmente è così che dovrebbe essere, ma faccio molta configurazione iniziale in onCreate
metodo, quindi ho bisogno di:
- Metti tutta la configurazione iniziale in un'altra funzione in modo che non vada persa tutta durante la rotazione del dispositivo o
- Fallo così
onCreate
non viene richiamato di nuovo e il layout viene semplicemente modificato o - Limita l'app solo al ritratto in questo modo
onCreate
non viene chiamato.
Soluzione
Utilizzando la classe dell'applicazione
A seconda di cosa stai facendo nella tua inizializzazione, potresti prendere in considerazione la creazione di una nuova classe che si estende Application
e spostando il codice di inizializzazione in un file sovrascritto onCreate
metodo all'interno di quella classe.
public class MyApplicationClass extends Application {
@Override
public void onCreate() {
super.onCreate();
// TODO Put your application initialization code here.
}
}
IL onCreate
nella classe dell'applicazione viene chiamato solo quando viene creata l'intera applicazione, quindi l'attività si riavvia in seguito alle modifiche dell'orientamento o della visibilità della tastiera non la attiverà.
È buona norma esporre l'istanza di questa classe come singleton ed esporre le variabili dell'applicazione che stai inizializzando utilizzando getter e setter.
NOTA:Dovrai specificare il nome della tua nuova classe Application nel manifest affinché possa essere registrata e utilizzata:
<application
android:name="com.you.yourapp.MyApplicationClass"
Reazione ai cambiamenti di configurazione [AGGIORNAMENTO:questo è deprecato dall'API 13; vedere l'alternativa consigliata]
Come ulteriore alternativa, puoi fare in modo che la tua applicazione ascolti gli eventi che potrebbero causare un riavvio, come le modifiche all'orientamento e alla visibilità della tastiera, e gestirli all'interno della tua attività.
Inizia aggiungendo il android:configChanges
nodo al nodo manifest della tua attività
<activity android:name=".MyActivity"
android:configChanges="orientation|keyboardHidden"
android:label="@string/app_name">
o per Android 3.2 (livello API 13) e versioni successive:
<activity android:name=".MyActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/app_name">
Quindi all'interno dell'attività sovrascrivi il file onConfigurationChanged
metodo e chiamata setContentView
per forzare la riesecuzione del layout della GUI nel nuovo orientamento.
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
setContentView(R.layout.myLayout);
}
Altri suggerimenti
Aggiornamento per Android 3.2 e versioni successive:
Attenzione : a partire da Android 3.2 (livello API 13), il " dimensioni dello schermo " cambia anche quando il dispositivo passa dall'orientamento verticale a quello orizzontale. Pertanto, se si desidera impedire il riavvio del runtime a causa della modifica dell'orientamento durante lo sviluppo per livello API 13 o superiore (come dichiarato dagli attributi minSdkVersion e targetSdkVersion), è necessario includere il valore
"screenSize"
oltre al valore"orientation"
. Cioè, devi dichiarareandroid:configChanges="orientation|screenSize"
. Tuttavia, se l'applicazione ha come target API livello 12 o inferiore, la tua attività gestisce sempre questa modifica della configurazione stessa (questa modifica della configurazione non riavvia la tua attività, anche quando è in esecuzione su un dispositivo Android 3.2 o superiore).
Invece di provare a fermare il onCreate()
di essere sparato del tutto, forse prova a controllare che Bundle
savedInstanceState
venga passato nell'evento per vedere se è nullo o meno.
Ad esempio, se ho una logica che dovrebbe essere eseguita quando Activity
è veramente creata, non ad ogni cambio di orientamento, eseguo quella logica solo in <=> solo se <=> è null.
Altrimenti, voglio ancora che il layout venga ridisegnato correttamente per l'orientamento.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_game_list);
if(savedInstanceState == null){
setupCloudMessaging();
}
}
non sono sicuro che questa sia la risposta definitiva, ma funziona per me.
quello che ho fatto ...
nel manifest, nella sezione attività, aggiunto:
android:configChanges="keyboardHidden|orientation"
nel codice per l'attività, implementato:
//used in onCreate() and onConfigurationChanged() to set up the UI elements
public void InitializeUI()
{
//get views from ID's
this.textViewHeaderMainMessage = (TextView) this.findViewById(R.id.TextViewHeaderMainMessage);
//etc... hook up click listeners, whatever you need from the Views
}
//Called when the activity is first created.
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
InitializeUI();
}
//this is called when the screen rotates.
// (onCreate is no longer called when screen rotates due to manifest, see: android:configChanges)
@Override
public void onConfigurationChanged(Configuration newConfig)
{
super.onConfigurationChanged(newConfig);
setContentView(R.layout.main);
InitializeUI();
}
Quello che descrivi è il comportamento predefinito. Devi rilevare e gestire questi eventi tu stesso aggiungendo:
android:configChanges
al tuo manifest e quindi alle modifiche che desideri gestire. Quindi, per l'orientamento, dovresti usare:
android:configChanges="orientation"
e per l'apertura o la chiusura della tastiera dovresti usare:
android:configChanges="keyboardHidden"
Se vuoi gestirli entrambi puoi semplicemente separarli con il comando pipe come:
android:configChanges="keyboardHidden|orientation"
Questo attiverà il metodo onConfigurationChanged in qualunque attività tu chiami. Se si ignora il metodo, è possibile passare i nuovi valori.
Spero che questo aiuti.
Ho appena scoperto questa tradizione:
Per mantenere viva l'attività attraverso una modifica dell'orientamento e gestirla tramite onConfigurationChanged
, la documentazione e l'esempio di codice sopra suggeriscilo nel file manifest:
<activity android:name=".MyActivity"
android:configChanges="orientation|keyboardHidden"
android:label="@string/app_name">
che ha il vantaggio extra che funziona sempre.
Il bonus è che omettere keyboardHidden
può sembrare logico, ma causa errori nell'emulatore (almeno per Android 2.1): specificando solo orientation
si farà chiamare sia l'emulatore OnCreate
che <=> a volte, e solo <=> altre volte.
Non ho visto l'errore su un dispositivo, ma ho sentito che l'emulatore non funziona per gli altri. Quindi vale la pena documentare.
Potresti anche prendere in considerazione l'uso del modo della piattaforma Android di conservare i dati attraverso i cambiamenti di orientamento: onRetainNonConfigurationInstance ()
e getLastNonConfigurationInstance ()
.
Ciò ti consente di conservare i dati attraverso le modifiche alla configurazione, come informazioni che potresti aver ottenuto da un recupero del server o qualcos'altro che è stato calcolato in onCreate
o da allora, consentendo allo stesso tempo ad Android di ridimensionare il tuo Activity
utilizzando il file xml per l'orientamento ora in uso.
Va ??notato che questi metodi sono ora deprecati (anche se ancora più flessibili della gestione dell'orientamento cambiano te stesso come suggerisce la maggior parte delle soluzioni di cui sopra) con la raccomandazione che tutti passano a Frammenti
e invece usano < code> setRetainInstance (true) su ogni frammento
che desideri conservare.
L'approccio è utile ma è incompleto quando si usano i frammenti.
I frammenti di solito vengono ricreati al cambio di configurazione. Se non desideri che ciò accada, utilizza
setRetainInstance (true);
nei costruttori del frammento
Ciò causerà la conservazione dei frammenti durante la modifica della configurazione.
Ho appena aggiunto
android:configChanges="keyboard|keyboardHidden|orientation"
nel file manifest e non ha aggiunto nessun metodo onConfigurationChanged
nella mia attività.
Quindi ogni volta che la tastiera scorre o non succede nulla .
Il metodo onCreate
viene ancora chiamato anche quando cambi l'orientamento
di Android. Quindi spostare tutte le funzionalità pesanti su questo metodo non ti aiuterà
È molto semplice, basta fare i seguenti passi:
<activity
android:name=".Test"
android:configChanges="orientation|screenSize"
android:screenOrientation="landscape" >
</activity>
Questo funziona per me:
Nota: l'orientamento dipende dal tuo requisito
Inserisci il codice di seguito nel tag < activity >
in Manifest.xml
:
android:configChanges="screenLayout|screenSize|orientation"
onConfigurationChanged is called when the screen rotates. (onCreate is no longer called when screen rotates due to manifest, see: android:configChanges)
Quale parte del manifest gli dice " non chiamare onCreate ()
" ;?
Inoltre,
I documenti di Google dicono di evitare di usare android: configChanges
(tranne che come ultima risorsa) .... Ma poi i metodi alternativi che suggeriscono a tutti DO usano android: configChanges
.
È stata la mia esperienza che l'emulatore chiama SEMPRE onCreate ()
al momento della rotazione.
Ma i dispositivi 1-2 su cui eseguo lo stesso codice su ... non lo fanno.
(Non so perché ci sarebbe qualche differenza.)
Aggiungi questa riga al tuo manifest: -
android:configChanges="orientation|keyboard|keyboardHidden|screenSize|screenLayout|uiMode"
e questo frammento dell'attività: -
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
Le modifiche da apportare nel manifest Android sono:
android:configChanges="keyboardHidden|orientation"
Le aggiunte da effettuare all'interno dell'attività sono:
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();
}
}
Esistono diversi modi per farlo:
Salva stato attività
Puoi salvare lo stato dell'attività in onSaveInstanceState
.
@Override
public void onSaveInstanceState(Bundle outState) {
/*Save your data to be restored here
Example : outState.putLong("time_state", time); , time is a long variable*/
super.onSaveInstanceState(outState);
}
e quindi utilizzare il bundle
per ripristinare lo stato.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(savedInstanceState!= null){
/*When rotation occurs
Example : time = savedInstanceState.getLong("time_state", 0); */
} else {
//When onCreate is called for the first time
}
}
Gestisci i cambiamenti di orientamento da solo
Un'altra alternativa è quella di gestire i cambiamenti di orientamento da soli. Ma questa non è considerata una buona pratica.
Aggiungi questo al tuo file manifest.
android:configChanges="keyboardHidden|orientation"
per Android 3.2 e versioni successive:
android:configChanges="keyboardHidden|orientation|screenSize"
@Override
public void onConfigurationChanged(Configuration config) {
super.onConfigurationChanged(config);
if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
//Handle rotation from landscape to portarit mode here
} else if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE){
//Handle rotation from portrait to landscape mode here
}
}
Limita rotazione
Puoi anche limitare la tua attività alla modalità verticale o orizzontale per evitare la rotazione.
Aggiungi questo al tag attività nel tuo file manifest:
android:screenOrientation="portrait"
O implementalo a livello di codice nella tua attività:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
Il modo in cui ho scoperto di farlo è usare gli eventi onRestoreInstanceState
e onSaveInstanceState
per salvare qualcosa nel Bundle
(anche se tu non è necessario salvare alcuna variabile, basta inserire qualcosa in modo che Bundle
non sia vuoto). Quindi, sul metodo onCreate
, controlla se Bundle
è vuoto e, se lo è, quindi esegui l'inizializzazione, in caso contrario, quindi eseguilo.
Anche se non è " il modo Android " Ho ottenuto ottimi risultati gestendo personalmente i cambiamenti di orientamento e riposizionando semplicemente i widget in una prospettiva per tenere conto dell'orientamento modificato. Questo è più veloce di qualsiasi altro approccio, perché le tue viste non devono essere salvate e ripristinate. Offre inoltre un'esperienza più fluida all'utente, poiché i widget riposizionati sono esattamente gli stessi widget, appena spostati e / o ridimensionati. Non solo lo stato modello, ma anche lo stato di visualizzazione, può essere preservato in questo modo.
RelativeLayout
a volte può essere una buona scelta per una vista che deve riorientarsi di volta in volta. È sufficiente fornire una serie di parametri di layout verticale e una serie di parametri di layout paesaggistici, con diverse regole di posizionamento relative su ciascuno, per ciascun widget figlio. Quindi, nel tuo metodo onConfigurationChanged ()
, passi quello appropriato a una chiamata setLayoutParams ()
su ogni bambino. Se uno stesso controllo figlio deve essere riorientato internamente , basta chiamare un metodo su quel figlio per eseguire il riorientamento. Quel bambino chiama allo stesso modo metodi su uno qualsiasi dei suoi controlli figlio che necessitano di riorientamento interno e così via.
Ogni volta che si ruota lo schermo, l'attività aperta viene terminata e viene richiamato onCreate ().
1. Puoi fare una cosa per salvare lo stato di attività quando lo schermo viene ruotato in modo tale da poter recuperare tutte le cose vecchie quando viene richiamato onCreate () di attività. Fai riferimento a questo link
2. Se vuoi impedire il riavvio dell'attività, inserisci le seguenti righe nel tuo file manifest.xml.
<activity android:name=".Youractivity"
android:configChanges="orientation|screenSize"/>
Nota: inserisco questa risposta se in futuro qualcuno dovesse affrontare lo stesso problema. Per me la seguente riga non era abbastanza:
android:configChanges="orientation"
Quando ho ruotato lo schermo, il metodo `onConfigurationChanged (Configuration newConfig) non è stato chiamato.
Soluzione: ho anche dovuto aggiungere " screenSize " anche se il problema riguardava l'orientamento. Quindi nel file AndroidManifest.xml, aggiungi questo:
android:configChanges="keyboardHidden|orientation|screenSize"
Quindi implementare il metodo onConfigurationChanged (Configuration newConfig)
devi usare il metodo onSavedInstanceState per archiviare tutto il valore nel suo parametro è che è bundle
@Override
public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
super.onSaveInstanceState(outState, outPersistentState);
outPersistentState.putBoolean("key",value);
}
e usa
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
savedInstanceState.getBoolean("key");
}
per recuperare e impostare il valore per visualizzare gli oggetti gestirà le rotazioni dello schermo
Nella sezione attività del manifest
, aggiungi:
android:configChanges="keyboardHidden|orientation"
Aggiungi questa riga in manifest: android:configChanges="orientation|screenSize"
Le persone dicono che dovresti usare
android:configChanges="keyboardHidden|orientation"
Ma il modo migliore e più professionale per gestire la rotazione in Android è utilizzare la classe Loader. Non è una classe famosa (non so perché), ma è molto meglio dell'AsyncTask. Per ulteriori informazioni, puoi leggere i tutorial Android disponibili nei corsi Android di Udacity.
Ovviamente, in un altro modo, è possibile memorizzare i valori o le viste con onSaveInstanceState e leggerli con onRestoreInstanceState. Dipende davvero da te.
Dopo un po 'di tentativi ed errori, ho trovato una soluzione adatta alle mie esigenze nella maggior parte delle situazioni. Ecco il codice:
Configurazione manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.pepperonas.myapplication">
<application
android:name=".App"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:configChanges="orientation|keyboardHidden|screenSize">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
MainActivity:
import android.content.res.Configuration;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private static final String TAG = "MainActivity";
private Fragment mFragment;
private int mSelected = -1;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate " + "");
// null check not realy needed - but just in case...
if (savedInstanceState == null) {
initUi();
// get an instance of FragmentTransaction from your Activity
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
/*IMPORTANT: Do the INITIAL(!) transaction only once!
* If we call this everytime the layout changes orientation,
* we will end with a messy, half-working UI.
* */
mFragment = FragmentOne.newInstance(mSelected = 0);
fragmentTransaction.add(R.id.frame, mFragment);
fragmentTransaction.commit();
}
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
Log.d(TAG, "onConfigurationChanged " +
(newConfig.orientation
== Configuration.ORIENTATION_LANDSCAPE
? "landscape" : "portrait"));
initUi();
Log.i(TAG, "onConfigurationChanged - last selected: " + mSelected);
makeFragmentTransaction(mSelected);
}
/**
* Called from {@link #onCreate} and {@link #onConfigurationChanged}
*/
private void initUi() {
setContentView(R.layout.activity_main);
Log.d(TAG, "onCreate instanceState == null / reinitializing..." + "");
Button btnFragmentOne = (Button) findViewById(R.id.btn_fragment_one);
Button btnFragmentTwo = (Button) findViewById(R.id.btn_fragment_two);
btnFragmentOne.setOnClickListener(this);
btnFragmentTwo.setOnClickListener(this);
}
/**
* Not invoked (just for testing)...
*/
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
Log.d(TAG, "onSaveInstanceState " + "YOU WON'T SEE ME!!!");
}
/**
* Not invoked (just for testing)...
*/
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
Log.d(TAG, "onSaveInstanceState " + "YOU WON'T SEE ME, AS WELL!!!");
}
@Override
protected void onResume() {
super.onResume();
Log.d(TAG, "onResume " + "");
}
@Override
protected void onPause() {
super.onPause();
Log.d(TAG, "onPause " + "");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy " + "");
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_fragment_one:
Log.d(TAG, "onClick btn_fragment_one " + "");
makeFragmentTransaction(0);
break;
case R.id.btn_fragment_two:
Log.d(TAG, "onClick btn_fragment_two " + "");
makeFragmentTransaction(1);
break;
default:
Log.d(TAG, "onClick null - wtf?!" + "");
}
}
/**
* We replace the current Fragment with the selected one.
* Note: It's called from {@link #onConfigurationChanged} as well.
*/
private void makeFragmentTransaction(int selection) {
switch (selection) {
case 0:
mFragment = FragmentOne.newInstance(mSelected = 0);
break;
case 1:
mFragment = FragmentTwo.newInstance(mSelected = 1);
break;
}
// Create new transaction
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack
transaction.replace(R.id.frame, mFragment);
/*This would add the Fragment to the backstack...
* But right now we comment it out.*/
// transaction.addToBackStack(null);
// Commit the transaction
transaction.commit();
}
}
E frammento di esempio:
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
/**
* @author Martin Pfeffer (pepperonas)
*/
public class FragmentOne extends Fragment {
private static final String TAG = "FragmentOne";
public static Fragment newInstance(int i) {
Fragment fragment = new FragmentOne();
Bundle args = new Bundle();
args.putInt("the_id", i);
fragment.setArguments(args);
return fragment;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.d(TAG, "onCreateView " + "");
return inflater.inflate(R.layout.fragment_one, container, false);
}
}
Può essere trovato su github .
Utilizza l'ascoltatore orientamento
per eseguire diverse attività con orientamento diverso.
@Override
public void onConfigurationChanged(Configuration myConfig)
{
super.onConfigurationChanged(myConfig);
int orient = getResources().getConfiguration().orientation;
switch(orient)
{
case Configuration.ORIENTATION_LANDSCAPE:
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
break;
case Configuration.ORIENTATION_PORTRAIT:
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
break;
default:
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
}
}
Inserisci questo codice sotto nel tuo Activity
in Android Manifest
.
android:configChanges="orientation"
Questo non riavvierà la tua attività quando cambierai orientamento.
Correggi l'orientamento dello schermo (orizzontale o verticale) in AndroidManifest.xml
android: screenOrientation = " portrait "
o android:screenOrientation="landscape"
per questo il tuo metodo onResume ()
non viene chiamato.
Uno dei migliori componenti di Android Architechure introdotti da Google soddisferà tutti i requisiti di ViewModel.
È progettato per archiviare e gestire i dati relativi all'interfaccia utente in modo lungo tutto il ciclo di vita, oltre a consentire ai dati di sopravvivere durante la rotazione dello schermo
class MyViewModel : ViewModel() {
Si prega di fare riferimento a questo: https://developer.android.com/topic/libraries / architettura / viewmodel
Puoi bloccare l'orientamento corrente dello schermo usando questo codice ...
int currentOrientation =context.getResources().getConfiguration().orientation;
if (currentOrientation == Configuration.ORIENTATION_PORTRAIT) {
((Activity) context).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
} else {
((Activity) context). setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}