1

J'ai des difficultés à rendre mon application persistante. Lorsque je fais pivoter mon téléphone, les données sur l'écran ne changent pas. Mais après avoir cliqué sur un bouton pour récupérer un nouveau fragment, j'obtiens une erreur disant "Impossible d'effectuer cette action après onSaveInstanceState". J'ai googlé et vu des problèmes similaires, mais je ne sais toujours pas comment aborder et résoudre ce problème.Comment puis-je sauvegarder mes données sur l'orientation du téléphone?

J'ai une classe d'activité, une classe de contrôleur et deux classes de fragment. La classe d'activité a un navigationviewer avec 2 boutons qui déclenche une opération de fragmentation. C'est-à-dire qu'à chaque clic sur un bouton, il remplacera le fragment actuel par celui défini dans l'écouteur de boutons. La classe de mon contrôleur initalise le système et les fragments ne sont que l'interface utilisateur.

Ma classe d'activité:

public class LoggedInActivity extends AppCompatActivity { 
    private final String TAG = "LoggedInActivity: "; 
    private Controller controller; 
    private TextView navName; 
    private NavigationView navigationView; 


    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     Log.v(TAG, "onCreate"); 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_logged_in); 
     if(savedInstanceState == null) { 
      Log.v(TAG, "savedInstanceState == null"); 
      initComponents(); 
      setNavName(); 
      initListener(); 
      initializeSystem(); 
     } else { 
      Log.v(TAG, "savedInstanceState != null"); 
      initComponents(); 
      setNavName(); 
      initListener(); 
      this.controller = (Controller)savedInstanceState.getSerializable("controller"); 
     } 
    } 


    private void initComponents() { 
     navigationView = (NavigationView) findViewById(R.id.navigation_view); 
     View headerView = navigationView.getHeaderView(0); 
     navName = (TextView) headerView.findViewById(R.id.tv_name_surname); 

    } 

    private void initListener() { 
     navigationView.setNavigationItemSelectedListener(new MyNavigationItemListener()); 
    } 

    private void initializeSystem() { 
     Log.v(TAG, "new controller"); 
     controller = new Controller(this, null); 
    } 

    public void setFragment(Fragment fragment) { 
      FragmentManager fragmentManager = getFragmentManager(); 
      FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); 
      fragmentTransaction.replace(R.id.fragment_container_logged_in, fragment).commit(); 
    } 



    private class MyNavigationItemListener implements NavigationView.OnNavigationItemSelectedListener { 
     @Override 
     public boolean onNavigationItemSelected(@NonNull MenuItem item) { 
      switch(item.getItemId()) { 

       case R.id.drawer_summary: 
        controller.setFragmentSummary(); 
        break; 

       case R.id.drawer_income: 
        controller.setFragmentIncome(); 
        break; 
      } 
      return false; 
     } 
    } 


    @Override 
    protected void onSaveInstanceState(Bundle outState) { 
     outState.putSerializable("controller", controller); 
     super.onSaveInstanceState(outState); 
     Log.v(TAG, "onSaveInstanceState, saving the controller"); 
    } 

} 

Ma classe contrôleur:

public class Controller implements Serializable { 
    private final String TAG = "Controller: "; 

    /********************** Fragments ***********************/ 
    private Fragment_Income fragment_income; 
    private Fragment_Summary fragment_summary; 
    /********************************************************/ 

    /********************** Activities **********************/ 
    private LoggedInActivity logged_in_activity; 
    /********************************************************/ 


    public Controller(LoggedInActivity logged_in_activity) { 
     this.logged_in_activity = logged_in_activity; 
     initLoggedInFragments(); 
     setFragmentSummary(); 

     } 
    } 



    /* Initializes fragments that are connected to LoggedInActivity */ 
    private void initLoggedInFragments() { 
     fragment_income = new Fragment_Income(); 
     fragment_income.setController(this); 
     fragment_summary = new Fragment_Summary(); 
     fragment_summary.setController(this); 
    } 



    /* use to replace current fragment with the given one */ 
    private void replaceFragmentWith(Fragment fragment) { 
     logged_in_activity.setFragment(fragment); 
    } 



    /*********************************************************** 
    *   METHODS REGARDING FRAGMENT INCOME    * 
    **********************************************************/ 

    public void setFragmentIncome() { 
     replaceFragmentWith(fragment_income); 
    } 


    /* Summary fragment is started at first */ 
    public void setFragmentSummary() { 
     replaceFragmentWith(fragment_summary); 
    } 

} 

Fragment_Income:

public class Fragment_Income extends Fragment implements Serializable{ 
    private final String TAG = "Fragment_Income: "; 
    private Controller controller; 
    private FloatingActionButton fab_income; 
    private ListView lv_income; 
    private ArrayList<LvData> incomeData; 
    private LvAdapterIncome lvAdapterIncome; 

    public Fragment_Income() { 

    } 


    @Override 
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 
     Log.v(TAG, "onCreateView"); 
     View view = inflater.inflate(R.layout.fragment_income, container, false); // Inflate the layout for this fragment 
     if(savedInstanceState != null) { 
      this.controller = (Controller) savedInstanceState.getSerializable("controller"); 
     } 
     initComponents(view); 
     initListener(); 
     setupListView(); 
     return view; 
    } 

    private void initComponents(View view) { 
     fab_income = (FloatingActionButton) view.findViewById(R.id.fab_income); 
     lv_income = (ListView) view.findViewById(R.id.lv_income); 
    } 


    private void initListener() { 
     ButtonListener buttonListener = new ButtonListener(); 
     fab_income.setOnClickListener(buttonListener); 
    } 

    private void setupListView() { 
     if (incomeData == null) { // checks if incomeData have been initalized before, if so do not change array to defualt 
      incomeData = new ArrayList<>(); 
      lvAdapterIncome = new LvAdapterIncome(getContext(), incomeData); 
     } 
     lv_income.setAdapter(lvAdapterIncome); 

    } 


    public void setController(Controller controller) { 
     this.controller = controller; 
    } 


    @Override 
    public void onSaveInstanceState(Bundle outState) { 
     Log.v(TAG, "onSaveInstanceState, saving the controller"); 
     outState.putSerializable("controller", this.controller); 
     super.onSaveInstanceState(outState); 
    } 

} 

Fragment_Summary:

public class Fragment_Summary extends Fragment implements Serializable { 
    private static final String TAG = "Fragment_Summary: "; 
    private Controller controller; 
    private TextView tv_user; 
    private TextView tv_total_revenue; 
    private TextView tv_total_expenditure; 
    private TextView tv_balance; 
    private float totalRevenue; 
    private float totalExpenditure; 
    private float balance; 

    @Override 
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 
     View view = inflater.inflate(R.layout.fragment_summary, container, false);// Inflate the layout for this fragment 
     initComponents(view); 
     setUserName(); 
     if(savedInstanceState == null) { 
      //DO SOMETHING 
     } 
     return view; 
    } 

    private void addData() { 
     totalRevenue = controller.getTotalRevenue(); 
     totalExpenditure = controller.getTotalExpenditure(); 
     balance = totalRevenue - totalExpenditure; 

     tv_total_revenue.setText(String.valueOf(totalRevenue)); 
     tv_total_expenditure.setText(String.valueOf(totalExpenditure)); 
     tv_balance.setText(String.valueOf(balance)); 
    } 

    private void initComponents(View view) { 
     tv_user = (TextView)view.findViewById(R.id.tv_user); 
     tv_total_revenue = (TextView)view.findViewById(R.id.tv_revenue); 
     tv_total_expenditure = (TextView)view.findViewById(R.id.tv_sum_exp); 
     tv_balance = (TextView)view.findViewById(R.id.tv_balance); 
    } 


    @Override 
    public void onSaveInstanceState(Bundle outState) { 
     outState.putString("revenue", String.valueOf(balance)); 
     outState.putString("totalExpenditure", String.valueOf(balance)); 
     outState.putString("balance", String.valueOf(balance)); 
     super.onSaveInstanceState(outState); 
    } 


    public void setController(Controller controller) { 
     this.controller = controller; 
    } 

} 

J'ai supprimé tous les fichiers d'en-tête et des méthodes de mes cours becuase je tought ils ne sont pas pertinents pour ce problème.

Voici l'erreur journal:

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState 
    at android.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1434) 
    at android.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1452) 
    at android.app.BackStackRecord.commitInternal(BackStackRecord.java:708) 
    at android.app.BackStackRecord.commit(BackStackRecord.java:672) 
    at com.example.user.my_app.LoggedInActivity.setFragment(LoggedInActivity.java:85) 
    at com.example.user.my_app.Controller.replaceFragmentWith(Controller.java:89) 
    at com.example.user.my_app.Controller.setFragmentIncome(Controller.java:99) 
    at com.example.user.my_app.LoggedInActivity$MyNavigationItemListener.onNavigationItemSelected(LoggedInActivity.java:127) 
    at android.support.design.widget.NavigationView$1.onMenuItemSelected(NavigationView.java:156) 
    at android.support.v7.view.menu.MenuBuilder.dispatchMenuItemSelected(MenuBuilder.java:822) 
    at android.support.v7.view.menu.MenuItemImpl.invoke(MenuItemImpl.java:156) 
    at android.support.v7.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:969) 
    at android.support.design.internal.NavigationMenuPresenter$1.onClick(NavigationMenuPresenter.java:342) 
    at android.view.View.performClick(View.java:5637) 
    at android.view.View$PerformClick.run(View.java:22429) 
    at android.os.Handler.handleCallback(Handler.java:751) 
    at android.os.Handler.dispatchMessage(Handler.java:95) 
    at android.os.Looper.loop(Looper.java:154) 
    at android.app.ActivityThread.main(ActivityThread.java:6119) 
    at java.lang.reflect.Method.invoke(Native Method) 
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886) 
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776) 
+0

Essayez d'utiliser 'getSupportFragmentManager()' au lieu de 'getFragmentManager()' puisque vous utilisez l'aide 'AppCompatActivity' – ono

+0

didnt, je vous remercie pour la réponse cependant. Ce que je trouve étrange c'est que quand je change commit() en commitAllowingStateLoss(), le code me donne l'erreur suivante "IllegalStateException: l'activité a été détruite". Mais comment peut-il être détruit s'il a été créé dans la méthode onCreate? – Pakash

Répondre

1

Cela ressemble à une activité perte de l'État. Voir cet excellent article par Alex Lockwood intitulé "Fragment Transactions & Activity State Loss". Je me réfère à cela encore et encore.

Pour citer l'intro de l'affichage:

La trace de la pile et le message suivants exception a frappé StackOverflow jamais depuis la version initiale de nid d'abeille:

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1341) 
    at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1352) 
    at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:595) 
    at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:574) 

Ce poste vous expliquera pourquoi et quand cette exception est levée, et se terminera par plusieurs suggestions qui aideront à s'assurer qu'il ne plante jamais votre application à nouveau.