web-dev-qa-db-fra.com

Android - enregistrer / restaurer l'état du fragment

J'ai une activité dans laquelle je traverse plusieurs fragments. Dans chaque fragment, j'ai plusieurs vues (EditText, ListView, Map, Etc.).

Comment puis-je sauvegarder l'instance du fragment qui est affichée à ce moment? J'ai besoin que cela fonctionne lorsque l'activité est onPause() --> onResume(). Aussi, j'ai besoin que ça fonctionne quand je reviens d'un autre fragment (pop de backstack).

À partir du Activity principal, j'appelle le premier fragment, puis du fragment, j'appelle le suivant.

Code de mon activité:

public class Activity_Main extends FragmentActivity{

public static Fragment_1 fragment_1;
public static Fragment_2 fragment_2;
public static Fragment_3 fragment_3;
public static FragmentManager fragmentManager;

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

     fragment_1 = new Fragment_1();

     fragment_2 = new Fragment_2();

     fragment_3 = new Fragment_3();

     fragmentManager = getSupportFragmentManager();
     FragmentTransaction transaction_1 = fragmentManager.beginTransaction();
     transaction_1.replace(R.id.content_frame, fragment_1);
     transaction_1.commit();
}}

Alors voici le code pour un de mes fragments:

public class Fragment_1 extends Fragment {

      private EditText title;
      private Button go_next;


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

            View rootView = inflater.inflate(R.layout.fragment_1,
            container, false);

            title = (EditText) rootView.findViewById(R.id.title);

            go_next = (Button) rootView.findViewById(R.id.go_next);

            image.setOnClickListener(new View.OnClickListener() {

         @Override
         public void onClick(View v) {

                 FragmentTransaction transaction_2 = Activity_Main.fragmentManager
                .beginTransaction();

                 transaction_2.replace(R.id.content_frame,
                  Activity_Main.fragment_2);
                 transaction_2.addToBackStack(null);
                 transaction_2.commit();  

            });
        }}

J'ai cherché beaucoup d'informations mais rien de clair. Quelqu'un peut-il donner une solution claire et un exemple, s'il vous plaît?

43
Stanete

Lorsqu'un fragment est déplacé dans le pile, il n'est pas détruit. Toutes les variables d'instance restent là. C'est donc l'endroit idéal pour sauvegarder vos données. Dans onActivityCreated, vous vérifiez les conditions suivantes:

  1. Est-ce que le paquet! = Null? Si oui, c'est là que les données sont enregistrées (changement d'orientation probablement).
  2. Existe-t-il des données enregistrées dans des variables d'instance? Si oui, rétablissez votre état d'eux (ou peut-être ne faites rien, car tout est comme il se doit).
  3. Sinon, votre fragment est affiché pour la première fois, créez tout à nouveau.

Edit: Voici un exemple

public class ExampleFragment extends Fragment {
    private List<String> myData;

    @Override
    public void onSaveInstanceState(final Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putSerializable("list", (Serializable) myData);
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        if (savedInstanceState != null) {
            //probably orientation change
            myData = (List<String>) savedInstanceState.getSerializable("list");
        } else {
            if (myData != null) {
                //returning from backstack, data is fine, do nothing
            } else {
                //newly created, compute data
                myData = computeData();
            }
        }
    }
}
81
Kirill Rakhman

Le fragment Android présente des avantages et des inconvénients. Le plus désavantage du fragment est que lorsque vous voulez utiliser un fragment, vous en créez un. Lorsque vous l'utilisez, onCreateView du fragment est appelé à chaque fois. Si vous souhaitez conserver l’état des composants dans le fragment, vous devez enregistrer l’état du fragment et vous devez charger son état dans le suivant. Cela rend la vue fragmentée un peu lente et bizarre.

J'ai trouvé une solution et j'ai utilisé cette solution: "Tout va bien. Tout le monde peut essayer".

Lors de la première exécution de onCreateView, créez la vue en tant que variable globale. Lorsque vous appelez ce fragment pour la deuxième fois, onCreateView est appelé à nouveau, vous pouvez retourner cette vue globale. L'état du composant fragment sera conservé.

View view;

@Override
public View onCreateView(LayoutInflater inflater,
        @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    setActionBar(null);
    if (view != null) {
        if ((ViewGroup)view.getParent() != null)
            ((ViewGroup)view.getParent()).removeView(view);
        return view; 
    }
    view = inflater.inflate(R.layout.mylayout, container, false);
}
16
nebyan

Essaye ça :

@Override
protected void onPause() {
    super.onPause();
    if (getSupportFragmentManager().findFragmentByTag("MyFragment") != null)
        getSupportFragmentManager().findFragmentByTag("MyFragment").setRetainInstance(true);
}

@Override
protected void onResume() {
    super.onResume();
    if (getSupportFragmentManager().findFragmentByTag("MyFragment") != null)
        getSupportFragmentManager().findFragmentByTag("MyFragment").getRetainInstance();
}

J'espère que cela aidera.

Vous pouvez aussi écrire ceci dans la balise activity dans le fichier menifest:

  Android:configChanges="orientation|screenSize"

Bonne chance !!!

7
Sar

Afin de sauvegarder l’état du fragment, vous devez implémenter onSaveInstanceState(): "De la même manière qu’une activité, vous pouvez conserver l’état d’un fragment à l’aide d’un ensemble, au cas où le processus de l’activité serait tué et que vous deviez restaurer le Etat du fragment lors de la recréation de l'activité.Vous pouvez enregistrer l'état pendant le rappel du fragment par le onSaveInstanceState() du fragment et le restaurer au cours de onCreate(), onCreateView() ou onActivityCreated(). Pour plus d'informations sur la sauvegarde de l'état, voir le document Activités. "

http://developer.Android.com/guide/components/fragments.html#Lifecycle

4
Raanan

Comme indiqué ici: Pourquoi utiliser Fragment # setRetainInstance (boolean)?

vous pouvez aussi utiliser la méthode fragments setRetainInstance(true) comme ceci:

public class MyFragment extends Fragment {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // keep the fragment and all its data across screen rotation
        setRetainInstance(true);

    }
}
4
Richard R

Vous pouvez obtenir le fragment actuel de fragmentManager. Et s'il n'y en a pas dans le gestionnaire de fragments, vous pouvez créer Fragment_1

public class MainActivity extends FragmentActivity {


    public static Fragment_1 fragment_1;
    public static Fragment_2 fragment_2;
    public static Fragment_3 fragment_3;
    public static FragmentManager fragmentManager;


    @Override
    protected void onCreate(Bundle arg0) {
        super.onCreate(arg0);
        setContentView(R.layout.main);

        fragment_1 = (Fragment_1) fragmentManager.findFragmentByTag("fragment1");

        fragment_2  =(Fragment_2) fragmentManager.findFragmentByTag("fragment2");

        fragment_3 = (Fragment_3) fragmentManager.findFragmentByTag("fragment3");


        if(fragment_1==null && fragment_2==null && fragment_3==null){           
            fragment_1 = new Fragment_1();          
            fragmentManager.beginTransaction().replace(R.id.content_frame, fragment_1, "fragment1").commit();
        }


    }


}

aussi vous pouvez utiliser setRetainInstance pour true ce qu’il va faire ignorer la méthode onDestroy() dans fragment et votre application en arrière-plan et tuer votre application pour allouer plus de mémoire dont vous aurez besoin pour tout sauvegarder données dont vous avez besoin dans onSaveInstanceState bundle

public class Fragment_1 extends Fragment {


    private EditText title;
    private Button go_next;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRetainInstance(true); //Will ignore onDestroy Method (Nested Fragments no need this if parent have it)
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        onRestoreInstanceStae(savedInstanceState);
        return super.onCreateView(inflater, container, savedInstanceState);
    }


    //Here you can restore saved data in onSaveInstanceState Bundle
    private void onRestoreInstanceState(Bundle savedInstanceState){
        if(savedInstanceState!=null){
            String SomeText = savedInstanceState.getString("title");            
        }
    }

    //Here you Save your data
    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putString("title", "Some Text");
    }

}
0

Je ne suis pas tout à fait sûr si cette question vous dérange toujours, car cela fait plusieurs mois. Mais je voudrais partager comment j'ai traité avec cela. Voici le code source:

int FLAG = 0;
private View rootView;
private LinearLayout parentView;

/**
 * The fragment argument representing the section number for this fragment.
 */
private static final String ARG_SECTION_NUMBER = "section_number";

/**
 * Returns a new instance of this fragment for the given section number.
 */
public static Fragment2 newInstance(Bundle bundle) {
    Fragment2 fragment = new Fragment2();
    Bundle args = bundle;
    fragment.setArguments(args);
    return fragment;
}

public Fragment2() {

}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    super.onCreateView(inflater, container, savedInstanceState);
    Log.e("onCreateView","onCreateView");
    if(FLAG!=12321){
        rootView = inflater.inflate(R.layout.fragment_create_new_album, container, false);
        changeFLAG(12321);
    }       
    parentView=new LinearLayout(getActivity());
    parentView.addView(rootView);

    return parentView;
}

/* (non-Javadoc)
 * @see Android.support.v4.app.Fragment#onDestroy()
 */
@Override
public void onDestroy() {
    // TODO Auto-generated method stub
    super.onDestroy();
    Log.e("onDestroy","onDestroy");
}

/* (non-Javadoc)
 * @see Android.support.v4.app.Fragment#onStart()
 */
@Override
public void onStart() {
    // TODO Auto-generated method stub
    super.onStart();
    Log.e("onstart","onstart");
}

/* (non-Javadoc)
 * @see Android.support.v4.app.Fragment#onStop()
 */
@Override
public void onStop() {
    // TODO Auto-generated method stub
    super.onStop();
    if(false){
        Bundle savedInstance=getArguments();
        LinearLayout viewParent;

        viewParent= (LinearLayout) rootView.getParent();
        viewParent.removeView(rootView);

    }
    parentView.removeView(rootView);

    Log.e("onStop","onstop");
}
@Override
public void onPause() {
    super.onPause();
    Log.e("onpause","onpause");
}

@Override
public void onResume() {
    super.onResume();
    Log.e("onResume","onResume");
}

Et voici l'activité principale:

/**
 * Fragment managing the behaviors, interactions and presentation of the
 * navigation drawer.
 */
private NavigationDrawerFragment mNavigationDrawerFragment;

/**
 * Used to store the last screen title. For use in
 * {@link #restoreActionBar()}.
 */

public static boolean fragment2InstanceExists=false;
public static Fragment2 fragment2=null;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    setContentView(R.layout.activity_main);

    mNavigationDrawerFragment = (NavigationDrawerFragment) getSupportFragmentManager()
            .findFragmentById(R.id.navigation_drawer);
    mTitle = getTitle();

    // Set up the drawer.
    mNavigationDrawerFragment.setUp(R.id.navigation_drawer,
            (DrawerLayout) findViewById(R.id.drawer_layout));
}

@Override
public void onNavigationDrawerItemSelected(int position) {
    // update the main content by replacing fragments
    FragmentManager fragmentManager = getSupportFragmentManager();
    FragmentTransaction fragmentTransaction=fragmentManager.beginTransaction();
    switch(position){
    case 0:
        fragmentTransaction.addToBackStack(null);
        fragmentTransaction.replace(R.id.container, Fragment1.newInstance(position+1)).commit();
        break;
    case 1:

        Bundle bundle=new Bundle();
        bundle.putInt("source_of_create",CommonMethods.CREATE_FROM_ACTIVITY);

        if(!fragment2InstanceExists){
            fragment2=Fragment2.newInstance(bundle);
            fragment2InstanceExists=true;
        }
        fragmentTransaction.addToBackStack(null);
        fragmentTransaction.replace(R.id.container, fragment2).commit();

        break;
    case 2:
        fragmentTransaction.addToBackStack(null);
        fragmentTransaction.replace(R.id.container, FolderExplorerFragment.newInstance(position+1)).commit();
        break;
    default: 
        break;
    }
}

Le parentView est le point clé. Normalement, lorsque onCreateView, nous utilisons simplement return rootView. Mais maintenant, j'ajoute rootView à parentView, puis je retourne parentView. Pour empêcher "L'enfant spécifié a déjà un parent. Vous devez appeler removeView() sur l'erreur ...", nous devons appeler parentView.removeView(rootView) ou la méthode que j'ai fournie est inutile. Je voudrais aussi partager comment je l'ai trouvé. Tout d'abord, j'ai mis en place un booléen pour indiquer si l'instance existe. Lorsque l'instance existe, le rootView ne sera pas gonflé à nouveau. Mais alors, logcat a donné à l'enfant déjà un truc de parent, alors j'ai décidé d'utiliser un autre parent comme vue parent intermédiaire. Ça fonctionne comme ça.

J'espère que cela vous sera utile.

0
butch