Я написал пустышку, которая переключается между двумя фрагментами. Когда вы переходите от FragmentA к FragmentB, FragmentA добавляется в задний стек. Однако когда я возвращаюсь к FragmentA (нажав назад), создается совершенно новый FragmentA, и состояние, в котором он находился, теряется. У меня такое ощущение, что мне нужно то же, что и в этом вопросе, но я включил полный пример кода, чтобы помочь устранить проблему:
public class FooActivity extends Activity {
@Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(android.R.id.content, new FragmentA());
transaction.commit();
}
public void nextFragment() {
final FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(android.R.id.content, new FragmentB());
transaction.addToBackStack(null);
transaction.commit();
}
public static class FragmentA extends Fragment {
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final View main = inflater.inflate(R.layout.main, container, false);
main.findViewById(R.id.next_fragment_button).setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
((FooActivity) getActivity()).nextFragment();
}
});
return main;
}
@Override public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// Save some state!
}
}
public static class FragmentB extends Fragment {
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.b, container, false);
}
}
}
Добавлены некоторые сообщения журнала:
07-05 14:28:59.722 D/OMG ( 1260): FooActivity.onCreate
07-05 14:28:59.742 D/OMG ( 1260): FragmentA.onCreateView
07-05 14:28:59.742 D/OMG ( 1260): FooActivity.onResume
<Tap Button on FragmentA>
07-05 14:29:12.842 D/OMG ( 1260): FooActivity.nextFragment
07-05 14:29:12.852 D/OMG ( 1260): FragmentB.onCreateView
<Tap 'Back'>
07-05 14:29:16.792 D/OMG ( 1260): FragmentA.onCreateView
Он никогда не вызывает FragmentA.onSaveInstanceState и создает новый FragmentA при ответном ударе. Однако, если я нахожусь на FragmentA и блокирую экран, FragmentA.onSaveInstanceState действительно вызывается. Так странно ... я ошибаюсь, ожидая, что фрагмент, добавленный в задний стек, не нуждается в воссоздании? Вот что говорят доктора :
Принимая во внимание, что если вы вызываете addToBackStack () при удалении фрагмента, то этот фрагмент останавливается и будет возобновлен, если пользователь перейдет обратно.
ListView
. Похоже, слишком много прыжков, чтобы присоединить слушателя прокрутки и обновить переменную экземпляра.Ответы:
Если вы вернетесь к фрагменту из заднего стека, он не создаст фрагмент заново, а повторно использует тот же экземпляр и начнет с
onCreateView()
жизненного цикла фрагмента, см. Жизненный цикл фрагмента .Поэтому, если вы хотите сохранить состояние, вы должны использовать переменные экземпляра, а не полагаться на них
onSaveInstanceState()
.источник
По сравнению с Apple,
UINavigationController
иUIViewController
, Google не преуспевает в архитектуре программного обеспечения Android. И документ об AndroidFragment
не сильно помогает.Когда вы вводите FragmentB из FragmentA, существующий экземпляр FragmentA не уничтожается. Когда вы нажимаете Назад в FragmentB и возвращаетесь во FragmentA, мы не создаем новый экземпляр FragmentA.
onCreateView()
Будут вызваны существующие экземпляры FragmentA .Ключевым моментом является то, что мы не должны снова раздувать представление в FragmentA
onCreateView()
, потому что мы используем существующий экземпляр FragmentA. Нам нужно сохранить и повторно использовать rootView.Следующий код работает хорошо. Он не только сохраняет состояние фрагмента, но и снижает нагрузку на ОЗУ и ЦП (поскольку мы раздуваем компоновку только при необходимости). Я не могу поверить, что пример кода Google и документ никогда не упоминают об этом, но всегда раздувают макет .
Версия 1 (Не используйте версию 1. Используйте версию 2)
------ Обновление от 3 мая 2005 года: -------
Как уже упоминалось в комментариях, иногда
_rootView.getParent()
ноль вonCreateView
, что приводит к сбою. Версия 2 удаляет _rootView в onDestroyView (), как предложено dell116. Проверено на Android 4.0.3, 4.4.4, 5.1.0.Версия 2
ПРЕДУПРЕЖДЕНИЕ!!!
Это взлом! Хотя я использую его в своем приложении, вам нужно тщательно проверить и прочитать комментарии.
источник
Я думаю, что есть альтернативный способ достичь того, что вы ищете. Я не говорю, что это полное решение, но оно послужило цели в моем случае.
То, что я сделал, вместо замены фрагмента, я просто добавил целевой фрагмент. Так что в основном вы будете использовать
add()
метод вместоreplace()
.Что еще я сделал. Я скрываю свой текущий фрагмент, а также добавляю его в backstack.
Следовательно, он перекрывает новый фрагмент поверх текущего фрагмента, не разрушая его вид. (Проверьте, что его
onDestroyView()
метод не вызывается. Плюс добавление его вbackstate
дает мне преимущество возобновления фрагмента.Вот код:
AFAIK System вызывает только
onCreateView()
если представление уничтожено или не создано. Но здесь мы сохранили вид, не удаляя его из памяти. Так что это не создаст новый взгляд.И когда вы вернетесь из Destination Fragment, вы увидите последний
FragmentTransaction
вытолкнет последний удаляемый верхний фрагмент, который заставит самый верхний вид (SourceFragment) появиться на экране.КОММЕНТАРИЙ: Как я уже сказал, это не полное решение, так как оно не удаляет вид фрагмента Source и, следовательно, занимает больше памяти, чем обычно. Но все же, служить цели. Кроме того, мы используем совершенно другой механизм сокрытия вида вместо его замены, что не является традиционным.
Так что дело не в том, как вы поддерживаете состояние, а в том, как вы поддерживаете представление.
источник
Activity A{Fragment A --> Fragment B}
когда я снова запускаю приложение после нажатия кнопки «Домой»,onResume()
вызывается оба фрагмента и, следовательно, они начинают опрос. Как я могу это контролировать?Я бы предложил очень простое решение.
Возьмите ссылочную переменную View и установите представление в OnCreateView. Проверьте, существует ли представление в этой переменной, затем верните то же представление.
источник
if (_rootView.getParent() != null) { ((ViewGroup)_rootView.getParent()).removeView(_rootView); }
уместно очистить память?null
кfragmentView
переменному вonDestroy()
методе.onDestroyView()
. Эта очистка не происходит для нашей переменной представления резервной копии (здесьfragmentView
), и она вызовет утечку памяти, когда фрагмент обратно сложен / уничтожен. Вы можете найти ту же ссылку в [Общие причины утечек памяти] ( square.github.io/leakcanary/fundamentals/… ) во введении LeakCanery.Я столкнулся с этой проблемой во фрагменте, содержащем карту, в которой слишком много подробностей настройки для сохранения / перезагрузки. Мое решение состояло в том, чтобы сохранить этот фрагмент активным все время (аналогично тому, что упоминал @kaushal).
Скажем, у вас есть текущий фрагмент A и вы хотите отобразить фрагмент B. Обобщая последствия:
Следовательно, если вы хотите сохранить оба фрагмента «сохраненными», просто переключите их, используя hide () / show ().
Плюсы : простой и простой способ поддерживать работу нескольких фрагментов.
Минусы : вы используете намного больше памяти, чтобы все они работали. Могут возникнуть проблемы, например, отображение большого количества растровых изображений.
источник
onSaveInstanceState()
вызывается только в случае изменения конфигурации.Так как при переходе от одного фрагмента к другому не происходит никаких изменений конфигурации, поэтому нет вызова
onSaveInstanceState()
нет. В каком состоянии не сохраняются? Можете ли вы указать?Если вы введете какой-либо текст в EditText, он будет сохранен автоматически. Любой элемент пользовательского интерфейса без идентификатора является элементом, состояние просмотра которого не должно быть сохранено.
источник
onSaveInstanceState()
также вызывается, когда система уничтожает активность, потому что ей не хватает ресурсов.Здесь, так как
onSaveInstanceState
во фрагмент не вызывает, когда вы добавляете фрагмент в backstack. Жизненный цикл фрагмента в backstack при восстановлении начинаетсяonCreateView
и заканчивается,onDestroyView
покаonSaveInstanceState
вызывается междуonDestroyView
иonDestroy
. Мое решение - создать переменную экземпляра и инициализировать вonCreate
. Образец кода:И проверьте это в
onActivityCreated
:источник
источник
Моя проблема была похожей, но я преодолел ее, не сохранив фрагмент. Предположим, у вас есть действие, которое имеет 2 фрагмента - F1 и F2. F1 запускается изначально и, скажем, содержит некоторую информацию о пользователе, а затем при некотором условии появляется F2 с просьбой ввести дополнительный атрибут - номер телефона. Затем вы хотите, чтобы этот номер телефона вернулся к F1 и завершил регистрацию, но вы понимаете, что вся предыдущая информация пользователя потеряна, и у вас нет их предыдущих данных. Фрагмент воссоздается с нуля, и даже если вы сохранили эту информацию в
onSaveInstanceState
комплекте, он возвращается в нольonActivityCreated
.Решение. Сохраните требуемую информацию в качестве переменной экземпляра при вызове. Затем передайте эту переменную экземпляра в ваш фрагмент.
Итак, следуя моему примеру: перед отображением F2 я сохраняю пользовательские данные в переменной экземпляра с помощью обратного вызова. Затем я запускаю F2, пользователь вводит номер телефона и нажимает сохранить. Я использую другой обратный вызов в действии, собираю эту информацию и заменяю свой фрагмент F1, на этот раз он содержит данные пакета, которые я могу использовать.
Дополнительную информацию о обратных вызовах можно найти здесь: https://developer.android.com/training/basics/fragments/communicating.html
источник
источник
Замените фрагмент, используя следующий код:
Активность onBackPressed ():
источник
источник
Идеальное решение, которое находит старый фрагмент в стеке и загружает его, если существует в стеке.
используйте это как
источник