The DialogFragment's combination with BackStack confuses me and maybe someone could help me out.
From what I found out, there are 2 common ways of displaying the DialogFragment. Either through show() method or by normal adding the fragment through transaction (from the checkup that's essentialy what show() does internaly).
The problem I have is with understanding of where addToBackstack() method comes into this whole process, especialy when you add transaction to backstack prior to calling on show() method, like in this sample:
// DialogFragment.show() will take care of adding the fragment
// in a transaction. We also want to remove any currently showing
// dialog, so make our own transaction and take care of that here.
FragmentTransaction ft = getFragmentManager().beginTransaction();
Fragment prev = getFragmentManager().findFragmentByTag("dialog");
if (prev != null) {
ft.remove(prev);
}
ft.addToBackStack(null);
// Create and show the dialog.
DialogFragment newFragment = MyDialogFragment.newInstance(mStackLevel);
newFragment.show(ft, "dialog");
In the sample above, before displaying the new DialogFragment, we check for other DialogFragment that could be displayed, we remove it and add this procedure to the backstack (I assume that this is for the purpose of displaying the previous DialogFragment, when the new DialogFragment is removed). Afterwards we display the new DialogFragment through show() method. But I fail to see any difference between this approach, and just calling show() method. I just checked on a Test project with displaying multiple DialogFragments one on top of eachother in a succesion, and the internal implementation of DialogFragment handles everything automaticaly, meaning that when I touch back button, the previous DialogFragment is displayed nevertheless.
So, why the addToBackstack is being used in context of DialogFragments?
Alright, after writing more code using the DialogFragment solution including more tests I came to the reason (most likely the key reason) of why to use the addToBackStack way, right after removing the previous dialog.
The reason for it (silly me that I missed that) is that it will make sure only one dialog is visible at one point in time. The show() method does exactly what it says, it "shows" a new dialog fragment, but does absolutely nothing with any previous visible dialog, so in essence all the dialogs using show() method will be stacked on top of the previous dialog. My error was that I didn't realize that until I made dialogs different in size. If all dialogs are of the same size, then the most top one will be hiding all the other dialogs.
So to summarize, show() method does not hide/remove any dialog that is already present on the screen. If we want to do that, we need to do the transaction manually, which of course must include the removing of the previous dialog as well as adding this transaction to the back stack so that when user presses the back button, the previous dialog will reemerge.
Related
I am using DialogFragment. I have three dialogs that appear one after another.
Countries List --> States List --> Regions List
I have tow button on each of these dialogs, Cancel and OK. I put current dialog onto stack before I show next dialog. Now a user may press Cancel button on say, States List dialog. I don't want him to be taken back Countries List dialog. I just was to exit the dialog. Similarly, I don't want stacked dialogs popup when a user presses OK button. I want to end the dialog gracefully and update the database.
I searched on SO and google docs. I found this should be achieved with this:
FragmentManager ft = getFragmentManager();
ft.popBackStack("mydialog", FragmentManager.POP_BACK_STACK_INCLUSIVE);
But it doesn't work for me. I tried to place it inside builder.setNegativeButton and also inside builder.setPositiveButton. Nothing seems to work.
Here is how I put them on the stack:
RegionsDialogFragment dialogFragment = new RegionsDialogFragment ();
FragmentTransaction ft = getFragmentManager().beginTransaction();
Fragment prev = getFragmentManager().findFragmentByTag("mydialog");
if (prev != null) {
ft.remove(prev);
}
ft.addToBackStack(null);
dialogFragment.show(ft, "mydialog");
Thank you
EDIT
It seems I wasn't clear. I am able to put dialogs unto the stack. That is not a problem. Problem is how can I empty(not popup) back stack when I no longer need to show stacked dialogs? I don't want stacked dialogs to popup on their own again. I want them to die on the stack and don't come back. I want to empty the stack.
Hope it helps.
You don't need add to back stack the dialogs, if you want close a Dialog, you should use: dimiss() method.
Note:
You are setting null in addToBackStack() method
Remove this line from your code
ft.addToBackStack(null);
I want to pop up a timepicker and a datepicker, more than once in an Activity.
It's not clear to me whether I should make one instance of each, and show them as needed, or if I should make new instances each time I need to pop a dialog up.
And I'm very confused by findFragmentByTag/findFragmentById. It seems that the fragment is only found if it's currently displayed?
But if it's not found and I make a new Fragment and show it with the same tag, I get an IllegalStateException. How can I recover a fragment in onCreate?
Currently, I'm doing this:
FragmentManager fm = getSupportFragmentManager();
if (savedInstanceState == null) {
timePicker = new RadialTimePickerDialog();
datePicker = new CalendarDatePickerDialog();
fm.beginTransaction().add(timePicker, FRAGMENT_TIME_PICKER)
.add(datePicker, FRAGMENT_DATE_PICKER).commit();
} else {
timePicker = (RadialTimePickerDialog)
fm.findFragmentByTag(MedicationCollapsePanel.FRAGMENT_TIME_PICKER);
datePicker = (CalendarDatePickerDialog)
fm.findFragmentByTag(MedicationCollapsePanel.FRAGMENT_DATE_PICKER);
}
As I mentioned, this fails to actually find the fragments, and almost as bad, it displays them immediately when the Activity starts, which I don't want.
Thanks.
I assume these classes are the ones by the same name from gitHub that pop up at the top of a google search.
They extend dialogFragments.
DialogFragments take care of removing themselves with the fragment manager when they are dismissed. You can see the source code as well as get this decription, from the link below:
Dismiss():
Dismiss the fragment and its dialog. If the fragment was added to the
back stack, all back stack state up to and including this entry will
be popped. Otherwise, a new transaction will be committed to remove
the fragment.
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.0.1_r1/android/app/DialogFragment.java#DialogFragment.onDismiss%28android.content.DialogInterface%29
Hence, they are not reusable after being dismissed since they will unattach themselves automatically by calling the underlying context and getting the fragment manager themselves. Rather create a new instance, and show them, or you can try overriding their default behavior. You can try either changing what they do after being dismissed, or prevent them from being dismissed in the first place (e.g. hiding them).
I still have a problem due to a DialogFragment used on my main activity.
I am currently using this code to remove it:
FragmentTransaction transaction = getFragmentManager().beginTransaction();
Fragment frag = getFragmentManager().findFragmentByTag("LockDialog");
if(frag != null) {
transaction.remove(frag);
transaction.commit();
}
The problem is that I am still getting crashes due to the fact that the dialog has duplicates (meaning the dialog was not properly removed sometimes).
So my question is: is it a right way to remove a DialogFragment or must it be only used only for Fragments?
Do I have to use the dismiss() method all the time?:
Fragment lockFragment = getFragmentManager().findFragmentByTag("LockDialog");
//If the dialog already exist, we dismiss it
if(lockFragment != null && lockFragment instanceof LockDialogFragment) {
LockDialogFragment lockDialog = (LockDialogFragment) lockFragment;
lockDialog.dismiss();
}
This is currently my biggest bug on one of my apps so I want to be sure before choosing one or the other.
Thanks!
EDIT: I just realized my current problem is maybe due to the fact that commits can be delayed, I will add executePendingTransactions to see if it gets any better.
But still it brings another question, is it necessary to call transaction.remove() if the dialog has been dismissed? Is using dismiss() more straightforward and safe than using the transactions?
DialogFragment.dismiss() is the correct way. From the documentation:
Dismiss the fragment and its dialog. If the fragment was added to the
back stack, all back stack state up to and including this entry will
be popped. Otherwise, a new transaction will be committed to remove
the fragment.
For showing Dialog Fragment dialogFragment.show(transition,FocusDialogFragment.TAG);
For dismissing dialog fragment by dialogFragment.dismiss();
Basically Dialogs inactivates the activity at the background. So the DialogFragment does the same with increased complexity. So why should one go for DialogFragment though various subclasses of Dialog are available.
Fragments are used with in your activity, but to present a fragment as dialog (window) using FragmentTransaction and followup with fragment's life-cycle, you need to use DialogFragment. However, you may do use simple Dialog too, but then it has nothing to do with the fragment's life-cycle.
As per google docs:
A DialogFragment can still optionally be used as a normal fragment, if
desired. This is useful if you have a fragment that in some cases
should be shown as a dialog and others embedded in a larger UI.
FragmentDialog is a fragment that can be:
used as fragment, e.g.:
FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
trans.add(R.id.navigation_fragment, mFriendFragment);
trans.commit();
used as dialog, e.g.:
FragmentManager fm = getFragmentManager();
UnsubscribeTabletFragment fragment = new UnsubscribeTabletFragment();
fragment.show(fm, "dialog");
So, if you have a fragment, and the fragment sometimes works as fragment, sometimes works as dialog, then you should use this one.
DialogFragment permits to reuse so part of dialog on your app. Just like fragments do it for your layouts.
Here you have a good article about DialogFragment:
http://android-developers.blogspot.fr/2012/05/using-dialogfragments.html
when u have a dynamic layout in your android app using fragment already, then u need to use it with/in your dialog from an action button click or other click, so this time dialogFragment is more convenient then the normal dialog.
Is it possible to show a FragmentDialog when a tab is selected in the ActionBar?
The onTabSelected() method happens inside a transaction that is commited after the execution of the method finishes. However, the show() method in DialogFragment does also happen in a transaction automatically, so if you try to show the dialog when the tab is selected you get a RuntimeException because the transaction gets commited twice.
Any hints will be appreciated.
Just ignore the supplied FragmentTransaction.
For example, you need to do this if you are using the Android Compatibility Library (ACL), because the FragmentTransaction you get from the action bar is an Android 3.0 FragmentTransaction, not an ACL FragmentTransaction. So, you ignore the passed-in one and use your own.
Similarly, I would expect that you can ignore the FragmentTransaction and display a DialogFragment yourself without problems.
Whether the UX will make any sense is another problem. When the user accepts the dialog the tab shows... nothing?