web-dev-qa-db-fra.com

Cycle de vie des fragments Android sur les changements d'orientation

Utilisation du package de compatibilité pour cibler 2.2 à l’aide de Fragments.

Après avoir recodé une activité pour utiliser des fragments dans une application, je ne pouvais pas faire fonctionner les changements d'orientation/la gestion d'état, j'ai donc créé une petite application de test avec un seul fragment et une fragmentation.

Les journaux des modifications d'orientation sont étranges, avec plusieurs appels aux fragments OnCreateView.

Il me manque évidemment quelque chose - comme détacher le fragment et le rattacher plutôt que de créer une nouvelle instance, mais je ne vois aucune documentation qui indiquerait où je me trompe.

Quelqu'un peut-il m'éclairer sur ce que je fais de mal ici, s'il te plaît ... Merci.

Le journal est comme suit après les changements d'orientation.

Initial creation
12-04 11:57:15.808: D/FragmentTest.FragmentTestActivity(3143): onCreate
12-04 11:57:15.945: D/FragmentTest.FragmentOne(3143): OnCreateView
12-04 11:57:16.081: D/FragmentTest.FragmentOne(3143): OnCreateView->SavedInstanceState null


Orientation Change 1
12-04 11:57:39.031: D/FragmentTest.FragmentOne(3143): onSaveInstanceState
12-04 11:57:39.031: D/FragmentTest.FragmentTestActivity(3143): onCreate
12-04 11:57:39.031: D/FragmentTest.FragmentOne(3143): OnCreateView
12-04 11:57:39.031: D/FragmentTest.FragmentOne(3143): OnCreateView->SavedInstanceState not null
12-04 11:57:39.031: D/FragmentTest.FragmentOne(3143): OnCreateView
12-04 11:57:39.167: D/FragmentTest.FragmentOne(3143): OnCreateView->SavedInstanceState null


Orientation Change 2
12-04 11:58:32.162: D/FragmentTest.FragmentOne(3143): onSaveInstanceState
12-04 11:58:32.162: D/FragmentTest.FragmentOne(3143): onSaveInstanceState
12-04 11:58:32.361: D/FragmentTest.FragmentTestActivity(3143): onCreate
12-04 11:58:32.361: D/FragmentTest.FragmentOne(3143): OnCreateView
12-04 11:58:32.361: D/FragmentTest.FragmentOne(3143): OnCreateView->SavedInstanceState not null
12-04 11:58:32.361: D/FragmentTest.FragmentOne(3143): OnCreateView
12-04 11:58:32.361: D/FragmentTest.FragmentOne(3143): OnCreateView->SavedInstanceState not null
12-04 11:58:32.498: D/FragmentTest.FragmentOne(3143): OnCreateView
12-04 11:58:32.498: D/FragmentTest.FragmentOne(3143): OnCreateView->SavedInstanceState null

Activité principale (FragmentActivity)

public class FragmentTestActivity extends FragmentActivity {
/** Called when the activity is first created. */

private static final String TAG = "FragmentTest.FragmentTestActivity";


FragmentManager mFragmentManager;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    Log.d(TAG, "onCreate");

    mFragmentManager = getSupportFragmentManager();
    FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();

    FragmentOne fragment = new FragmentOne();

    fragmentTransaction.add(R.id.fragment_container, fragment);
    fragmentTransaction.commit();
}

Et le fragment

public class FragmentOne extends Fragment {

private static final String TAG = "FragmentTest.FragmentOne";

EditText mEditText;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {

    Log.d(TAG, "OnCreateView");

    View v = inflater.inflate(R.layout.fragmentonelayout, container, false);

    // Retrieve the text editor, and restore the last saved state if needed.
    mEditText = (EditText)v.findViewById(R.id.editText1);

    if (savedInstanceState != null) {

        Log.d(TAG, "OnCreateView->SavedInstanceState not null");

        mEditText.setText(savedInstanceState.getCharSequence("text"));
    }
    else {
        Log.d(TAG,"OnCreateView->SavedInstanceState null");
    }
    return v;
}

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);

    Log.d(TAG, "FragmentOne.onSaveInstanceState");

    // Remember the current text, to restore if we later restart.
    outState.putCharSequence("text", mEditText.getText());
}

Manifeste

<uses-sdk Android:minSdkVersion="8" />

<application
    Android:icon="@drawable/ic_launcher"
    Android:label="@string/app_name" >
    <activity
        Android:label="@string/app_name"
        Android:name=".activities.FragmentTestActivity" 
        Android:configChanges="orientation">
        <intent-filter >
            <action Android:name="Android.intent.action.MAIN" />

            <category Android:name="Android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>
109
MartinS

Vous superposez vos fragments l'un sur l'autre.

Lorsqu'un changement de configuration survient, l'ancien fragment s'ajoute à la nouvelle activité lorsqu'il est recréé. C'est une douleur massive à l'arrière la plupart du temps.

Vous pouvez empêcher les erreurs de se produire en utilisant le même fragment plutôt que d'en recréer un nouveau. Ajoutez simplement ce code:

if (savedInstanceState == null) {
    // only create fragment if activity is started for the first time
    mFragmentManager = getSupportFragmentManager();
    FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();

    FragmentOne fragment = new FragmentOne();

    fragmentTransaction.add(R.id.fragment_container, fragment);
    fragmentTransaction.commit();
} else {        
    // do nothing - fragment is recreated automatically
}

Soyez averti cependant: des problèmes surviendront si vous essayez d'accéder aux vues d'activité de l'intérieur du fragment, car les cycles de vie changeront subtilement. (Obtenir des vues à partir d'une activité parent à partir d'un fragment n'est pas facile).

174
Graeme

Pour citer ce livre , "afin de garantir une expérience utilisateur cohérente. Android conserve la présentation du fragment et la pile d'arrière-plan associée lors du redémarrage d'une activité .__ en raison d'un changement de configuration." (p. 124)

Et la meilleure façon de procéder consiste à vérifier d'abord si la pile de fragments du fragment a déjà été remplie et à créer la nouvelle instance de fragment uniquement si ce n'est pas le cas:

@Override
public void onCreate(Bundle savedInstanceState) {

        ...    

    FragmentOne fragment = (FragmentOne) mFragmentManager.findFragmentById(R.id.fragment_container); 

    if (fragment == null) {
        FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
        fragmentTransaction.add(R.id.fragment_container, new FragmentOne());
        fragmentTransaction.commit();
    }
}
82
k29

La méthode onCreate () de votre activité est appelée après que l'orientation a changé, comme vous l'avez vu. Donc, n'exécutez pas FragmentTransaction qui ajoute le fragment après le changement d'orientation de votre activité.

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if (savedInstanceState==null) {
        //do your stuff
    }
}

Les fragments doivent et doivent être inchangés.

9
Αλέκος

Vous pouvez @Override le FragmentActivity en utilisant onSaveInstanceState(). Veillez à ne pas appeler la super.onSaveInstanceState() dans la méthode.

4
Victor.Chan

Nous devrions toujours essayer d'empêcher l'exception nullpointer, nous devons donc d'abord vérifier dans la méthode saveinstance les informations relatives aux ensembles. pour une brève explication pour vérifier ce blog link

public static class DetailsActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (getResources().getConfiguration().orientation
            == Configuration.ORIENTATION_LANDSCAPE) {
            // If the screen is now in landscape mode, we can show the
            // dialog in-line with the list so we don't need this activity.
            finish();
            return;
        }

        if (savedInstanceState == null) {
            // During initial setup, plug in the details fragment.
            DetailsFragment details = new DetailsFragment();
            details.setArguments(getIntent().getExtras());
            getFragmentManager().beginTransaction().add(Android.R.id.content, details).commit();
        }
    } 
}
0
abhi

Lors du changement de configuration, le cadre créera une nouvelle instance du fragment et l'ajoutera à l'activité. Donc au lieu de cela:

FragmentOne fragment = new FragmentOne();

fragmentTransaction.add(R.id.fragment_container, fragment);

faire ceci:

if (mFragmentManager.findFragmentByTag(FRAG1_TAG) == null) {
    FragmentOne fragment = new FragmentOne();

    fragmentTransaction.add(R.id.fragment_container, fragment, FRAG1_TAG);
}

Veuillez noter que le framework ajoute une nouvelle instance de FragmentOne lors du changement d'orientation à moins d'appeler setRetainInstance (true), auquel cas il ajoutera l'ancienne instance de FragmentOne.

0
vlazzle