[android] 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

따라서 내 관점에서 올바른 코드는 다음과 같아야합니다.

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가 제공 한 것입니다.

—- 원본 게시물 —-

실제로 지원 패키지 의 알려진 버그 입니다 (편집 : 실제로 버그가 아닙니다. @ 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가 “종료”되는 경우). 그렇지 않으면이 대화 상자 조각이 해당 리스너가 더 이상 존재하지 않을 때 등록 된 리스너에서 함수를 호출하려고 할 수 있습니다.


답변

@Natix 가 남긴 댓글 은 일부 사람들이 삭제했을 수있는 빠른 한 줄입니다.

이 문제에 대한 가장 간단한 해결책은 코드를 실행하기 전에 super.onActivityResult () 를 호출 하는 것입니다. 이것은 지원 라이브러리를 사용하는지 여부에 관계없이 작동하며 활동에서 행동 일관성을 유지합니다.

다음이 있습니다.

내가 이것에 대해 더 많이 읽을수록 내가 본 더 미친 해킹.

여전히 문제가 발생하는 경우 Alex Lockwood 가 확인해야합니다.


답변

Android 버그라고 생각합니다. 기본적으로 Android는 활동 / 조각 수명주기 (onStart () 이전)의 잘못된 지점에서 onActivityResult를 호출합니다.

버그는 https://issuetracker.google.com/issues/36929762 에서보고됩니다.

기본적으로 Intent를 나중에 onResume ()에서 처리 한 매개 변수로 저장하여 해결했습니다.

[편집] 요즘에는 2012 년에는 사용할 수 없었던이 문제에 대한 더 나은 솔루션이 있습니다. 다른 답변을 참조하십시오.


답변

편집 : 또 다른 옵션, 그리고 아마도 최고 (또는 적어도 지원 라이브러리가 기대하는 것 …)

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 의 소스 를 살펴본 결과 상태를 잃지 않고 조각을 재개하기 위해 내부 조각 관리자를 호출하는 것 같습니다.


여기에 나열되지 않은 솔루션을 찾았습니다. 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
}

이것은 나에게 더 깨끗하고 덜 해커 보인다.


답변

두 가지 DialogFragment 쇼 () 방법은 – 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 ​​상태가 사용자에게 예기치 않게 변경 되어도 괜찮은 경우에만 사용해야합니다. “


답변

onSaveInstanceState () 메서드를 호출 한 연결된 활동 후에는 대화 상자를 표시 할 수 없습니다. 분명히 onSaveInstanceState ()는 onActivityResult () 전에 호출됩니다. 따라서이 콜백 메서드 OnResumeFragment ()에서 대화 상자를 표시해야합니다. DialogFragment의 show () 메서드를 재정의 할 필요가 없습니다. 이것이 당신을 도울 것입니다.