Android
Changements d'orientation
Recherche…
Remarques
Enregistrement et restauration de l'état d'activité
À mesure que votre activité commence à s'arrêter, le système appelle onSaveInstanceState()
afin que votre activité puisse enregistrer les informations d'état avec une collection de paires clé-valeur. L'implémentation par défaut de cette méthode enregistre automatiquement les informations sur l'état de la hiérarchie de vues de l'activité, telles que le texte d'un widget EditText
ou la position de défilement d'un objet ListView
.
Pour enregistrer des informations d'état supplémentaires pour votre activité, vous devez implémenter onSaveInstanceState()
et ajouter des paires clé-valeur à l'objet Bundle. Par exemple:
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);
}
}
Le système appellera cette méthode avant qu'une activité ne soit détruite. Ensuite, le système appellera onRestoreInstanceState
où nous pouvons restaurer l'état du 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);
}
L'état de l'instance peut également être restauré dans la méthode standard Activity # onCreate, mais il est pratique de le faire dans onRestoreInstanceState
qui garantit que toutes les initialisations ont été effectuées et permet aux sous-classes de décider si elles doivent ou non être implémentées. Lisez ce post stackoverflow pour plus de détails.
Notez que onSaveInstanceState
et onRestoreInstanceState
ne sont pas garantis pour être appelés ensemble. Android appelle onSaveInstanceState()
lorsqu'il y a une chance que l'activité soit détruite. Cependant, il existe des cas où onSaveInstanceState
est appelé mais l'activité n'est pas détruite et par conséquent onRestoreInstanceState
n'est pas onRestoreInstanceState
.
Enregistrement et restauration de l'état des fragments
Les fragments ont également une méthode onSaveInstanceState()
appelée lorsque leur état doit être enregistré:
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);
}
}
Ensuite, nous pouvons extraire des données de cet état enregistré dans 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;
}
}
Pour que l'état de fragment soit correctement enregistré, nous devons nous assurer que nous ne recréons pas inutilement le fragment lors des modifications de configuration. Cela signifie faire attention à ne pas réinitialiser les fragments existants lorsqu'ils existent déjà. Tout fragment initialisé dans une activité doit être recherché par balise après un changement de configuration:
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();
}
}
}
Cela nous oblige à faire attention à inclure une balise pour la recherche chaque fois que l'on met un fragment dans l'activité dans une transaction:
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();
}
}
}
Avec ce modèle simple, nous pouvons réutiliser correctement les fragments et restaurer leur état dans les modifications de configuration.
Conservation des fragments
Dans de nombreux cas, nous pouvons éviter les problèmes lorsqu'une activité est recréée en utilisant simplement des fragments. Si vos vues et votre état se trouvent dans un fragment, vous pouvez facilement conserver le fragment lorsque l’activité est recréée:
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;
}
}
Cette approche empêche le fragment d'être détruit pendant le cycle de vie de l'activité. Ils sont conservés dans le gestionnaire de fragments. Voir les documents officiels Android pour plus d' informations .
Maintenant, vous pouvez vérifier si le fragment existe déjà par tag avant d'en créer un et le fragment conservera son état dans toutes les modifications de configuration. Voir le guide Handling Runtime Changes pour plus de détails .
Orientation de l'écran de verrouillage
Si vous souhaitez verrouiller le changement d’orientation de l’écran (activité) de votre application Android, il vous suffit de définir la propriété android:screenOrientation
d’une <activity>
dans le fichier AndroidManifest.xml :
<activity
android:name="com.techblogon.screenorientationexample.MainActivity"
android:screenOrientation="portrait"
android:label="@string/app_name" >
<!-- ... -->
</activity>
Maintenant, cette activité est forcée à toujours être affichée en mode " portrait ".
Gestion manuelle des modifications de configuration
Si votre application n'a pas besoin de mettre à jour les ressources lors d'un changement de configuration spécifique et que vous avez une limitation de performance qui vous oblige à éviter le redémarrage de l'activité, vous pouvez déclarer que votre activité gère elle-même le changement de configuration. activité.
Cependant, cette technique doit être considérée comme un dernier recours lorsque vous devez éviter les redémarrages en raison d'un changement de configuration et que cela n'est pas recommandé pour la plupart des applications. Pour adopter cette approche, nous devons ajouter le nœud android:configChanges
à l'activité dans le fichier AndroidManifest.xml :
<activity android:name=".MyActivity"
android:configChanges="orientation|screenSize|keyboardHidden"
android:label="@string/app_name">
Maintenant, lorsque l'une de ces configurations change, l'activité ne redémarre pas mais reçoit un appel à 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();
}
}
Voir la documentation sur la gestion du changement . Pour plus d'informations sur les modifications de configuration que vous pouvez gérer dans votre activité, consultez la documentation android: configChanges et la classe Configuration .
Gestion de la tâche asynchrone
Problème:
- Si, après le démarrage de
AsyncTask
il y a une rotation de l'écran, l'activité propriétaire est détruite et recréée. - Lorsque
AsyncTask
termine, il souhaite mettre à jour l'interface utilisateur qui pourrait ne plus être valide.
Solution:
En utilisant des chargeurs , on peut facilement surmonter l'activité de destruction / récréation.
Exemple:
Activité 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;
}
}
Remarque:
Il est important d'utiliser la bibliothèque de compatibilité v4 ou non, mais n'utilisez pas une partie de l'une ou l'autre partie, car cela entraînerait des erreurs de compilation. Pour vérifier, vous pouvez consulter les importations pour android.support.v4.content
et android.content
(vous ne devriez pas avoir les deux).
Verrouiller la rotation de l'écran par programmation
Il est très courant que pendant le développement, on puisse trouver très utile de verrouiller / déverrouiller l'écran de l'appareil pendant certaines parties du code .
Par exemple, tout en affichant une boîte de dialogue contenant des informations, le développeur peut vouloir verrouiller la rotation de l'écran pour empêcher le rejet de la boîte de dialogue et la reconstruction de l'activité en cours pour la déverrouiller .
Même si nous pouvons réaliser un verrouillage de rotation à partir du manifeste en effectuant:
<activity
android:name=".TheActivity"
android:screenOrientation="portrait"
android:label="@string/app_name" >
</activity>
On peut le faire aussi par programmation en procédant comme suit:
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);
}
}
}
Et puis appelant le suivant, pour verrouiller et déverrouiller respectivement la rotation de l'appareil
lockDeviceRotation(true)
et
lockDeviceRotation(false)