【问题标题】:Android - save/restore fragment stateAndroid - 保存/恢复片段状态
【发布时间】:2014-04-25 15:59:36
【问题描述】:

我有一个 Activity,我在其中经历了几个片段。在每个片段中,我都有几个视图(EditText, ListView, Map 等)。

如何保存当时显示的片段实例?当活动为onPause() --> onResume() 时,我需要它工作。当我从另一个片段(从后台弹出)返回时,我也需要它工作。

我从主要的Activity 调用第一个片段,然后从片段中调用下一个片段。

我的活动代码:

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

然后这是我的片段之一的代码:

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

            });
        }}

我已经搜索了很多信息,但没有明确的信息。有人可以给出一个明确的解决方案和一个例子吗?

【问题讨论】:

标签: android android-fragments fragment onresume back-stack


【解决方案1】:

当一个片段被移动到后台堆栈时,它不会被销毁。所有实例变量都保留在那里。所以这是保存数据的地方。在onActivityCreated 中检查以下条件:

  1. 捆绑包 != null 吗?如果是,那就是保存数据的地方(可能是方向改变)。
  2. 实例变量中是否保存了数据?如果是,请从它们那里恢复您的状态(或者什么也不做,因为一切都应该如此)。
  3. 否则您的片段将首次显示,请重新创建所有内容。

编辑:这是一个例子

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

【讨论】:

  • 你能举个例子吗?
  • 你想要什么样的例子?我描述的三个步骤的代码?
  • 第二个。实例变量中存储的信息在哪里?以及如何取回这些信息。谢谢。
  • 每次展示Fragment都会调用onCreateView方法吗?正如你所看到的,我现在在那里声明我的观点。我需要改变什么吗?
  • @Stanete 这个帖子是解决方案吗?如果是,请考虑标记它。
【解决方案2】:

Android 片段有一些优点和一些缺点。 片段的最大缺点是,当您想使用片段时,您会创建它。 使用时,每次都会调用片段的onCreateView。如果要保留片段中组件的状态,则必须保存片段状态,并且必须在下图中加载其状态。 这使得片段视图有点慢和奇怪。

我找到了一个解决方案,并使用了这个解决方案:“一切都很棒。每个人都可以尝试”。

第一次运行onCreateView 时,将视图创建为全局变量。当你第二次调用这个片段onCreateView 时,你可以返回这个全局视图。片段组件状态将被保留。

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

【讨论】:

  • ((ViewGroup)view.getParent()).removeView(view);给出 NullPointerException,但如果我只使用返回视图;它工作正常!
  • @PavelBiryukov 你必须控制 (ViewGroup)view.getParent() 值。
  • 我也是 ((ViewGroup)view.getParent()).removeView(view);给出 NullPointerException
  • 你为什么要这样做..在对象中存储视图..说当片段进入后台时(transaction.replace case)片段被拆除但你仍然持有对视图的引用,它发生在 10这样的碎片......它将获得不必要的内存空间并可能导致内存不足异常
  • 嗨@TusharSaha,当片段加载回来时,将再次创建视图。查看视图可能会有延迟。并且视图状态也可能丢失。在此代码中,片段不会再次创建视图并保持视图状态。如果片段要被销毁,您可以删除视图。
【解决方案3】:

试试这个:

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

希望这会有所帮助。

您也可以将其写入清单文件中的活动标签:

  android:configChanges="orientation|screenSize"

祝你好运!!!

【讨论】:

  • 给出空指针异常。
  • android:configChanges="orientation|screenSize" 会导致许多其他问题,因为它会覆盖正常的 Android 行为。这很可能会添加意想不到的错误。我建议不要使用它。
【解决方案4】:

为了保存 Fragment 状态你需要实现onSaveInstanceState(): “和activity一样,你可以使用Bundle来保留fragment的状态,以防activity的进程被杀掉,需要在activity重新创建时恢复fragment状态。你可以在fragment的onSaveInstanceState()保存状态在onCreate()onCreateView()onActivityCreated() 期间回调并恢复它。有关保存状态的更多信息,请参阅活动文档。"

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

【讨论】:

  • onSaveInstanceState 是一个反复无常的 - 即使导航离开片段,它也不会总是被调用,有时即使被破坏 - 在此处查看此答案stackoverflow.com/a/20892548/4096214
【解决方案5】:

如此处所述:Why use Fragment#setRetainInstance(boolean)?

你也可以像这样使用片段方法setRetainInstance(true)

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

    }
}

【讨论】:

    【解决方案6】:

    您可以从 fragmentManager 获取当前的 Fragment。如果片段管理器中没有它们,您可以创建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();
            }
    
    
        }
    
    
    }
    

    您也可以使用setRetainInstance 来验证它会做什么,它会忽略片段中的onDestroy() 方法,并且您的应用程序将返回后台并且操作系统会杀死您的应用程序以分配更多内存,您需要将所有数据保存在其中onSaveInstanceState捆绑

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

    【讨论】:

      【解决方案7】:

      我不太确定这个问题是否仍然困扰着您,因为它已经过去了几个月。但我想分享一下我是如何处理这个问题的。 以下是源代码:

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

      这里是 MainActivity:

      /**
       * 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;
          }
      }
      

      parentView 是关键点。 通常,当onCreateView 时,我们只使用return rootView。但是现在,我将rootView添加到parentView,然后返回parentView。为了防止“指定的孩子已经有一个父母。您必须在...上调用removeView()”错误,我们需要调用parentView.removeView(rootView),否则我提供的方法没有用。 我也想分享我是如何找到它的。首先,我设置了一个布尔值来指示实例是否存在。当实例存在时,rootView 不会再次膨胀。但是后来,logcat给了child已经有了parent的东西,所以我决定用另一个parent作为中间的Parent View。这就是它的工作原理。

      希望对您有所帮助。

      【讨论】:

        【解决方案8】:

        如果您使用底部栏和 viewpager 的 insted,您想设置自定义片段替换逻辑并检索先前保存状态,您可以使用以下代码进行操作

         String current_frag_tag = null;
         String prev_frag_tag = null;
        
        
        
            @Override
            public void onTabSelected(TabLayout.Tab tab) {
           
        
                switch (tab.getPosition()) {
                    case 0:
        
                        replaceFragment(new Fragment1(), "Fragment1");
                        break;
        
                    case 1:
                        replaceFragment(new Fragment2(), "Fragment2");
                        break;
        
                    case 2:
                        replaceFragment(new Fragment3(), "Fragment3");
                        break;
        
                    case 3:
                       replaceFragment(new Fragment4(), "Fragment4");
                        break;
        
                    default:
                        replaceFragment(new Fragment1(), "Fragment1");
                        break;
        
                }
        
            public void replaceFragment(Fragment fragment, String tag) {
                if (current_frag_tag != null) {
                    prev_frag_tag = current_frag_tag;
                }
        
                current_frag_tag = tag;
        
        
                FragmentManager manager = null;
                try {
                    manager = requireActivity().getSupportFragmentManager();
                    FragmentTransaction ft = manager.beginTransaction();
        
                    if (manager.findFragmentByTag(current_frag_tag) == null) { // No fragment in backStack with same tag..
                        ft.add(R.id.viewpagerLayout, fragment, current_frag_tag);
        
                        if (prev_frag_tag != null) {
                            try {
                                ft.hide(Objects.requireNonNull(manager.findFragmentByTag(prev_frag_tag)));
                            } catch (NullPointerException e) {
                                e.printStackTrace();
                            }
                        }
        //            ft.show(manager.findFragmentByTag(current_frag_tag));
                        ft.addToBackStack(current_frag_tag);
                        ft.commit();
        
                    } else {
        
                        try {
                            ft.hide(Objects.requireNonNull(manager.findFragmentByTag(prev_frag_tag)))
                                    .show(Objects.requireNonNull(manager.findFragmentByTag(current_frag_tag))).commit();
                        } catch (NullPointerException e) {
                            e.printStackTrace();
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
        
        
        
        
            }
        

        在子片段内部,您可以使用以下方法访问片段是否可见 注意:您必须在子片段中实现以下方法

        @Override
            public void onHiddenChanged(boolean hidden) {
                super.onHiddenChanged(hidden);
        
                try {
                    if(hidden){
                        adapter.getFragment(mainVideoBinding.viewPagerVideoMain.getCurrentItem()).onPause();
                    }else{
                        adapter.getFragment(mainVideoBinding.viewPagerVideoMain.getCurrentItem()).onResume();
                    }
                }catch (Exception e){
               }
        
            }
        

        【讨论】:

          猜你喜欢
          • 2013-04-04
          • 2015-10-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-01-11
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多