OnActivityResult'dan DialogFragment'ı Göster


82

Benim bir parçam için onActivityResult'umda şu koda sahibim:

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

Ancak şu hatayı alıyorum:

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

Neler olduğunu veya bunu nasıl düzeltebileceğimi bilen var mı? Android Destek Paketi kullandığımı not etmeliyim.

Yanıtlar:


76

Android destek kitaplığını kullanıyorsanız onResume yöntemi parçalarla nerede oynanacağı konusunda doğru yer değildir. Bunu onResumeFragments yönteminde yapmalısınız, onResume yöntem açıklamasına bakın: http://developer.android.com/reference/android/support/v4/app/FragmentActivity.html#onResume%28%29

Benim bakış açıma göre doğru kod şu olmalıdır:

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, bu doğru cevap. Sınıfta onResumeFragments()olmadığını unutmayın Activity. Temel kullanıyorsanız Activity, onPostResume()bunun yerine kullanmalısınız .
Alex Lockwood

3
Bu çözümü uygulamadan önce, bunun neden bir hack olduğunu anlamak için lütfen bunu okuyun . Bu soruda başka bir çözümün yorumlarında saklı çok daha basit bir çözüm var.
dal

1
Super.onActivityResult çağrısı IllegalStateException'ı engellemez, dolayısıyla bir subj'nin düzeltilmesi de değildir. problem
demaksee

1
Bu soru, bu sorun için google'da ilk isabet oldu, ancak kabul edilen cevap bence en iyisi değil. Bunun yerine bu cevap kabul edilmelidir: stackoverflow.com/a/30429551/1226020
JHH

27

DÜZENLEME: Bir hata değil, daha çok fragment çerçevesindeki bir eksiklik. Bu soruya daha iyi cevap, yukarıda @Arcao tarafından verilen cevaptır.

---- Orijinal gönderi ---

Aslında bu , destek paketiyle ilgili bilinen bir hatadır (düzenleme: aslında bir hata değildir. @ Alex-lockwood'un yorumuna bakın). Hata raporunun yorumlarında yayınlanan bir çalışma, DialogFragment'ın kaynağını şu şekilde değiştirmektir:

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

Bunun dev bir hack olduğunu unutmayın. Aslında yaptığım yol, orijinal parçadan kayıt yapabileceğim kendi diyalog parçamı yapmaktı. Diğer diyalog parçası bir şeyler yaptığında (göz ardı edilmek gibi), herhangi bir dinleyiciye onun uzaklaştığını söyledi. Ben böyle yaptım:

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

Artık işler olduğunda PlayerListFragment'ı bilgilendirmek için bir yolum var. UnregisterPasswordEnteredListener'ı uygun bir şekilde çağırmanızın çok önemli olduğuna dikkat edin (yukarıdaki durumda PlayerListFragment "kaybolduğunda"), aksi takdirde bu iletişim kutusu parçası, o dinleyici artık mevcut olmadığında kayıtlı dinleyicide işlevleri çağırmayı deneyebilir.


3
kaynağın kopyalanmasını gerektirmeyen bir çözüm ... sadece geçersiz kılın show()ve IllegalStateException.
Jeffrey Blattman

1
DialogFragment'ın kaynağı nasıl değiştirilir? Ya da yazınızın sonunda bahsedilen çözümünüzü gönderebilir misiniz?
Piotr Ślesarew

1
@PeterSlesarew (Oldukça spesifik) çözümümü yayınladım.
Kurtis Nusbaum

9
Gah, bu bir hata değil ! Android çerçevesi istisnayı bilerek fırlatıyor çünkü içeride parça işlemlerini gerçekleştirmek güvenli değil onActivityResult()! Bunun yerine şu çözümü deneyin: stackoverflow.com/questions/16265733/…
Alex Lockwood

2
@AlexLockwood Bu soru sorulduğunda belgeler bu konuda uyarmadı. Ek olarak, çözümünüz şu anda iyi görünse de, Nisan 2012'de işe yaramadı onPostResumeve onResumeFragmentsher ikisi de destek kitaplığına nispeten yeni eklemeler.
hrnt

24

@Natix tarafından bırakılan yorum , bazı kişilerin kaldırmış olabileceği hızlı bir tek satırlıktır .

Bu sorunun en basit çözümü , kendi kodunuzu çalıştırmadan ÖNCE super.onActivityResult () işlevini çağırmaktır . Bu, destek kitaplığını kullanıp kullanmadığınıza bakılmaksızın çalışır ve Faaliyetinizdeki davranışsal tutarlılığı korur.

Var:

Bunu ne kadar çok okursam o kadar çılgınca hackler gördüm.

Hala sorunla karşılaşıyorsanız, Alex Lockwood'un yazdığı kişi kontrol edilmelidir .


Mirasınız varsa ne olur? Super.onActivityResult () 'u çağırmak sorun olabilir, eğer super'i çağırmadan önce kodunuzu çalıştırmak istiyorsanız, süper sınıfın kendi kodu onActivityResult içinde olabilir, dikkatli olun.
Ricard

Kodumun super.onActivityResult(requestCode, resultCode, data)herhangi birinin önüne ekliyorum, sorunumu çözdü. Ancak onActivityResult üzerinde miras eklerken veya varsayılanı geçersiz kılarken, onStart / onResume'u manuel olarak ele almalıyız
mochadwi

14

Bunun bir Android hatası olduğuna inanıyorum. Temel olarak Android, onActivityResult'u etkinlik / parça yaşam döngüsünde yanlış bir noktada çağırır (onStart () öncesinde).

Hata https://issuetracker.google.com/issues/36929762 adresinde bildirilmektedir.

Temelde Niyeti daha sonra onResume () 'de işlediğim bir parametre olarak saklayarak çözdüm.

[DÜZENLE] Günümüzde bu sorun için 2012'de olmayan daha iyi çözümler var. Diğer yanıtlara bakın.


8
Aslında bu gerçekten bir hata değil. Oradaki yorumlarda belirtildiği gibi, daha onActivityResult()önce çağrıldığı açıkça belirtiliyoronResume()
Kurtis Nusbaum

2
Hataya yapılan son yorumu okudunuz mu? Buradaki hata, onActivityResult () 'un onStart ()' dan önce çağrılması, onResume () 'den önce çağrılmamasıdır.
hrnt

Ah evet, bu da doğru. Bunu kaçırdım. Yine de diğer hata raporunun sorunumla biraz daha alakalı olduğuna inanıyorum.
Kurtis Nusbaum

OnActivityResult çağrıldığında iyi tanımlanmıştır. Bu nedenle, bazı durumlarda uygunsuz görünse bile bir hata olamaz.
sstn

1
@sstn, detaylandırır mısın? OnActivityResult çağrıldığında iyi tanımlanmıştır (= onResume'dan hemen önce). Android, onResume'dan hemen önce onActivityResult'u çağırmaz. Dolayısıyla bu bir hatadır.
hrnt

11

DÜZENLEME: Yine başka bir seçenek ve muhtemelen en iyisi (veya en azından destek kitaplığının beklediği şey ...)

DialogFragments'ı Android destek kitaplığıyla kullanıyorsanız, FragmentActivity alt sınıfını kullanıyor olmalısınız. Takip etmeyi dene:

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 kaynağına bir göz attım ve görünüşü kaybetmeden parçaları devam ettirmek için dahili bir parça yöneticisi çağırıyor gibi görünüyor.


Burada listelenmeyen bir çözüm buldum. Bir İşleyici oluşturuyorum ve İşleyicide iletişim kutusu parçasını başlatıyorum. Yani, kodunuzu biraz düzenleyin:

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
}

Bu bana daha temiz ve daha az karmaşık görünüyor.


5
Bu sorunu çözmek için bir İşleyici kullanmak yalnızca bir gecikme ekler, bu nedenle sorunun ortaya çıkma olasılığını azaltır. Ama sorunun ortadan kalkacağını garanti etmez! Bu biraz kullanarak yarış koşullarını çözmeye benziyor Thread#sleep().
Alex Lockwood

27
Arama super.onActivityResult(), var olan en basit çalışan çözümdür ve muhtemelen kabul edilen cevap olmalıdır! Kayıp süper aramayı kazara fark ettim ve eklemenin işe yaramasına hoş bir şekilde şaşırdım. Bu, bu sayfada bahsedilen eski hacklerden birini kaldırmama izin verdi (diyaloğu geçici bir değişkene kaydetme ve içinde gösterme onResume()).
Natix

Güzel çözüm. Tek sorun, onActivityResult()sonucu parçaların işleyip işlemediğini gösteren herhangi bir değer döndürmemesidir.
Michael

Super.onActivityResult () 'u çağırmak projemdeki IllegalStateException çökmesini
gidermiyor

9

İki DialogFragment show () yöntemi vardır - show(FragmentManager manager, String tag)ve show(FragmentTransaction transaction, String tag).

Yöntemin FragmentManager sürümünü kullanmak istiyorsanız (orijinal soruda olduğu gibi), bu yöntemi geçersiz kılmak ve commitAllowingStateLoss kullanmak kolay bir çözümdür:

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)Bu yolu geçersiz kılmak o kadar kolay değildir çünkü orijinal DialogFragment kodundaki bazı dahili değişkenleri de değiştirmelidir, bu yüzden bunu tavsiye etmem - bu yöntemi kullanmak istiyorsanız, kabul edilen cevaptaki önerileri deneyin (veya Jeffrey Blattman).

CommitAllowingStateLoss kullanımında bazı riskler vardır - dokümantasyon "commit () gibi, ancak bir aktivitenin durumu kaydedildikten sonra commit'in yürütülmesine izin verir. Bu tehlikelidir çünkü aktivitenin daha sonra durumundan geri yüklenmesi gerekirse commit kaybedilebilir. , bu nedenle bu, yalnızca kullanıcı arabirimi durumunun kullanıcı üzerinde beklenmedik şekilde değişmesinin uygun olduğu durumlarda kullanılmalıdır. "


4

Yöntemi onSaveInstanceState () olarak adlandırılan ekli etkinlikten sonra iletişim kutusunu gösteremezsiniz. Açıkçası, onSaveInstanceState () onActivityResult () 'dan önce çağrılır. Bu nedenle, iletişim kutunuzu bu geri çağırma yönteminde göstermelisiniz OnResumeFragment (), DialogFragment'ın show () yöntemini geçersiz kılmanıza gerek yoktur. Umarım bu sana yardımcı olur.


3

Kısmen hmt'nin çözümüne dayanan üçüncü bir çözüm buldum. Temel olarak, onResume () üzerinde gösterilmek üzere bir DialogFragments ArrayList'i oluşturun;

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 () öncesinde çalıştırılır. Kullanıcı arayüzünüzü onResume () veya daha sonra yapmanız gerekir.

Bir boole veya bu iki yöntem arasında bir sonucun geri geldiğini bildirmek için ihtiyacınız olan her şeyi kullanın.

... Bu kadar. Basit.


2

Bunun epey bir süre önce yanıtlandığını biliyorum .. ama bunu yapmanın burada gördüğüm diğer cevaplardan çok daha kolay bir yolu var ... Benim özel durumumda ,ActivityResult () üzerindeki bir parçadan bir DialogFragment göstermem gerekiyordu. yöntem.

Bunu halletmek için benim kodum bu ve çok güzel çalışıyor:

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

Diğer yayınların bazılarında da belirtildiği gibi, durum kaybı ile taahhütte bulunmak, dikkatli değilseniz sorunlara neden olabilir ... benim durumumda, kullanıcıya iletişim kutusunu kapatmak için bir düğme ile bir hata mesajı gösteriyordum, bu nedenle bunun kaybolması çok önemli değil.

Bu yardımcı olur umarım...


2

Bu eski bir soru ama en basit şekilde çözdüm, sanırım:

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

2

Bunun nedeni #onActivityResult () çağrıldığında, üst etkinlik zaten #onSaveInstanceState () öğesini çağırmıştır.

#OnActivityResult () üzerinde eylemi (iletişim kutusunu göster) "kaydetmek" için, daha sonra etkinlik hazır olduğunda kullanmak üzere Runnable'ı kullanırdım.

Bu yaklaşımla, istediğimiz eylemin her zaman işe yaramasını sağlıyoruz

@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

Bulduğum en temiz çözüm şuydu:

@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

Diğer yol:

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

0

super.onActivityResult(requestCode, resultCode, data);parçayı işlemeden önce arayın


-3

Hepinizin bildiği gibi, bu problem onActivityResult () 'un onstart ()' dan önce çağırmasından kaynaklanıyor, bu yüzden bu kodda yaptığım gibi onActivityResult () 'da başlangıçta onstart () çağırın.

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

Sen gerektiğini asla doğrudan Android yaşam döngüsü yöntemleri çağırmak. Bunlar yalnızca sistem tarafından çağrılmalıdır.
Chantell Osejo
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.