Reinicio de actividad en rotación Android
-
19-08-2019 - |
Pregunta
En mi aplicación de Android, cuando giro el dispositivo (deslizo el teclado hacia afuera), mi Activity
se reinicia (onCreate
se llama).Probablemente así es como se supone que debe ser, pero hago muchas configuraciones iniciales en el onCreate
método, entonces necesito:
- Coloque toda la configuración inicial en otra función para que no se pierda por completo en la rotación del dispositivo o
- Hazlo así
onCreate
no se vuelve a llamar y el diseño simplemente se ajusta o - Limite la aplicación solo a retrato para que
onCreate
no se llama.
Solución
Usando la clase de aplicación
Dependiendo de lo que esté haciendo en su inicialización, podría considerar crear una nueva clase que se extienda Application
y moviendo su código de inicialización a un anulado onCreate
método dentro de esa clase.
public class MyApplicationClass extends Application {
@Override
public void onCreate() {
super.onCreate();
// TODO Put your application initialization code here.
}
}
El onCreate
en la clase de aplicación solo se llama cuando se crea toda la aplicación, por lo que la actividad se reinicia con los cambios de orientación o visibilidad del teclado no la activará.
Es una buena práctica exponer la instancia de esta clase como un singleton y exponer las variables de la aplicación que está inicializando usando captadores y definidores.
NOTA:Deberá especificar el nombre de su nueva clase de Aplicación en el manifiesto para que se registre y utilice:
<application
android:name="com.you.yourapp.MyApplicationClass"
Reaccionar a los cambios de configuración [ACTUALIZAR:esto está en desuso desde API 13; ver la alternativa recomendada]
Como alternativa adicional, puede hacer que su aplicación escuche eventos que provocarían un reinicio (como cambios de orientación y visibilidad del teclado) y los maneje dentro de su Actividad.
Comience agregando el android:configChanges
nodo al nodo manifiesto de su actividad
<activity android:name=".MyActivity"
android:configChanges="orientation|keyboardHidden"
android:label="@string/app_name">
o por Android 3.2 (API nivel 13) y más reciente:
<activity android:name=".MyActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/app_name">
Luego, dentro de la Actividad, anule el onConfigurationChanged
método y llamada setContentView
para forzar que el diseño de la GUI se rehaga en la nueva orientación.
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
setContentView(R.layout.myLayout);
}
Otros consejos
Actualización para Android 3.2 y superior:
Precaución : a partir de Android 3.2 (nivel 13 de API), la " tamaño de pantalla " también cambia cuando el dispositivo cambia entre orientación vertical y horizontal. Por lo tanto, si desea evitar reinicios en tiempo de ejecución debido al cambio de orientación al desarrollar para el nivel 13 de API o superior (según lo declarado por los atributos minSdkVersion y targetSdkVersion), debe incluir el valor
"screenSize"
además del valor"orientation"
. Es decir, debe declararandroid:configChanges="orientation|screenSize"
. Sin embargo, si su aplicación se dirige al nivel de API 12 o inferior, entonces su actividad siempre maneja este cambio de configuración por sí mismo (este cambio de configuración no reinicia su actividad, incluso cuando se ejecuta en un dispositivo Android 3.2 o superior).
En lugar de tratar de evitar que el onCreate()
se dispare por completo, tal vez intente verificar si se pasa el Bundle
savedInstanceState
al evento para ver si es nulo o no.
Por ejemplo, si tengo alguna lógica que debería ejecutarse cuando el Activity
se crea realmente, no en cada cambio de orientación, solo ejecuto esa lógica en el <=> solo si el <=> es nulo.
De lo contrario, todavía quiero que el diseño se vuelva a dibujar correctamente para la orientación.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_game_list);
if(savedInstanceState == null){
setupCloudMessaging();
}
}
no estoy seguro si esta es la respuesta definitiva, pero funciona para mí.
lo que hice ...
en el manifiesto, a la sección de actividad, añadido:
android:configChanges="keyboardHidden|orientation"
en el código de la actividad, implementado:
//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();
}
Lo que describe es el comportamiento predeterminado. Debe detectar y manejar estos eventos usted mismo agregando:
android:configChanges
a su manifiesto y luego los cambios que desea manejar. Entonces, para la orientación, usaría:
android:configChanges="orientation"
y para que el teclado se abra o cierre, usaría:
android:configChanges="keyboardHidden"
Si desea manejar ambos, puede separarlos con el comando de tubería como:
android:configChanges="keyboardHidden|orientation"
Esto activará el método onConfigurationChanged en cualquier actividad que llame. Si anula el método, puede pasar los nuevos valores.
Espero que esto ayude.
Acabo de descubrir esta historia:
Para mantener viva la Actividad a través de un cambio de orientación y manejarla a través de onConfigurationChanged
, la documentación y la muestra de código anterior sugiera esto en el archivo de manifiesto:
<activity android:name=".MyActivity"
android:configChanges="orientation|keyboardHidden"
android:label="@string/app_name">
que tiene el beneficio adicional de que siempre funciona.
La historia adicional es que omitir el keyboardHidden
puede parecer lógico, pero causa fallas en el emulador (al menos para Android 2.1): al especificar solo orientation
hará que el emulador llame tanto a OnCreate
como a <=> a veces, y solo <=> otras veces.
No he visto la falla en un dispositivo, pero he oído que el emulador falla para otros. Vale la pena documentarlo.
También podría considerar usar la forma de la plataforma Android de persistir los datos en los cambios de orientación: onRetainNonConfigurationInstance ()
y getLastNonConfigurationInstance ()
.
Esto le permite conservar los datos a través de los cambios de configuración, como la información que puede haber obtenido de una búsqueda del servidor o algo más que se ha calculado en onCreate
o desde entonces, mientras que también permite que Android vuelva a diseñar su Activity
usando el archivo xml para la orientación ahora en uso.
Debe tenerse en cuenta que estos métodos ahora están en desuso (aunque aún más flexible que la orientación del manejo, cambie usted mismo como sugiere la mayoría de las soluciones anteriores) con la recomendación de que todos cambien a Fragments
y en su lugar usen < code> setRetainInstance (true) en cada Fragment
que desea conservar.
El enfoque es útil pero está incompleto cuando se usan Fragmentos.
Los fragmentos generalmente se recrean al cambiar la configuración. Si no desea que esto suceda, use
setRetainInstance (true);
en los constructores del Fragmento
Esto hará que se retengan fragmentos durante el cambio de configuración.
Simplemente agregué
android:configChanges="keyboard|keyboardHidden|orientation"
en el archivo de manifiesto y no agregó ningún método onConfigurationChanged
en mi actividad.
Entonces, cada vez que el teclado se desliza hacia afuera o no sucede nada .
El método onCreate
todavía se llama incluso cuando cambia la orientación
de Android. Por lo tanto, mover todas las funciones pesadas a este método no te ayudará
Es muy simple, solo siga los siguientes pasos:
<activity
android:name=".Test"
android:configChanges="orientation|screenSize"
android:screenOrientation="landscape" >
</activity>
Esto funciona para mí:
Nota: la orientación depende de su requerimiento
Coloque el código a continuación dentro de su etiqueta < activity >
en 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)
¿Qué parte del manifiesto le dice " no llame a onCreate ()
" ;?
Además,
Los documentos de Google dicen que se debe evitar usar android: configChanges
(excepto como último recurso) .... Pero luego, los métodos alternativos sugieren que todos DO usen android: configChanges
.
Según mi experiencia, el emulador SIEMPRE llama a onCreate ()
al rotar.
Pero los dispositivos 1-2 en los que ejecuto el mismo código ... no lo hacen.
(No estoy seguro de por qué habría alguna diferencia).
Agregue esta línea a su manifiesto: -
android:configChanges="orientation|keyboard|keyboardHidden|screenSize|screenLayout|uiMode"
y este fragmento de la actividad: -
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
Los cambios que se realizarán en el manifiesto de Android son:
android:configChanges="keyboardHidden|orientation"
Las adiciones a realizar dentro de la actividad son:
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();
}
}
Hay varias formas de hacer esto:
Guardar estado de actividad
Puede guardar el estado de la actividad en 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);
}
y luego use el bundle
para restaurar el estado.
@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
}
}
Maneja los cambios de orientación por ti mismo
Otra alternativa es manejar los cambios de orientación usted mismo. Pero esto no se considera una buena práctica.
Agregue esto a su archivo de manifiesto.
android:configChanges="keyboardHidden|orientation"
para Android 3.2 y posterior:
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
}
}
Restringir rotación
También puede limitar su actividad al modo vertical u horizontal para evitar la rotación.
Agregue esto a la etiqueta de actividad en su archivo de manifiesto:
android:screenOrientation="portrait"
O implemente esto programáticamente en su actividad:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
La forma en que he encontrado para hacer esto es usar los eventos onRestoreInstanceState
y onSaveInstanceState
para guardar algo en el Bundle
(incluso si usted no necesita guardar ninguna variable, simplemente coloque algo allí para que el Bundle
no esté vacío). Luego, en el método onCreate
, verifique si el Bundle
está vacío, y si lo está, realice la inicialización, si no, hágalo.
Aunque no es "la forma de Android" He obtenido muy buenos resultados manejando los cambios de orientación y simplemente reposicionando los widgets dentro de una vista para tener en cuenta la orientación alterada. Esto es más rápido que cualquier otro enfoque, ya que sus vistas no tienen que guardarse ni restaurarse. También proporciona una experiencia más fluida para el usuario, porque los widgets reposicionados son exactamente los mismos, simplemente movidos y / o redimensionados. No solo el estado del modelo, sino también el estado de vista, se pueden conservar de esta manera.
RelativeLayout
a veces puede ser una buena opción para una vista que tiene que reorientarse de vez en cuando. Simplemente proporcione un conjunto de parámetros de diseño de retrato y un conjunto de parámetros de diseño de paisaje, con diferentes reglas de posicionamiento relativo en cada uno, para cada widget secundario. Luego, en su método onConfigurationChanged ()
, pasa el apropiado a una llamada setLayoutParams ()
en cada elemento secundario. Si algún control secundario en sí mismo necesita ser interno reorientado, simplemente llame a un método en ese elemento secundario para realizar la reorientación. Ese niño llama de manera similar a los métodos en cualquiera de sus controles secundarios que necesitan una reorientación interna, y así sucesivamente.
Cada vez que se gira la pantalla, la actividad abierta finaliza y se vuelve a llamar a onCreate ().
1. Puede hacer una cosa, salvo el estado de la actividad cuando se gira la pantalla para que pueda recuperar todas las cosas viejas cuando se vuelve a llamar a onCreate () de la actividad. Consulte este enlace
2. Si desea evitar el reinicio de la actividad, simplemente coloque las siguientes líneas en su archivo manifest.xml.
<activity android:name=".Youractivity"
android:configChanges="orientation|screenSize"/>
Nota: publico esta respuesta si alguien en el futuro enfrenta el mismo problema que yo. Para mí, la siguiente línea no fue suficiente:
android:configChanges="orientation"
Cuando roté la pantalla, no se llamó al método `onConfigurationChanged (Configuration newConfig).
Solución: también tuve que agregar " screenSize " incluso si el problema tuviera que ver con la orientación. Entonces, en el archivo AndroidManifest.xml, agregue esto:
android:configChanges="keyboardHidden|orientation|screenSize"
Luego implemente el método onConfigurationChanged (Configuration newConfig)
necesita usar el método onSavedInstanceState para almacenar todo el valor de su parámetro has que es paquete
@Override
public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
super.onSaveInstanceState(outState, outPersistentState);
outPersistentState.putBoolean("key",value);
}
y usar
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
savedInstanceState.getBoolean("key");
}
para recuperar y establecer el valor para ver objetos manejará las rotaciones de la pantalla
En la sección de actividad del manifiesto
, agregue:
android:configChanges="keyboardHidden|orientation"
Agregue esta línea en manifiesto: android:configChanges="orientation|screenSize"
La gente dice que deberías usar
android:configChanges="keyboardHidden|orientation"
Pero la mejor y más profesional forma de manejar la rotación en Android es usar la clase Loader. No es una clase famosa (no sé por qué), pero es mucho mejor que AsyncTask. Para obtener más información, puede leer los tutoriales de Android que se encuentran en los cursos de Android de Udacity.
Por supuesto, como otra forma, puede almacenar los valores o las vistas con onSaveInstanceState y leerlos con onRestoreInstanceState. Depende de ti realmente.
Después de un tiempo de prueba y error, encontré una solución que se adapta a mis necesidades en la mayoría de las situaciones. Aquí está el código:
Configuración de manifiesto:
<?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();
}
}
Y Fragmento de muestra:
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);
}
}
Se puede encontrar en github .
Use la escucha de orientación
para realizar diferentes tareas en diferentes orientaciones.
@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);
}
}
Ponga este código debajo en su Activity
en Android Manifest
.
android:configChanges="orientation"
Esto no reiniciará su actividad cuando cambie la orientación.
Repara la orientación de la pantalla (horizontal o vertical) en AndroidManifest.xml
android: screenOrientation = " portrait "
o android:screenOrientation="landscape"
para esto no se llama a su método onResume ()
.
Uno de los mejores componentes de Android Architechure presentado por Google cumplirá con todos sus requisitos que es ViewModel.
Eso está diseñado para almacenar y administrar datos relacionados con la interfaz de usuario en el ciclo de vida, además de que permitirá que los datos sobrevivan a medida que la pantalla gira
class MyViewModel : ViewModel() {
Consulte esto: https://developer.android.com/topic/libraries / architecture / viewmodel
Puede bloquear la orientación actual de la pantalla con este código ...
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);
}