Android
Cambios de orientación
Buscar..
Observaciones
Ahorro y restauración del estado de actividad
A medida que su actividad comienza a detenerse, el sistema llama a Guardar Estado de estado de onSaveInstanceState()
para que su actividad pueda guardar información de estado con una colección de pares clave-valor. La implementación predeterminada de este método guarda automáticamente la información sobre el estado de la jerarquía de vista de la actividad, como el texto en un widget EditText
o la posición de desplazamiento de un ListView
.
Para guardar información de estado adicional para su actividad, debe implementar onSaveInstanceState()
y agregar pares clave-valor al objeto Bundle. Por ejemplo:
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);
}
}
El sistema llamará a ese método antes de que se destruya una Actividad. Luego, más tarde, el sistema invocará onRestoreInstanceState
donde podremos restaurar el estado del paquete:
@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);
}
El estado de instancia también se puede restaurar en el método estándar de Actividad # onCreate, pero es conveniente hacerlo en onRestoreInstanceState
que garantiza que se haya realizado toda la inicialización y permita que las subclases decidan si usar la implementación predeterminada. Lea esta publicación de stackoverflow para más detalles.
Tenga en cuenta que no se garantiza que se llame a onSaveInstanceState
y onRestoreInstanceState
. Android invoca onSaveInstanceState()
cuando existe la posibilidad de que la actividad se destruya. Sin embargo, hay casos en los que se llama onSaveInstanceState
pero la actividad no se destruye y, como resultado, no se invoca onRestoreInstanceState
.
Guardando y restaurando el estado del fragmento
Los fragmentos también tienen un método onSaveInstanceState()
que se llama cuando es necesario guardar su estado:
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);
}
}
Luego podemos extraer datos de este estado guardado en 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;
}
}
Para que el estado del fragmento se guarde correctamente, debemos asegurarnos de que no estamos recreando innecesariamente el fragmento en los cambios de configuración. Esto significa tener cuidado de no reinicializar los fragmentos existentes cuando ya existen. Cualquier fragmento que se inicialice en una Actividad debe buscarse por etiqueta después de un cambio de configuración:
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();
}
}
}
Esto requiere que tengamos cuidado de incluir una etiqueta para la búsqueda cada vez que coloquemos un fragmento en la actividad dentro de una transacción:
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 este patrón simple, podemos reutilizar correctamente los fragmentos y restaurar su estado a través de los cambios de configuración.
Fragmentos de retención
En muchos casos, podemos evitar problemas cuando una Actividad se vuelve a crear simplemente usando fragmentos. Si sus puntos de vista y su estado están dentro de un fragmento, podemos conservar fácilmente el fragmento cuando la actividad se vuelva a crear:
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;
}
}
Este enfoque evita que el fragmento se destruya durante el ciclo de vida de la actividad. En su lugar, se mantienen dentro del Administrador de fragmentos. Ver los documentos oficiales de Android para más información .
Ahora puede verificar si el fragmento ya existe por etiqueta antes de crear uno y el fragmento conservará su estado en todos los cambios de configuración. Consulte la guía Manipulación de cambios en el tiempo de ejecución para obtener más detalles .
Orientación de la pantalla de bloqueo
Si desea bloquear el cambio de orientación de la pantalla de cualquier pantalla (actividad) de su aplicación de Android, solo necesita configurar la propiedad android:screenOrientation
de una <activity>
dentro del AndroidManifest.xml :
<activity
android:name="com.techblogon.screenorientationexample.MainActivity"
android:screenOrientation="portrait"
android:label="@string/app_name" >
<!-- ... -->
</activity>
Ahora esa actividad está obligada a mostrarse siempre en modo " retrato ".
Gestionar manualmente los cambios de configuración
Si su aplicación no necesita actualizar los recursos durante un cambio de configuración específico y usted tiene una limitación de rendimiento que requiere que evite el reinicio de la actividad, entonces puede declarar que su actividad maneja el cambio de configuración en sí, lo que evita que el sistema reinicie su actividad.
Sin embargo, esta técnica debe considerarse un último recurso cuando debe evitar reinicios debido a un cambio de configuración y no se recomienda para la mayoría de las aplicaciones. Para adoptar este enfoque, debemos agregar el nodo android:configChanges
a la actividad dentro de AndroidManifest.xml :
<activity android:name=".MyActivity"
android:configChanges="orientation|screenSize|keyboardHidden"
android:label="@string/app_name">
Ahora, cuando una de estas configuraciones cambia, la actividad no se reinicia sino que recibe una llamada 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();
}
}
Ver la documentación de Manipulación del cambio . Para obtener más información sobre qué cambios de configuración puede manejar en su actividad, consulte la documentación de android: configChanges y la clase de configuración .
Manejo AsyncTask
Problema:
- Si después de que se inicie
AsyncTask
se produce una rotación de pantalla, la actividad propietaria se destruye y seAsyncTask
crear. - Cuando finaliza
AsyncTask
, desea actualizar la interfaz de usuario que puede que ya no sea válida.
Solución:
Usando los cargadores , uno puede superar fácilmente la actividad de destrucción / recreación.
Ejemplo:
Actividad principal:
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:
Es importante utilizar la biblioteca de compatibilidad v4 o no, pero no use parte de una y parte de la otra, ya que dará lugar a errores de compilación. Para verificarlo, puede consultar las importaciones de android.support.v4.content
y android.content
(no debería tener ambos).
Bloquear la rotación de la pantalla programáticamente
Es muy común que durante el desarrollo, uno pueda encontrar muy útil bloquear / desbloquear la pantalla del dispositivo durante partes específicas del código .
Por ejemplo, al mostrar un cuadro de diálogo con información, es posible que el desarrollador desee bloquear la rotación de la pantalla para evitar que se cierre el cuadro de diálogo y que se vuelva a generar la actividad actual para desbloquearla nuevamente cuando se cierre el cuadro de diálogo.
Aunque podemos lograr un bloqueo de rotación desde el manifiesto haciendo:
<activity
android:name=".TheActivity"
android:screenOrientation="portrait"
android:label="@string/app_name" >
</activity>
Uno puede hacerlo programáticamente también haciendo lo siguiente:
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);
}
}
}
Y luego llamando a lo siguiente, para bloquear y desbloquear respectivamente la rotación del dispositivo
lockDeviceRotation(true)
y
lockDeviceRotation(false)