Подтвердить что ты не робот

Android - сохранение/восстановление состояния фрагмента

У меня есть Activity, в котором я просматриваю несколько фрагментов. В каждом фрагменте у меня есть несколько видов (EditText, ListView, Map и т.д.).

Как сохранить экземпляр фрагмента, который отображается в этот момент? Мне нужно, чтобы он работал, когда активность onPause() --> onResume(). Также мне нужно, чтобы он работал, когда я возвращаюсь из другого фрагмента (pop from backstack).

Из основного 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();  

            });
        }}

Я искал много информации, но ничего не понял. Может кто-нибудь дать ясное решение и пример, пожалуйста?

4b9b3361

Ответ 1

Когда фрагмент перемещается в backstack, он не уничтожается. Все переменные экземпляра остаются там. Таким образом, это место для сохранения ваших данных. В onActivityCreated вы проверяете следующие условия:

  • Является ли связка!= null? Если да, то, где сохраняются данные (возможно, изменение ориентации).
  • Есть ли данные, сохраненные в переменных экземпляра? Если да, восстановите свое состояние из них (или, возможно, ничего не сделайте, потому что все так, как должно быть).
  • В противном случае ваш фрагмент будет показан в первый раз, создайте все заново.

Изменить: Здесь пример

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

Ответ 2

Попробуйте следующее:

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

Удачи!!!

Ответ 3

Фрагмент 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);

Ответ 4

Чтобы сохранить состояние фрагмента, вам нужно реализовать onSaveInstanceState(): "Также как и действие, вы можете сохранить состояние фрагмента с помощью Bundle, если процесс активности убит, и вам нужно восстановить состояние фрагмента при воссоздании активности. Вы можете сохранить состояние во время фрагмента onSaveInstanceState() обратного вызова и восстановить его во время onCreate(), onCreateView() или onActivityCreated(). Дополнительные сведения о состоянии сохранения см. в документе" Действия".

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

Ответ 5

Как указано здесь: Зачем использовать 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

Вы можете получить текущий фрагмент из фрагментатора. И если их нет в менеджере фрагментов, вы можете создать 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() в фрагменте, и ваше приложение переместилось на землю и os убило ваше приложение, чтобы выделить больше памяти, вам нужно будет сохранить все данные, которые вам нужны в 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");
    }

}

Ответ 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 дал ребенку уже родительский предмет, поэтому я решил использовать другого родителя в качестве промежуточного родительского представления. Вот как это работает.

Надеюсь, что это будет полезно для вас.