Показать диалог с помощью 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.

Ответ 1

Если вы используете библиотеку поддержки 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);
    }
  }
}

Ответ 2

EDIT: Не ошибка, а недостаток в структуре фрагментов. Лучшим ответом на этот вопрос является тот, который предоставлен @Arcao выше.

---- Оригинальное сообщение ----

На самом деле это известная ошибка с пакетом поддержки (отредактируйте: на самом деле не ошибка. см. комментарий @alex-lockwood). Опубликованная работа в комментариях отчета об ошибке состоит в том, чтобы изменить источник диалогового окна так:

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 "уходит" ), в противном случае этот фрагмент диалога может попытаться вызвать функции в зарегистрированном прослушивателе, когда этот прослушиватель больше не существует.

Ответ 3

Комментарий от @Natix - это быстрый однострочный шрифт, который некоторые люди могли удалить.

Простейшим решением этой проблемы является вызов super.onActivityResult() ПЕРЕД началом использования собственного кода. Это работает независимо от того, используете ли вы библиотеку поддержки или нет и поддерживаете поведенческую согласованность в своей деятельности.

Существует:

Чем больше я читал в этом, тем больше сумасшедших хаков я видел.

Если вы все еще сталкиваетесь с проблемами, то один из них Алекс Локвуд проверяет.

Ответ 4

Я считаю, что это ошибка Android. В основном Android вызывает onActivityResult в неправильной точке жизненного цикла действия/фрагмента (до onStart()).

Об ошибке сообщается в https://issuetracker.google.com/issues/36929762

Я решил эту проблему, сохранив Intent в качестве параметра, который позже обработал в onResume().

[ПРАВКА] В настоящее время есть лучшие решения для этой проблемы, которые не были доступны еще в 2012 году. Смотрите другие ответы.

Ответ 5

EDIT: Еще один вариант и, возможно, лучший (или, по крайней мере, то, что ожидает библиотека поддержки...)

Если вы используете 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
}

Это кажется более чистым и менее хриплым для меня.

Ответ 6

Есть два метода отображения 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(), но разрешает выполнение коммита после сохранения состояния активности. Это опасно, поскольку фиксация может быть потеряна, если действие необходимо будет восстановить позднее из его состояния, поэтому это должно использоваться только в тех случаях, когда пользовательское пользовательское состояние нормально меняется на пользователя".

Ответ 7

Вы не можете показать диалог после присоединенного действия, называемого его методом onSaveInstanceState(). Очевидно, onSaveInstanceState() вызывается перед onActivityResult(). Поэтому вы должны показать свое диалоговое окно в этом методе обратного вызова OnResumeFragment(), вам не нужно переопределять метод DialogFragment show(). Надеюсь, это поможет вам.

Ответ 8

Я придумал третье решение, частично основанное на решении 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");
}

Ответ 9

onActivityResult() выполняет перед onResume(). Вам нужно сделать свой пользовательский интерфейс в onResume() или позже.

Используйте логическое или все, что вам нужно сообщить, что результат вернулся между обоими этими методами.

... Это. Простой.

Ответ 10

Я знаю, что это ответ было довольно давно.. но есть намного более простой способ сделать это, чем некоторые другие ответы, которые я видел здесь... В моем конкретном случае мне нужно было показать DialogFragment из фрагментов onActivityResult().

Это мой код для обработки этого, и он прекрасно работает:

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

Как уже упоминалось в некоторых других сообщениях, фиксация с потерей состояния может вызвать проблемы, если вы не будете осторожны... в моем случае я просто отображал сообщение об ошибке пользователю с кнопкой, чтобы закрыть диалог, поэтому, если состояние этого потеряно, это не имеет большого значения.

Надеюсь, что это поможет...

Ответ 11

Это старый вопрос, но я решил самым простым способом, я думаю:

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

Ответ 12

Это происходит потому, что, когда вызывается #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;
    }
}

Ответ 13

Самое чистое решение, которое я нашел, это:

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

Ответ 14

Я получил эту ошибку, выполняя .show(getSupportFragmentManager(), "MyDialog"); в действии.

Сначала попробуйте .show(getSupportFragmentManager().beginTransaction(), "MyDialog");.

Если все еще не работает, этот пост (Показать диалоговое окно из onActivityResult) помогает мне решить проблему.

Ответ 15

Другой способ:

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

Ответ 16

просто вызовите super.onActivityResult(requestCode, resultCode, data); перед обработкой фрагмента

Ответ 17

Как вы все знаете, эта проблема связана с тем, что onActivityResult() вызывается перед onstart(), поэтому просто запускайте onstart() при запуске в onActivityResult(), как я сделал в этом коде

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