Mam następujący kod w moim onActivityResult dla mojego fragmentu:
onActivityResult(int requestCode, int resultCode, Intent data){
//other code
ProgressFragment progFragment = new ProgressFragment();
progFragment.show(getActivity().getSupportFragmentManager(), PROG_DIALOG_TAG);
// other code
}
Jednak pojawia się następujący błąd:
Caused by: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
Czy ktoś wie, co się dzieje lub jak mogę to naprawić? Powinienem zauważyć, że używam pakietu wsparcia dla Androida.
android
android-fragments
android-dialogfragment
Kurtis Nusbaum
źródło
źródło
onResumeFragments()
nie istnieje wActivity
klasie. Jeśli używasz wersji podstawowejActivity
, powinieneś użyćonPostResume()
zamiast niej.EDYCJA: Nie jest to błąd, ale raczej wada we frameworku fragmentów. Lepszą odpowiedzią na to pytanie jest ta, której udzielił powyżej @Arcao.
---- Oryginalny post ----
Właściwie jest to znany błąd w pakiecie wsparcia (edycja: właściwie nie jest to błąd. Zobacz komentarz @ alex-lockwood). Opublikowane obejście w komentarzach do zgłoszenia błędu polega na zmodyfikowaniu źródła DialogFragment w następujący sposób:
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; }
Zauważ, że to gigantyczny hack. Sposób, w jaki to zrobiłem, polegał na stworzeniu własnego fragmentu okna dialogowego, w którym mogłem się zarejestrować z oryginalnego fragmentu. Kiedy ten inny fragment dialogu zrobił coś (na przykład został odrzucony), powiedział wszystkim słuchaczom, że odchodzi. Zrobiłem to tak:
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; } }
Teraz mam sposób powiadamiania PlayerListFragment, gdy coś się wydarzy. Zauważ, że bardzo ważne jest, abyś odpowiednio wywołał unregisterPasswordEnteredListener (w powyższym przypadku, gdy PlayerListFragment "znika"), w przeciwnym razie ten fragment okna dialogowego może próbować wywołać funkcje zarejestrowanego nasłuchiwania, gdy ten odbiornik już nie istnieje.
źródło
show()
i przechwyć plikIllegalStateException
.onActivityResult()
! Zamiast tego wypróbuj to rozwiązanie: stackoverflow.com/questions/16265733/ ...onPostResume
IonResumeFragments
oba są stosunkowo nowymi dodatkami do biblioteki pomocy technicznej.Komentarz pozostawiony przez @Natix to szybka jedna linijka, którą niektórzy ludzie mogli usunąć.
Najprostszym rozwiązaniem tego problemu jest wywołanie super.onActivityResult () PRZED uruchomieniem własnego kodu. Działa to niezależnie od tego, czy korzystasz z biblioteki pomocy technicznej, czy nie, i utrzymuje spójność behawioralną w Twoim działaniu.
Jest:
Im więcej o tym czytam, tym bardziej szalone triki widzę.
Jeśli nadal napotykasz problemy, to ten autorstwa Alexa Lockwooda jest tym, który należy sprawdzić.
źródło
super.onActivityResult(requestCode, resultCode, data)
przed jakimkolwiek kodem, to rozwiązało mój problem. Ale dodając dziedziczenie lub nadpisując domyślną wartość onActivityResult, powinniśmy ręcznie obsłużyć onStart / onResumeUważam, że to błąd Androida. Zasadniczo system Android wywołuje metodę onActivityResult w niewłaściwym punkcie cyklu życia działania / fragmentu (przed onStart ()).
Błąd został zgłoszony pod adresem https://issuetracker.google.com/issues/36929762
Rozwiązałem to, zasadniczo przechowując intencję jako parametr, który później przetworzyłem w onResume ().
[EDYTUJ] Obecnie są lepsze rozwiązania tego problemu, które nie były dostępne w 2012 roku. Zobacz pozostałe odpowiedzi.
źródło
onActivityResult()
nazywa się to wcześniejonResume()
EDYCJA: Jeszcze inna opcja i prawdopodobnie najlepsza z dotychczasowych (lub przynajmniej tego, czego oczekuje biblioteka wsparcia ...)
Jeśli używasz DialogFragments z biblioteką obsługi systemu Android, powinieneś używać podklasy FragmentActivity. Spróbuj wykonać następujące czynności:
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 }
Przyjrzałem się źródłu FragmentActivity i wygląda na to, że wywołuje wewnętrznego menedżera fragmentów w celu wznowienia fragmentów bez utraty stanu.
Znalazłem rozwiązanie, którego nie ma na liście. Tworzę Handler i uruchamiam fragment dialogu w Handlerze. Więc trochę edytuj swój kod:
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 }
Wydaje mi się to czystsze i mniej hakerskie.
źródło
Thread#sleep()
.super.onActivityResult()
jest najprostszym działającym rozwiązaniem, jakie istnieje i prawdopodobnie powinna być akceptowaną odpowiedzią! Przypadkowo zauważyłem brakujące połączenie super i byłem mile zaskoczony, że dodanie go po prostu zadziałało. Pozwoliło mi to usunąć jeden ze starych hacków wspomnianych na tej stronie (zapisanie okna dialogowego w zmiennej tymczasowej i wyświetlenie go wonResume()
).onActivityResult()
nie zwraca żadnej wartości wskazującej, czy fragmenty obsłużyły wynik.Istnieją dwie metody show () DialogFragment -
show(FragmentManager manager, String tag)
ishow(FragmentTransaction transaction, String tag)
.Jeśli chcesz użyć wersji metody FragmentManager (jak w pierwotnym pytaniu), prostym rozwiązaniem jest zastąpienie tej metody i użycie commitAllowingStateLoss:
public class MyDialogFragment extends DialogFragment { @Override public void show(FragmentManager manager, String tag) { FragmentTransaction ft = manager.beginTransaction(); ft.add(this, tag); ft.commitAllowingStateLoss(); } }
Zastępowanie w
show(FragmentTransaction, String)
ten sposób nie jest tak łatwe, ponieważ powinno również zmodyfikować niektóre zmienne wewnętrzne w oryginalnym kodzie DialogFragment, więc nie polecałbym tego - jeśli chcesz użyć tej metody, wypróbuj sugestie w zaakceptowanej odpowiedzi (lub komentarz z Jeffrey Blattman).Korzystanie z commitAllowingStateLoss wiąże się z pewnym ryzykiem - w dokumentacji jest napisane „Like commit ()”, ale zezwala na wykonanie zatwierdzenia po zapisaniu stanu aktywności. Jest to niebezpieczne, ponieważ zatwierdzenie może zostać utracone, jeśli działanie będzie później przywrócone ze stanu , więc powinno to być używane tylko w przypadkach, gdy stan interfejsu użytkownika może się nieoczekiwanie zmienić na użytkowniku. "
źródło
Nie można wyświetlić okna dialogowego po dołączonym działaniu wywołanym jego metodą onSaveInstanceState (). Oczywiście onSaveInstanceState () jest wywoływana przed onActivityResult (). Więc powinieneś pokazać swoje okno dialogowe w tej metodzie wywołania zwrotnego OnResumeFragment (), nie musisz nadpisywać metody show () DialogFragment. Mam nadzieję, że to ci pomoże.
źródło
Wymyśliłem trzecie rozwiązanie, częściowo oparte na rozwiązaniu hmt. Zasadniczo utwórz ArrayList of DialogFragments, które będą wyświetlane w 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"); }
źródło
onActivityResult () wykonuje się przed onResume (). Musisz wykonać swój interfejs użytkownika w onResume () lub później.
Użyj wartości logicznej lub czegokolwiek innego, czego potrzebujesz, aby zakomunikować, że wynik powrócił między obiema tymi metodami.
... Otóż to. Prosty.
źródło
Wiem, że odpowiedź na to pytanie udzielono już jakiś czas temu .. ale jest na to znacznie łatwiejszy sposób niż niektóre inne odpowiedzi, które tu widziałem ... W moim konkretnym przypadku musiałem pokazać fragment DialogFragment z fragmentów onActivityResult () metoda.
To jest mój kod do obsługi tego i działa pięknie:
DialogFragment myFrag; //Don't forget to instantiate this FragmentTransaction trans = getActivity().getSupportFragmentManager().beginTransaction(); trans.add(myFrag, "MyDialogFragmentTag"); trans.commitAllowingStateLoss();
Jak wspomniano w niektórych innych postach, popełnienie błędu z utratą stanu może powodować problemy, jeśli nie jesteś ostrożny ... w moim przypadku po prostu wyświetlałem komunikat o błędzie użytkownikowi z przyciskiem do zamknięcia okna dialogowego, więc jeśli stan tego jest stracony, to nie jest wielka sprawa.
Mam nadzieję że to pomoże...
źródło
To stare pytanie, które rozwiązałem w najprostszy sposób, myślę:
getActivity().runOnUiThread(new Runnable() { @Override public void run() { MsgUtils.toast(getString(R.string.msg_img_saved), getActivity().getApplicationContext()); } });
źródło
Dzieje się tak, ponieważ po wywołaniu #onActivityResult () aktywność nadrzędna już wywołała #onSaveInstanceState ()
Użyłbym Runnable, aby "zapisać" akcję (pokaż okno dialogowe) na #onActivityResult (), aby użyć go później, gdy aktywność była gotowa.
Dzięki takiemu podejściu upewniamy się, że akcja, którą chcemy wykonać, zawsze zadziała
@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; } }
źródło
Najczystsze rozwiązanie, które znalazłem, to:
@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. }
źródło
Otrzymałem ten błąd podczas wykonywania
.show(getSupportFragmentManager(), "MyDialog");
czynności.Spróbuj
.show(getSupportFragmentManager().beginTransaction(), "MyDialog");
najpierw.Jeśli nadal nie działa, ten post ( Pokaż DialogFragment z onActivityResult ) pomaga mi rozwiązać problem.
źródło
Inny sposób:
@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 }
źródło
po prostu zadzwoń
super.onActivityResult(requestCode, resultCode, data);
przed obsługą fragmentuźródło
Jak wszyscy wiecie, ten problem jest spowodowany wywołaniem onActivityResult () przed onstart (), więc po prostu wywołaj onstart () na początku w onActivityResult (), tak jak to zrobiłem w tym kodzie
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { onStart(); //write you code here }
źródło