Recherche…


Remarques

Référence: https://guides.codepath.com/android/Handling-Configuration-Changes#references

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) 


Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow