从onActivityResult显示DialogFragment

时间:2012-04-11 21:33:27

标签: 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
}

但是,我收到以下错误:

Caused by: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState   

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

17 个答案:

答案 0 :(得分:72)

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

答案 1 :(得分:26)

编辑: 不是错误,而是碎片框架中的更多缺陷。这个问题的更好答案是@Arcao上面提供的答案。

----原帖----

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

答案 2 :(得分:18)

@Natix留下的评论是一个人可能已删除的快速单行内容。

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

有:

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

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

答案 3 :(得分:14)

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

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

答案 4 :(得分: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的source,看起来它正在调用内部片段管理器,以便在不丢失状态的情况下恢复片段。


我找到了一个未列在此处的解决方案。我创建一个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
}

这对我来说似乎更干净,也更少了。

答案 5 :(得分: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存在一些风险 - 文档声明“Like commit()”但允许在保存活动状态后执行提交。这很危险,因为如果稍后需要恢复活动,则提交可能会丢失从它的状态来看,所以这只应该用于UI状态可以在用户上意外改变的情况。“

答案 6 :(得分:4)

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

答案 7 :(得分:3)

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

使用布尔值或您需要的任何内容来表明结果在这两种方法之间都会回来。

......就是这样。简单。

答案 8 :(得分: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");
}

答案 9 :(得分:2)

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

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

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

正如其他一些帖子中提到的那样,如果你不小心,那么承担国家损失会导致问题......在我的情况下,我只是向用户显示一条错误消息,并带有一个按钮来关闭对话框,所以如果它的状态丢失了,那就不大了。

希望这会有所帮助......

答案 10 :(得分:2)

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

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

答案 11 :(得分:1)

这是因为当调用#onActivityResult()时,父活动已经调用#onSaveInstanceState()

我会使用Runnable来保存&#34; #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;
    }
}

答案 12 :(得分: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.
}

答案 13 :(得分:0)

我在活动中.show(getSupportFragmentManager(), "MyDialog");时遇到此错误。

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

如果仍无效,此帖子(Show DialogFragment from onActivityResult)可帮助我解决问题。

答案 14 :(得分: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
}

答案 15 :(得分:0)

在处理片段

之前调用super.onActivityResult(requestCode, resultCode, data);

答案 16 :(得分:-2)

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

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