Buscar..


Observaciones

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

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 se AsyncTask 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) 


Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow