В андроиде фрагмент (скажем FragA
) добавляется в backstack, а другой фрагмент (например FragB
) подходит к вершине. Теперь при ударе назад FragA
появляется вверху и вызывается onCreateView()
. Теперь у меня было FragA
в определенном состоянии до того, как FragB
было нажато поверх него.
Мой вопрос: как восстановить FragA
в предыдущее состояние? Есть ли способ сохранить состояние (например, сказать в Bundle), и если да, то какой метод мне следует переопределить?
Ответ 1
В руководстве фрагмента Пример 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);
}
}
Я новичок в Fragments, но это похоже на решение вашей проблемы;)
OnActivityCreated вызывается после возврата фрагмента из предыдущего стека.
Ответ 2
Фрагмент onSaveInstanceState(Bundle outState)
никогда не будет вызываться, если операция фрагмента не вызывает его сам по себе и прикрепленные фрагменты. Таким образом, этот метод не будет вызываться до тех пор, пока что-то (обычно вращение) не активирует активность до 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()
вместо них.
Ответ 3
Просто обратите внимание, что если вы работаете с фрагментами, используя ViewPager, это довольно легко. Вам нужно только вызвать этот метод: setOffscreenPageLimit()
.
Согласовать с документами:
Установите количество страниц, которые должны быть сохранены по обе стороны от текущей страницы в иерархии представления в состоянии ожидания. Страницы, превышающие этот предел, будут воссозданы из адаптера при необходимости.
Подобная проблема здесь
Ответ 4
Просто раздуйте свой вид за один раз.
Пример:
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;
}
}
Ответ 5
Я работал с проблемой, очень похожей на это. Поскольку я знал, что часто возвращаюсь к предыдущему фрагменту, я проверял, является ли фрагмент .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 для вашего node:
android:configChanges="orientation|screenSize"
В Android 3.0 и выше требуется screenSize
.
Удачи.
Ответ 6
Самое лучшее решение, которое я нашел, ниже:
onSavedInstanceState(): всегда вызывается внутри фрагмента, когда активность прекращается (перемещение активности из одного в другое или изменения конфигурации).
Поэтому, если мы вызываем несколько фрагментов при одной и той же активности, мы должны использовать следующий подход:
Используйте OnDestroyView() фрагмента и сохраните весь объект внутри этого метода.
затем
OnActivityCreated(): проверьте, является ли объект нулевым или нет (поскольку этот метод вызывает каждый раз). Теперь восстановите состояние объекта здесь.
Его работы всегда!
Ответ 7
если вы обрабатываете изменения конфигурации в активности вашего фрагмента, указанные в андроиде, как это показано
<activity
android:name=".courses.posts.EditPostActivity"
android:configChanges="keyboardHidden|orientation"
android:screenOrientation="unspecified" />
то onSaveInstanceState
фрагмента не будет вызываться, а объект savedInstanceState
всегда будет null.
Ответ 8
Не думаю, что onSaveInstanceState
- хорошее решение. он просто использует для деятельности, которая была разрушена.
Из android 3.0 Fragmen был менеджером FragmentManager, условие: одно действие, отображающее фрагменты manny, когда фрагмент добавлен (не заменяется: он будет воссоздан) в backStack, представление будет уничтожено. когда он возвращается к последнему, он будет отображаться по-прежнему.
Итак, я думаю, что documentManager и транзакция достаточно хороши для его обработки.
Ответ 9
Я использовал гибридный подход для фрагментов, содержащих представление списка. Кажется, это исполнено, поскольку я не заменяю текущий фрагмент, а добавляю новый фрагмент и скрываю текущий. У меня есть следующий метод в активности, в котором размещаются мои фрагменты:
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 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 state.");
lv.onRestoreInstanceState(mListViewState);
}
Ответ 10
В конце концов, попробовав многие из этих сложных решений, поскольку мне нужно было сохранить/восстановить только одно значение в моем фрагменте (содержимое EditText), и хотя это может быть не самое элегантное решение, создавая SharedPreference и хранение моего состояния там работало для меня
Ответ 11
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) {
}