从onActivityResult显示DialogFragment

我在我的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支持包。

如果你使用Android支持库,onResume方法是不正确的地方,在哪里玩碎片。 您应该在onResumeFragments方法中执行此操作,请参阅onResume方法说明: http : //developer.android.com/reference/android/support/v4/app/FragmentActivity.html#onResume%28%29

所以从我的angular度来看,正确的代码应该是:

 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提供的。

—-原创post—-

其实这是一个已知的错误与支持包(编辑:实际上不是一个错误,请参阅@ 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“消失”时),否则此对话框片段可能会在该监听器不再存在时尝试调用已注册侦听器上的函数。

Android在onStart()之前调用onActivityResult() onStart()

我通过基本上将Intent作为参数存储在onResume()来解决这个问题。

@Natix留下的评论是一些人可能已经删除的快速的一行。

解决这个问题最简单的方法是在运行你自己的代码之前调用super.onActivityResult() 。 无论您是否使用支持库,都可以工作,并在您的活动中保持行为一致性。

有:

  • 无需使用线程,处理程序或睡眠添加虚假的延迟 。
  • 无需提交允许状态丢失或子类来覆盖show()。 这不是一个错误,这是一个警告。 不要丢弃状态数据。 ( 另一个 例子 , 奖金 )
  • 不需要跟踪活动中的对话框片段 (你好,内存泄漏!)
  • 而上帝, 通过手动调用onStart() ,不需要混淆活动生命周期 。

我越读到这个更疯狂的黑客,我见过。

如果你仍然遇到问题,那么Alex Lockwood的那个是要检查的问题。

  • 不需要为onSaveInstanceState()编写任何代码 ( 另一个 )

编辑: 另一种select,可能是最好的(或至less是什么支持图书馆期望…)

如果您在Android支持库中使用DialogFragments,则应该使用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的源代码 ,看起来它正在调用一个内部的片段pipe理器来恢复片段而不会丢失状态。


我find了一个解决scheme,这里没有列出。 我创build一个处理程序,并在处理程序中启动对话框片段。 所以,编辑一下你的代码:

 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 } 

这对我来说似乎更干净,更不好听。

有两个DialogFragment show()方法 – show(FragmentManager manager, String tag)show(FragmentTransaction transaction, String tag)

如果要使用方法的FragmentManager版本(如原始问题),一个简单的解决scheme是重写此方法并使用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代码中的一些内部variables,所以我不会推荐它 – 如果你想使用该方法,那么尝试接受的build议回答(或Jeffrey Blattman的评论)。

使用commitAllowingStateLoss存在一些风险 – 文档状态为“Like commit()”,但是允许在一个activity的状态被保存之后执行commit,这是很危险的,因为如果活动需要稍后从状态中恢复,commit会丢失,所以只能在UI状态在用户意外改变的情况下使用。

附加的活动调用其方法onSaveInstanceState()后,不能显示对话框。 很明显,onSaveInstanceState()在onActivityResult()之前被调用。 所以你应该在这个callback方法OnResumeFragment()中显示你的对话框,你不需要重写DialogFragment的show()方法。 希望这会帮助你。

我想出了第三个解决scheme,部分基于hmt的解决scheme。 基本上,创build一个DialogFragments的ArrayList,显示在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()或更高版本中执行您的UI。

使用布尔值或任何你需要沟通的结果已经回来这两种方法之间。

… 而已。 简单。

我知道这是很久以前的答案..但有一个更简单的方法来做到这一点比我在这里看到的其他答案…在我的具体情况下,我需要显示从片段onActivityResult()的DialogFragment方法。

这是我处理的代码,它运行得非常漂亮:

 DialogFragment myFrag; //Don't forget to instantiate this FragmentTransaction trans = getActivity().getSupportFragmentManager().beginTransaction(); trans.add(myFrag, "MyDialogFragmentTag"); trans.commitAllowingStateLoss(); 

正如在其他一些职位上所提到的,如果你不小心的话,犯下状态丢失可能会导致问题…在我的情况下,我只是用一个buttonclosures对话框显示错误消息给用户,所以如果那是丢失的状态,这不是什么大不了的事情。

希望这可以帮助…

这是一个古老的问题,但我已经用最简单的方法解决了,我想:

 getActivity().runOnUiThread(new Runnable() { @Override public void run() { MsgUtils.toast(getString(R.string.msg_img_saved), getActivity().getApplicationContext()); } }); 

我find的最干净的解决scheme是这样的:

 @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"); 第一。

如果仍然不工作,这篇文章( 从onActivityResult显示DialogFragment )帮助我解决这个问题。

其他方式:

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

只需调用super.onActivityResult(requestCode, resultCode, data); 在处理片段之前

发生这种情况是因为当调用#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; } } 

大家都知道这个问题是因为onActivityResult()在onStart()之前调用的,所以只需在onActivityResult()开始时调用onstart()就像我在这段代码中所做的那样

 @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { onStart(); //write you code here }