从onActivityResult显示DialogFragment

Kur*_*aum 79 android android-fragments android-dialogfragment

我的onActivityResult中有以下代码用于我的片段:

onActivityResult(int requestCode, int resultCode, Intent data){
   //other code
   ProgressFragment progFragment = new ProgressFragment();  
   progFragment.show(getActivity().getSupportFragmentManager(), PROG_DIALOG_TAG);
   // other code
}
Run Code Online (Sandbox Code Playgroud)

但是,我收到以下错误:

Caused by: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState   
Run Code Online (Sandbox Code Playgroud)

有谁知道发生了什么,或者我如何解决这个问题?我应该注意到我正在使用Android支持包.

Arc*_*cao 73

如果你使用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);
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

  • +1,这是正确的答案。请注意,“Activity”类中不存在“onResumeFragments()”。如果您使用的是基本的“Activity”,则应改用“onPostResume()”。 (12认同)
  • 在实施此解决方案之前,请[阅读本文](http://stackoverflow.com/a/30429551/253704)了解为什么这是一个黑客攻击.在这个问题的另一个解决方案的评论中隐藏着一个更简单的解决方案. (3认同)
  • 这个问题是谷歌针对这个问题的第一次点击,但在我看来,接受的答案并不是最好的答案。应接受此答案:http://stackoverflow.com/a/30429551/1226020 (2认同)

Kur*_*aum 27

编辑:不是一个错误,但更多的是片段框架的缺陷.这个问题的更好答案是上面提到的@Arcao.

----原帖----

实际上这是支持包的一个已知错误(编辑:实际上不是一个错误.请参阅@ 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;
}
Run Code Online (Sandbox Code Playgroud)

请注意,这是一个巨大的黑客.我实际做的方式就是制作我自己的对话框片段,我可以从原始片段注册.当其他对话片段做了事情(比如被解雇)时,它告诉任何听众它已经消失了.我是这样做的:

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;
 }
}
Run Code Online (Sandbox Code Playgroud)

所以现在我有办法在事情发生时通知PlayerListFragment.请注意,正确调用unregisterPasswordEnteredListener非常重要(在上述情况下,当PlayerListFragment"消失"时),否则此对话框片段可能会尝试在该侦听器不再存在时调用已注册侦听器上的函数.

  • Gah,这**不是**一个错误!Android 框架故意抛出异常,因为在 `onActivityResult()` 中执行片段事务是不安全的!试试这个解决方案:http://stackoverflow.com/questions/16265733/failure-delivering-result-onactivityforresult/18345899#18345899 (9认同)
  • 一个不需要复制源的解决方案......只需覆盖`show()`,并捕获`IllegalStateException`. (3认同)
  • @AlexLockwood当提出这个问题时,文档没有对此提出警告.此外,虽然您的解决方案看起来很好_now_,但它在2012年4月没有工作.`onPostResume`和`onResumeFragments`都是支持库的新增功能. (2认同)

twi*_*wig 21

@Natix留下的评论是一个快速的单行,有些人可能已经删除.

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

有:

我读到的越多,我看到的更疯狂的黑客.

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


hrn*_*rnt 14

Android __CODE__之前的电话__CODE__.

我通过基本上将Intent存储为我稍后处理的参数来解决它__CODE__.

  • 实际上这不是一个真正的错误.正如在那里的评论中指出的那样,它明确指出`onActivityResult()`在`onResume()之前被调用. (8认同)
  • 您是否阅读了该错误的最新评论?错误在于onActivityResult()在onStart()之前调用,而不是在onResume()之前调用。 (2认同)

小智 11

编辑:还有另一种选择,可能是最好的(或者,至少支持库期望的......)

如果您将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
}
Run Code Online (Sandbox Code Playgroud)

我看了一下FragmentActivity 的源代码,看起来它正在调用一个内部片段管理器,以便在不丢失状态的情况下恢复片段.


我找到了一个未列在此处的解决方案.我创建一个Handler,并在Handler中启动对话框片段.所以,编辑你的代码:

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
}
Run Code Online (Sandbox Code Playgroud)

这对我来说似乎更干净,也更不那么惹人讨厌.

  • **调用`super.onActivityResult()`是最简单的工作解决方案,它可能应该是接受的答案!**我注意到偶然发现了缺少的超级呼叫,并且惊喜地发现添加它只是起作用.这允许我删除此页面中提到的一个旧hacks(将对话框保存为临时变量并在`onResume()`中显示它). (26认同)
  • 使用 Handler 来解决这个问题只会增加一个延迟,因此使问题发生的可能性 * 更不可能 *。但这并不能保证问题会消失!这有点像使用 Thread#sleep() 解决竞争条件。 (5认同)

gke*_*kee 9

有两个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();
  }

}
Run Code Online (Sandbox Code Playgroud)

覆盖show(FragmentTransaction, String)这种方式并不容易,因为它还应该修改原始DialogFragment代码中的一些内部变量,所以我不推荐它 - 如果你想使用那个方法,那么尝试接受的答案中的建议(或者来自的注释)杰弗里布拉特曼).

使用commitAllowingStateLoss存在一些风险 - 文档声明"Like commit()"但允许在保存活动状态后执行提交.这很危险,因为如果活动需要稍后从状态恢复,则提交可能会丢失,所以这只应该用于UI状态可以在用户上意外更改的情况."