vie - développez votre première application android




Redémarrage de l'activité sur la rotation Android (20)

Dans mon application Android, lorsque je fais pivoter l'appareil ( onCreate glisser le clavier), mon Activity est redémarrée ( onCreate est appelé). Maintenant, c'est probablement comme ça que ça doit être, mais je fais beaucoup de réglages initiaux dans la méthode onCreate , donc j'ai besoin de:

  1. Mettez toute la configuration initiale dans une autre fonction afin qu'elle ne soit pas perdue lors de la rotation de l'appareil ou
  2. Faites en sorte que onCreate ne soit pas appelé à nouveau et que la mise en page s'ajuste ou
  3. Limitez l'application à juste portrait pour que onCreate ne soit pas appelé.

 onConfigurationChanged is called when the screen rotates. 
 (onCreate is no longer called when screen rotates due to manifest, see:  
 android:configChanges)

Quelle partie du manifeste le dit "n'appelez pas onCreate() "?

Aussi, les docs de Google disent d'éviter d'utiliser android:configChanges (sauf en dernier recours) .... Mais alors les méthodes alternatives qu'ils suggèrent tous DO utilisent android:configChanges .

Il a été mon expérience que l'émulateur TOUJOURS appelle onCreate() lors de la rotation.
Mais les appareils 1-2 sur lesquels je cours le même code ... ne le font pas. (Je ne sais pas pourquoi il y aurait une différence.)


Ajouter cette ligne android: configChanges = "orientation | screenSize" dans le manifeste


Après un moment d'essais et d'erreurs, j'ai trouvé une solution qui correspond à mes besoins dans la plupart des situations. Voici le code:

Configuration du manifeste:

<?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>

Activité principale:

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();
    }

}

Et échantillon Fragment:

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);
    }

}

Peut être trouvé sur github .


Au lieu d'essayer d'empêcher le onCreate() , essayez de vérifier si le savedInstanceState est passé dans l'événement pour voir s'il est nul ou non.

Par exemple, si j'ai une logique qui doit être exécutée lorsque l' Activity est réellement créée, et non à chaque changement d'orientation, je ne lance cette logique dans onCreate() que si savedInstanceState est null.

Sinon, je souhaite toujours que la mise en page soit correctement redessinée pour l'orientation.

public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_game_list);

        if(savedInstanceState == null){
            setupCloudMessaging();
        }
}

Je ne sais pas si c'est la réponse ultime, mais cela fonctionne pour moi.


Chaque fois que l'écran est pivoté, l'activité ouverte est terminée et onCreate () est appelée à nouveau.

1 . Vous pouvez faire une chose sauvegarder l'état d'activité lorsque l'écran est tourné de sorte que, Vous pouvez récupérer toutes les anciennes choses lorsque l'onCreate () de l'activité est appelée à nouveau. Référer this lien

2. Si vous voulez empêcher le redémarrage de l'activité, placez les lignes suivantes dans votre fichier manifest.xml.

  <activity android:name=".Youractivity"
  android:configChanges="orientation|screenSize"/>

Corrige l'orientation de l'écran (paysage ou portrait) dans AndroidManifest.xml

android:screenOrientation="portrait" ou android:screenOrientation="landscape"

pour cela, votre méthode onResume() n'est pas appelée.


Il est très simple de faire les étapes suivantes:

<activity
    android:name=".Test"
    android:configChanges="orientation|screenSize"
    android:screenOrientation="landscape" >
</activity>

Cela fonctionne pour moi:

Note: l' orientation dépend de votre requitation


Il y a plusieurs moyens de le faire:

Enregistrer l'état de l'activité

Vous pouvez enregistrer l'état de l'activité dans 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);
}

puis utilisez le bundle pour restaurer l'état.

@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
    }
}

Gérer les changements d'orientation par vous-même

Une autre alternative consiste à gérer les changements d'orientation par vous-même. Mais ceci n'est pas considéré comme une bonne pratique.

Ajoutez ceci à votre fichier manifeste.

android:configChanges="keyboardHidden|orientation"

pour Android 3.2 et ultérieur:

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
    }
}

Restreindre la rotation

Vous pouvez également limiter votre activité au mode portrait ou paysage pour éviter la rotation.

Ajoutez ceci à la balise d'activité dans votre fichier manifeste:

        android:screenOrientation="portrait"

Ou implémentez ceci par programme dans votre activité:

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}

Je viens de découvrir cette tradition:

Pour conserver l'activité en vie grâce à un changement d'orientation et la gérer via onConfigurationChanged , la documentation et l'exemple de code ci-dessus suggèrent cela dans le fichier manifeste:

android:configChanges="keyboardHidden|orientation"

qui a l'avantage supplémentaire que cela fonctionne toujours.

La légende du bonus est que l'omission de keyboardHidden peut sembler logique, mais elle provoque des échecs dans l'émulateur (pour Android 2.1 au moins): spécifier uniquement l' orientation fera que l'émulateur appellera OnCreate et onConfigurationChanged parfois, et seulement OnCreate autres fois.

Je n'ai pas vu l'échec sur un appareil, mais j'ai entendu parler de l'émulateur échouant pour les autres. Cela vaut donc la peine de documenter.


L'approche est utile mais incomplète lors de l'utilisation de Fragments.

Les fragments sont généralement recréés lors du changement de configuration. Si vous ne souhaitez pas que cela se produise, utilisez

setRetainInstance(true); dans le (s) constructeur (s) du Fragment

Cela entraînera la conservation des fragments pendant le changement de configuration.

http://developer.android.com/reference/android/app/Fragment.html#setRetainInstance(boolean)


La méthode onCreate est toujours appelée même lorsque vous modifiez l' orientation d'Android. Donc déplacer toutes les fonctionnalités lourdes de cette méthode ne va pas vous aider


Les gens disent que vous devriez utiliser

android:configChanges="keyboardHidden|orientation"

Mais la meilleure façon de gérer la rotation sous Android est d'utiliser la classe Loader. Ce n'est pas une classe célèbre (je ne sais pas pourquoi), mais c'est bien mieux que l'AsyncTask. Je l'ai utilisé pour compléter la tâche Popular-Movies-Part-1 du cours nano degré Android.

Films populaires partie 1

Bien sûr, vous pouvez stocker les valeurs ou les vues avec onSaveInstanceState et les lire avec onRestoreInstanceState. C'est à vous de voir.


Même si ce n'est pas "la manière Android", j'ai obtenu de très bons résultats en manipulant les changements d'orientation moi-même et en repositionnant simplement les widgets dans une vue pour prendre en compte l'orientation modifiée. C'est plus rapide que toute autre approche, car vos vues ne doivent pas être sauvegardées et restaurées. Il fournit également une expérience plus transparente à l'utilisateur, car les widgets repositionnés sont exactement les mêmes widgets, juste déplacés et / ou redimensionnés. Non seulement l'état du modèle, mais aussi l'état d'affichage, peuvent être préservés de cette manière.

RelativeLayout peut parfois être un bon choix pour une vue qui doit se réorienter de temps en temps. Vous fournissez simplement un ensemble de paramètres de mise en page du portrait et un ensemble de paramètres de mise en page paysagés, avec des règles de positionnement relatives différentes pour chaque widget enfant. Ensuite, dans votre méthode onConfigurationChanged() , vous passez la méthode appropriée à un appel setLayoutParams() sur chaque enfant. Si un contrôle enfant lui-même doit être réorienté en interne , vous appelez simplement une méthode sur cet enfant pour effectuer la réorientation. Cet enfant appelle également des méthodes sur l'un de ses contrôles enfants qui nécessitent une réorientation interne, et ainsi de suite.


Mettez ce code ci-dessous dans votre Activity dans le Android Manifest .

android:configChanges="orientation"

Cela ne redémarrera pas votre activité lorsque vous changeriez d'orientation.


Placez le code ci-dessous à l'intérieur de votre <activity> dans Manifest.xml :

android:configChanges="screenLayout|screenSize|orientation"

Utilisez l'écouteur d' orientation pour effectuer différentes tâches selon différentes orientations.

@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);
    }
}

ce que j'ai fait...

dans le manifeste, à la section d'activité, ajouté:

android:configChanges="keyboardHidden|orientation"

dans le code de l'activité, implémenté:

//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();
}

vous devez utiliser la méthode onSavedInstanceState pour stocker toute la valeur de son paramètre est a ce paquet

@Override
    public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
        super.onSaveInstanceState(outState, outPersistentState);
        outPersistentState.putBoolean("key",value);
    }

et utilise

@Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        savedInstanceState.getBoolean("key");
    } 

pour récupérer et définir la valeur pour afficher les objets, il va gérer les rotations de l'écran


Utilisation de la classe d'application

En fonction de ce que vous faites dans votre initialisation, vous pouvez envisager de créer une nouvelle classe qui étend Application et de déplacer votre code d'initialisation dans une méthode onCreate substituée onCreate sein de cette classe.

public class MyApplicationClass extends Application {
  @Override
  public void onCreate() {
    super.onCreate();
    // TODO Put your application initialization code here.
  }
}

Le onCreate de la classe d'application est appelé uniquement lorsque l'application entière est créée. Par conséquent, l'activité redémarre lors de l'orientation ou les modifications de la visibilité au clavier ne la déclenchent pas.

Il est recommandé d'exposer l'instance de cette classe en tant que singleton et d'exposer les variables d'application que vous initialisez à l'aide de getters et de setters.

Remarque: vous devrez spécifier le nom de votre nouvelle classe Application dans le manifeste pour qu'il soit enregistré et utilisé:

<application
    android:name="com.you.yourapp.MyApplicationClass"

Réagir aux changements de configuration [UPDATE: ceci est obsolète depuis API 13; voir l'alternative recommandée ]

Comme alternative, vous pouvez demander à votre application d'écouter les événements qui provoqueraient un redémarrage - comme l'orientation et les changements de visibilité du clavier - et de les gérer dans votre activité.

Commencez par ajouter le noeud android:configChanges noeud android:configChanges de votre activité.

android:configChanges="keyboardHidden|orientation"

ou pour Android 3.2 (niveau d'API 13) et plus récent :

android:configChanges="keyboardHidden|orientation|screenSize"

Ensuite, dans l'activité, remplacez la méthode onConfigurationChanged et appelez setContentView pour forcer la mise en page de l'interface graphique dans la nouvelle orientation.

@Override
public void onConfigurationChanged(Configuration newConfig) {
  super.onConfigurationChanged(newConfig);
  setContentView(R.layout.myLayout);
}

Vous pouvez verrouiller à l'orientation actuelle de l'écran en utilisant ce code ...

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);
        }






android-activity