Показать DialogFragment из onActivityResult
у меня есть следующий код в моем onActivityResult для моего фрагмента:
onActivityResult(int requestCode, int resultCode, Intent data){
//other code
ProgressFragment progFragment = new ProgressFragment();
progFragment.show(getActivity().getSupportFragmentManager(), PROG_DIALOG_TAG);
// other code
}
однако, я получаю следующую ошибку:
Caused by: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
кто-нибудь знает, что происходит, или как я могу это исправить? Я должен отметить, что я использую пакет поддержки Android.
17 ответов:
Если вы используете библиотеку поддержки Android, метод onResume не является правильным местом, где можно играть с фрагментами. Вы должны сделать это в методе onResumeFragments, см. описание метода onResume: http://developer.android.com/reference/android/support/v4/app/FragmentActivity.html#onResume%28%29
Так что правильный код с моей точки зрения должен быть:
private boolean mShowDialog = false; @Override protected void onActivityResult(int requestCode, int resultCode, Intent data){ super.onActivityResult(requestCode, resultCode, data); // remember that dialog should be shown mShowDialog = true; } @Override protected void onResumeFragments() { super.onResumeFragments(); // play with fragments here if (mShowDialog) { mShowDialog = false; // Show only if is necessary, otherwise FragmentManager will take care if (getSupportFragmentManager().findFragmentByTag(PROG_DIALOG_TAG) == null) { new ProgressFragment().show(getSupportFragmentManager(), PROG_DIALOG_TAG); } } }
изменить: Не баг, а скорее недостаток во фрагментах фреймворка. Лучший ответ на этот вопрос-тот, который предоставлен @Arcao выше.
---- исходное сообщение ----
на самом деле это известная ошибка С пакетом поддержки (edit: на самом деле не ошибка. см. комментарий @alex-lockwood). Опубликованная работа в комментариях к отчету об ошибке заключается в изменении источника DialogFragment следующим образом:
public int show(FragmentTransaction transaction, String tag) { return show(transaction, tag, false); } public int show(FragmentTransaction transaction, String tag, boolean allowStateLoss) { transaction.add(this, tag); mRemoved = false; mBackStackId = allowStateLoss ? transaction.commitAllowingStateLoss() : transaction.commit(); return mBackStackId; }обратите внимание, что это гигантский Хак. На самом деле я просто сделал свой собственный фрагмент диалога, который я мог зарегистрировать из исходного фрагмента. Когда этот другой фрагмент диалога делал что-то (например, был уволен), он сказал всем слушателям, что он уходит. Я сделал это так:
public static class PlayerPasswordFragment extends DialogFragment{ Player toJoin; EditText passwordEdit; Button okButton; PlayerListFragment playerListFragment = null; public void onCreate(Bundle icicle){ super.onCreate(icicle); toJoin = Player.unbundle(getArguments()); Log.d(TAG, "Player id in PasswordFragment: " + toJoin.getId()); } public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle icicle){ View v = inflater.inflate(R.layout.player_password, container, false); passwordEdit = (EditText)v.findViewById(R.id.player_password_edit); okButton = (Button)v.findViewById(R.id.ok_button); okButton.setOnClickListener(new View.OnClickListener(){ public void onClick(View v){ passwordEntered(); } }); getDialog().setTitle(R.string.password_required); return v; } public void passwordEntered(){ //TODO handle if they didn't type anything in playerListFragment.joinPlayer(toJoin, passwordEdit.getText().toString()); dismiss(); } public void registerPasswordEnteredListener(PlayerListFragment playerListFragment){ this.playerListFragment = playerListFragment; } public void unregisterPasswordEnteredListener(){ this.playerListFragment = null; } }Так что теперь у меня есть способ уведомить PlayerListFragment, когда что-то происходит. Обратите внимание, что очень важно, что вы вызываете unregisterPasswordEnteredListener соответствующим образом (в приведенном выше случае, когда когда-либо PlayerListFragment " идет away") в противном случае этот фрагмент диалога может попытаться вызвать функции на зарегистрированном прослушивателе, когда этот прослушиватель больше не существует.
комментарий, оставленный @Natix быстрый ОДН-вкладыш который некоторые люди могут извлечь.
самым простым решением этой проблемы является вызов супер.onActivityResult () перед запуском собственного кода. Это работает независимо от того, используете ли вы библиотеку поддержки или нет, и поддерживает поведенческую согласованность в вашей деятельности.
есть:
- не нужно добавить искусственные задержки с помощью потоков, обработчиков или спит.
- не нужно зафиксировать разрешение потери состояния или подкласс для переопределения show (). Это не ошибка, это предупреждение. Не выбрасывайте данные о состоянии. (еще один,пример бонус)
- не нужно отслеживание фрагментов диалога в вашей деятельности (привет утечкам памяти!)
- и ей-богу, не надо возиться с жизненным циклом активности, вызывая onStart() вручную.
чем больше я читаю в этом, тем больше безумных хаков я видел.
Если вы все еще сталкиваетесь с проблемами, то один по Алекс Локвуд это один, чтобы проверить.
Android звонки
onActivityResult()доonStart().я решил это, в основном сохраняя намерение в качестве параметра, который я позже обработал в
onResume().
есть два метода DialogFragment show () -
show(FragmentManager manager, String tag)иshow(FragmentTransaction transaction, String tag).если вы хотите использовать версию метода FragmentManager (как в исходном вопросе), простым решением является переопределение этого метода и использование commitAllowingStateLoss:
public class MyDialogFragment extends DialogFragment { @Override public void show(FragmentManager manager, String tag) { FragmentTransaction ft = manager.beginTransaction(); ft.add(this, tag); ft.commitAllowingStateLoss(); } }переопределение
show(FragmentTransaction, String)этот способ не так прост, потому что он также должен изменить некоторые внутренние переменные в исходном коде DialogFragment, поэтому я бы не рекомендовал его - если вы хотите использовать этот метод, то попробуйте предложения в принятый ответ (или комментарий от Джеффри блаттманн дал высокую оценку).существует некоторый риск в использовании commitAllowingStateLoss-в документации говорится " как commit (), но позволяет выполнять фиксацию после сохранения состояния действия. Это опасно, потому что фиксация может быть потеряна, если действие должно быть позже восстановлено из своего состояния, поэтому это должно использоваться только в тех случаях, когда состояние пользовательского интерфейса может неожиданно измениться для пользователя."
EDIT: Yet другое вариант, и, возможно, лучший (или, по крайней мере, то, что ожидает библиотека поддержки...)
Если вы используете DialogFragments с библиотекой поддержки Android, вы должны использовать подкласс FragmentActivity. Попробуйте следующее:
onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, intent); //other code ProgressFragment progFragment = new ProgressFragment(); progFragment.show(getActivity().getSupportFragmentManager(), PROG_DIALOG_TAG); // other code }Я взглянул на источник для FragmentActivity, и похоже, что он вызывает внутренний менеджер фрагментов, чтобы возобновить фрагменты без потери государство.
Я нашел решение, которое не указано здесь. Я создаю обработчик и запускаю фрагмент диалогового окна в обработчике. Итак, немного отредактировав свой код:
onActivityResult(int requestCode, int resultCode, Intent data) { //other code final FragmentManager manager = getActivity().getSupportFragmentManager(); Handler handler = new Handler(); handler.post(new Runnable() { public void run() { ProgressFragment progFragment = new ProgressFragment(); progFragment.show(manager, PROG_DIALOG_TAG); } }); // other code }Это кажется чище и менее замысловато для меня.
вы не можете показать диалог после того, как вложенное действие вызвало свой метод onSaveInstanceState (). Очевидно, что onSaveInstanceState () вызывается перед onActivityResult (). Таким образом, вы должны показать свой диалог в этом методе обратного вызова OnResumeFragment (), вам не нужно переопределять метод Show() DialogFragment. Надеюсь, это поможет вам.
Я придумал третье решение, частично основанное на решении hmt. В принципе, создайте ArrayList DialogFragments, который будет отображаться на onResume ();
ArrayList<DialogFragment> dialogList=new ArrayList<DialogFragment>(); //Some function, like onActivityResults { DialogFragment dialog=new DialogFragment(); dialogList.add(dialog); } protected void onResume() { super.onResume(); while (!dialogList.isEmpty()) dialogList.remove(0).show(getSupportFragmentManager(),"someDialog"); }
onActivityResult () выполняется перед onResume (). Вам нужно сделать свой пользовательский интерфейс в onResume () или позже.
используйте логическое значение или все, что вам нужно, чтобы сообщить, что результат вернулся между обоими этими методами.
... Вот и все. Простой.
Я знаю, что это был ответ довольно давно.. но есть гораздо более простой способ сделать это, чем некоторые другие ответы, которые я видел здесь... В моем конкретном случае мне нужно было показать DialogFragment из метода fragments onActivityResult ().
Это мой код для обработки этого, и он прекрасно работает:
DialogFragment myFrag; //Don't forget to instantiate this FragmentTransaction trans = getActivity().getSupportFragmentManager().beginTransaction(); trans.add(myFrag, "MyDialogFragmentTag"); trans.commitAllowingStateLoss();Как упоминалось в некоторых других сообщениях, фиксация с потерей состояния может вызвать проблемы, если вы не будете осторожны... в моем случае я был просто отображение сообщения об ошибке для пользователя с кнопкой, чтобы закрыть диалоговое окно, так что если что пропало, это не важно.
надеюсь, что это помогает...
Это старый вопрос, но я решил самым простым способом, я думаю:
getActivity().runOnUiThread(new Runnable() { @Override public void run() { MsgUtils.toast(getString(R.string.msg_img_saved), getActivity().getApplicationContext()); } });
самое чистое решение, которое я нашел это:
@Override public void onActivityResult(final int requestCode, final int resultCode, final Intent data) { new Handler().post(new Runnable() { @Override public void run() { onActivityResultDelayed(requestCode, resultCode, data); } }); } public void onActivityResultDelayed(int requestCode, int resultCode, Intent data) { // Move your onActivityResult() code here. }
Я получил эту ошибку при выполнении
.show(getSupportFragmentManager(), "MyDialog");в активности.попробовать
.show(getSupportFragmentManager().beginTransaction(), "MyDialog");первый.Если все еще не работает, этот пост (показать DialogFragment от onActivityResult) помогает мне решить проблему.
иначе:
@Override public void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { case Activity.RESULT_OK: new Handler(new Handler.Callback() { @Override public boolean handleMessage(Message m) { showErrorDialog(msg); return false; } }).sendEmptyMessage(0); break; default: super.onActivityResult(requestCode, resultCode, data); } } private void showErrorDialog(String msg) { // build and show dialog here }
это происходит потому, что при вызове #onActivityResult () родительское действие уже вызывает #onSaveInstanceState ()
Я бы использовал Runnable, чтобы" сохранить " действие (показать диалог) на #onActivityResult (), чтобы использовать его позже, когда активность будет готова.
при таком подходе мы гарантируем, что действие, которое мы хотим, всегда будет работать
@Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == YOUR_REQUEST_CODE) { mRunnable = new Runnable() { @Override public void run() { showDialog(); } }; } else { super.onActivityResult(requestCode, resultCode, data); } } @Override public void onStart() { super.onStart(); if (mRunnable != null) { mRunnable.run(); mRunnable = null; } }
Как вы все знаете, эта проблема из-за on onActivityResult() вызывает перед onstart(),поэтому просто вызовите onstart() при запуске в onActivityResult (), как я сделал в этом коде
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { onStart(); //write you code here }
Comments