从onActivityResult显示DialogFragment


82

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

Answers:


75

如果您使用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);
    }
  }
}

13
+1,这是正确的答案。请注意,该类onResumeFragments()中不存在Activity。如果您使用的是basic Activity,则应该使用onPostResume()
Alex Lockwood

3
在实施此解决方案之前,请阅读此内容以了解其原因。这个问题的另一个解决方案的注释中隐藏着一个简单得多的解决方案。
树枝

1
调用super.onActivityResult不会阻止IllegalStateException,因此不能解决subj。问题
demaksee,2015年

1
这个问题是这个问题在Google上的第一个热门,但我认为接受的答案不是最佳答案。应该改用这个答案:stackoverflow.com/a/30429551/1226020
JHH

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

请注意,这是一个巨大的hack。实际上,我只是通过自己的对话框片段来注册原始片段。当其他对话片段执行某项操作(例如被驳回)时,它告诉所有听众它即将消失。我这样做是这样的:

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“消失”时),否则当该侦听器不再存在时,此对话框片段可能会尝试在已注册的侦听器上调用函数。


3
一个不需要复制源的解决方案...只需覆盖show()并捕获IllegalStateException
杰弗里·布拉特曼

1
如何修改DialogFragment的来源?还是可以发布帖子末尾提到的解决方案?
PiotrŚlesarew'12

1
@PeterSlesarew我发布了我的(非常特定的)解决方案。
Kurtis Nusbaum

9
Gah,这不是错误!Android框架故意抛出异常,因为在其中执行片段事务并不安全onActivityResult()!:尝试这种解决方案,而不是stackoverflow.com/questions/16265733/...
亚历克斯·洛克伍德

2
@AlexLockwood提出此问题时,文档未对此进行警告。此外,虽然您的解决方案看起来不错,现在,它并没有在2012年四月下班回来onPostResume,并onResumeFragments都相对较新增加的支持库。
hrnt 2014年

24

@Natix留下的评论是一些人可能已删除的一种简单的方法。

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

有:

我读得越多,看到的疯子就越多。

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


如果您有继承权怎么办?如果要在调用super之前先运行代码,则调用super.onActivityResult()可能是一个问题,请小心,超类可能在onActivityResult中具有自己的代码。
Ricard

super.onActivityResult(requestCode, resultCode, data)在任何代码之前添加,它解决了我的问题。但是,当添加继承或覆盖默认的onActivityResult时,我们应该手动处理onStart / onResume
mochadwi

14

我相信这是一个Android错误。基本上,Android在活动/片段生命周期的错误点(在onStart()之前)调用onActivityResult。

该错误报告在https://issuetracker.google.com/issues/36929762

我基本上通过将Intent作为参数存储起来,稍后在onResume()中处理了它来解决它。

[编辑]对于此问题,如今有更好的解决方案,而2012年还没有提供。请参阅其他答案。


8
实际上,这并不是一个错误。正如那里的评论所指出的,它清楚地指出了onActivityResult()之前所说的onResume()
Kurtis Nusbaum 2012年

2
您是否阅读了该错误的最新评论?错误在于onActivityResult()在onStart()之前调用,而不是在onResume()之前调用。
hrnt 2012年

啊,是的,这也是事实。错过了。尽管我仍然相信其他错误报告与我的问题更为相关。
Kurtis Nusbaum 2012年

调用onActivityResult时,它是明确定义的。因此,即使在某些情况下似乎不合适,也不能算是错误。
sstn 2012年

1
@sstn,您能详细说说吗?当调用onActivityResult时(在onResume之前立即),它是明确定义的。Android不会在onResume之前立即调用onActivityResult。因此,这是一个错误。
hrnt 2013年

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
}

我看了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
}

在我看来,这看起来更干净,也没有那么客气。


5
使用处理程序解决此问题只会增加延迟,因此使问题发生的可能性降低。但这并不能保证问题会消失!有点像使用来解决比赛条件Thread#sleep()
Alex Lockwood

27
打电话super.onActivityResult()是最简单的解决方案,应该可以接受!我偶然发现了丢失的超级调用,并为它添加了效果感到惊讶。这样一来,我就可以删除此页面中提到的一种老旧的技巧(将对话框保存到一个临时变量中并在中显示onResume())。
Natix

不错的解决方案。唯一的问题是onActivityResult()不会返回任何值指示片段是否处理了结果。
Michael

调用super.onActivityResult()不能解决我项目中的IllegalStateException崩溃
demaksee 2015年

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

}

覆写 show(FragmentTransaction, String)这种方法并不容易,因为它还应该修改原始DialogFragment代码中的一些内部变量,所以我不建议这样做-如果您要使用该方法,请尝试接受的答案中的建议(或杰弗里·布拉特曼(Jeffrey Blattman)。

使用commitAllowingStateLoss存在一些风险-文档指出“类似于commit(),但允许在保存活动状态后执行提交。这很危险,因为如果以后需要从其状态恢复活动,则提交可能会丢失。 ,因此仅应在UI状态可以在用户上意外更改的情况下使用。”


4

附加活动调用其方法onSaveInstanceState()后,您将无法显示对话框。显然,onSaveInstanceState()在onActivityResult()之前被调用。因此,您应该在此回调方法OnResumeFragment()中显示对话框,而无需覆盖DialogFragment的show()方法。希望这会帮助你。


3

我提出了第三个解决方案,部分基于hmt的解决方案。基本上,创建一个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");
}

3

onActivityResult()在onResume()之前执行。您需要在onResume()或更高版本中执行UI。

使用布尔值或您需要传达的结果,这两种方法之间都需要返回结果。

... 而已。简单。


2

我知道这已经回答了好一阵子了..但是比起我在这里看到的其他一些答案,有一种更简单的方法来完成此操作...在我的特定情况下,我需要显示片段onActivityResult()中的DialogFragment方法。

这是我用于处理该问题的代码,它可以很好地工作:

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

如在其他一些帖子中所提到的,如果您不小心,则由于状态丢失而提交会导致问题……就我而言,我只是向用户显示错误消息,并带有关闭对话框的按钮,因此如果丢失的状态没什么大不了的。

希望这可以帮助...


2

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

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

2

这是因为调用#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;
    }
}

0

我发现的最干净的解决方案是这样的:

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

0

我在进行.show(getSupportFragmentManager(), "MyDialog");活动时收到此错误。

请先尝试.show(getSupportFragmentManager().beginTransaction(), "MyDialog");

如果仍然无法解决问题,那么这篇文章(显示onActivityResult的DialogFragment)可以帮助我解决问题。


0

其他方式:

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


-3

众所周知,这个问题是由于onActivityResult()在onstart()之前被调用的,所以就像我在此代码中所做的那样,只需在onActivityResult()的开始时调用onstart()

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

永远不要直接调用Android生命周期方法。这些仅应由系统调用。
Chantell Osejo
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.