Android fragment - как сохранить состояния представлений во фрагменте, когда другой фрагмент помещается поверх него
в android, фрагмент (скажем FragA) добавляется в backstack и другой фрагмент (скажем FragB) выходит на вершину. Теперь на ответный удар FragA доходит до вершины и onCreateView() называется. Теперь у меня было FragA в определенном состоянии перед FragB его толкнули сверху.
мой вопрос: как я могу восстановить FragA в прежнее состояние ? Есть ли способ сохранить состояние (например, в пакете), и если да, то какой метод я должен переопределить ?
11 ответов:
на руководство фрагмент пример FragmentList вы можете найти:
@Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putInt("curChoice", mCurCheckPosition); }, который можно использовать позже, как это:
@Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); if (savedInstanceState != null) { // Restore last state for checked position. mCurCheckPosition = savedInstanceState.getInt("curChoice", 0); } }Я новичок в фрагментах, но это похоже на решение вашей проблемы ;) OnActivityCreated вызывается после возврата фрагмента из заднего стека.
фрагмента!--1--> никогда не будет вызван, если активность фрагмента не вызовет его на себя и прикрепленные фрагменты. Таким образом, этот метод не будет вызываться до тех пор, пока что-то (обычно вращение) не заставит активность
SaveInstanceStateи восстановить его позже. Но если у вас есть только одно действие и большой набор фрагментов внутри него (с интенсивным использованиемreplace) и приложение работает только в одной ориентации деятельностиonSaveInstanceState(Bundle outState)не может быть привлечен в течение длительного времени.Я знаю три возможных обходные приемы.
первый:
используйте аргументы фрагмента для хранения важных данных:
public class FragmentA extends Fragment { private static final String PERSISTENT_VARIABLE_BUNDLE_KEY = "persistentVariable"; private EditText persistentVariableEdit; public FragmentA() { setArguments(new Bundle()); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_a, null); persistentVariableEdit = (EditText) view.findViewById(R.id.editText); TextView proofTextView = (TextView) view.findViewById(R.id.textView); Bundle mySavedInstanceState = getArguments(); String persistentVariable = mySavedInstanceState.getString(PERSISTENT_VARIABLE_BUNDLE_KEY); proofTextView.setText(persistentVariable); view.findViewById(R.id.btnPushFragmentB).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { getFragmentManager() .beginTransaction() .replace(R.id.frameLayout, new FragmentB()) .addToBackStack(null) .commit(); } }); return view; } @Override public void onPause() { super.onPause(); String persistentVariable = persistentVariableEdit.getText().toString(); getArguments().putString(PERSISTENT_VARIABLE_BUNDLE_KEY, persistentVariable); } }второй, но менее педантичный способ - держите переменные в синглетах
третий - не
replace()фрагменты, ноadd()/show()/hide()их вместо.
просто обратите внимание, что если вы работаете с фрагментами с помощью ViewPager, это довольно легко. Вам нужно только вызвать этот метод:
setOffscreenPageLimit().Accordign к документам:
установите количество страниц, которые должны быть сохранены на любой стороне текущей страницы в иерархии представлений в состоянии ожидания. Страницы за пределами этого предела будут воссозданы из адаптера при необходимости.
просто раздуть свой взгляд на этот раз.
пример следующим образом:
public class AFragment extends Fragment { private View mRootView; @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if(mRootView==null){ mRootView = inflater.inflate(R.id.fragment_a, container, false); //...... } return mRootView; }}
Я работал с проблемой, очень похожей на эту. Поскольку я знал, что часто буду возвращаться к предыдущему фрагменту, я проверил, есть ли фрагмент
.isAdded()было правдой, и если да, то вместо того, чтобы делатьtransaction.replace()Я просто делаюtransaction.show(). Это предотвращает повторное создание фрагмента, если он уже находится в стеке-сохранение состояния не требуется.Fragment target = <my fragment>; FragmentTransaction transaction = getFragmentManager().beginTransaction(); transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN); if(target.isAdded()) { transaction.show(target); } else { transaction.addToBackStack(button_id + "stack_item"); transaction.replace(R.id.page_fragment, target); } transaction.commit();еще одна вещь, чтобы иметь в виду, что в то время как это сохраняет естественный порядок для самих фрагментов, вы все еще можете нужно обрабатывать саму активность, которая уничтожается и воссоздается при изменении ориентации (конфигурации). Чтобы обойти это в AndroidManifest.xml для вашего узла:
android:configChanges="orientation|screenSize"в Android 3.0 и выше,
screenSizeэто, видимо, требуется.удачи
лучшее решение, которое я нашел ниже:
onSavedInstanceState (): всегда вызывается внутри фрагмента, когда действие собирается завершить работу(переместить действие из одного в другой или изменения конфигурации). Так что если мы вызываем несколько фрагментов на одной и той же активности, то мы должны использовать следующий подход:
используйте OnDestroyView () фрагмента и сохраните весь объект внутри этого метода. Затем OnActivityCreated (): проверьте, что если объект null или нет(потому что этот метод вызывает каждый раз.) Теперь восстановите состояние объекта здесь.
его работы всегда!
Если вы обрабатываете изменения конфигурации в своей активности фрагмента, указанной в манифесте android, как это
<activity android:name=".courses.posts.EditPostActivity" android:configChanges="keyboardHidden|orientation" android:screenOrientation="unspecified" />тут
onSaveInstanceStateиз фрагмента не будет вызываться иsavedInstanceStateобъект всегда будет null.
я использовал гибридный подход для фрагментов, содержащих представления списка. Кажется, он работает, так как я не заменяю текущий фрагмент, а скорее добавляю новый фрагмент и скрываю текущий. У меня есть следующий метод в деятельности, которая содержит мои фрагменты:
public void addFragment(Fragment currentFragment, Fragment targetFragment, String tag) { FragmentManager fragmentManager = getSupportFragmentManager(); FragmentTransaction transaction = fragmentManager.beginTransaction(); transaction.setCustomAnimations(0,0,0,0); transaction.hide(currentFragment); // use a fragment tag, so that later on we can find the currently displayed fragment transaction.add(R.id.frame_layout, targetFragment, tag) .addToBackStack(tag) .commit(); }Я использую этот метод в моем фрагменте (содержащем представление списка) всякий раз, когда элемент списка нажат/нажат (и поэтому мне нужно запустить/отобразить фрагмент сведений):
FragmentManager fragmentManager = getActivity().getSupportFragmentManager(); SearchFragment currentFragment = (SearchFragment) fragmentManager.findFragmentByTag(getFragmentTags()[0]); DetailsFragment detailsFragment = DetailsFragment.newInstance("some object containing some details"); ((MainActivity) getActivity()).addFragment(currentFragment, detailsFragment, "Details");
getFragmentTags()возвращает массив строк, которые я использую в качестве тегов для различных фрагментов при добавлении нового фрагмента (см.transaction.addметодaddFragmentспособ выше).во фрагменте, содержащем представление списка, я делаю это в своем методе onPause ():
@Override public void onPause() { // keep the list view's state in memory ("save" it) // before adding a new fragment or replacing current fragment with a new one ListView lv = (ListView) getActivity().findViewById(R.id.listView); mListViewState = lv.onSaveInstanceState(); super.onPause(); }затем в onCreateView фрагмента (на самом деле в методе, который вызывается в onCreateView), я восстанавливаю состояние:
// Restore previous state (including selected item index and scroll position) if(mListViewState != null) { Log.d(TAG, "Restoring the listview's state."); lv.onRestoreInstanceState(mListViewState); }
Я не думаю, что
onSaveInstanceState- Это хорошее решение. его просто используют для деятельности, которая была уничтожена.из android 3.0 Fragmen был менеджером FragmentManager, условие: одно сопоставление фрагментов Мэнни, когда фрагмент добавляется (не заменяется: он будет воссоздан) в backStack, представление будет destored. вернувшись к последнему, он будет отображаться как и прежде.
поэтому я думаю, что fragmentManger и транзакция достаточно хороши, чтобы справиться с этим.
в конце концов, после попытки многих из этих сложных решений, поскольку мне нужно было только сохранить / восстановить одно значение в моем фрагменте (содержимое EditText), и хотя это может быть не самое элегантное решение, создание SharedPreference и сохранение моего состояния там работали для меня
private ViewPager viewPager; viewPager = (ViewPager) findViewById(R.id.pager); mAdapter = new TabsPagerAdapter(getSupportFragmentManager()); viewPager.setAdapter(mAdapter); viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageSelected(int position) { // on changing the page // make respected tab selected actionBar.setSelectedNavigationItem(position); } @Override public void onPageScrolled(int arg0, float arg1, int arg2) { } @Override public void onPageScrollStateChanged(int arg0) { } }); } @Override public void onTabReselected(Tab tab, FragmentTransaction ft) { } @Override public void onTabSelected(Tab tab, FragmentTransaction ft) { // on tab selected // show respected fragment view viewPager.setCurrentItem(tab.getPosition()); } @Override public void onTabUnselected(Tab tab, FragmentTransaction ft) { }
Comments