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?
Related
Now I have a fragment to do something will last long time. In some case , the activity will call onSaveInstanceState when fragment is still running. After fragment do all the things , I want to close it. Here will throw an exception Can not perform this action after onSaveInstanceState.
I know what it means, but I really need to close the fragment after something has been done. So if I use commitAllowingStateLoss to force fragment to close , after activity recreate, the close state won't be recreated, the UI will be broken.
So how should I close the fragment correctly ?
I don't know any safe way to automatically close fragment, and it seems like a bad idea (why would you depend on the system for this?). You will have to time it correctly in your app. I can give you code suggestion to do it. Since there is no close method, you use remove method instead, or popBackStack. Since you did not post any code, I am suggesting a generic way to do this.
Code suggestion using remove:
Fragment fragment = getSupportFragmentManager().findFragmentByTag(TAG_FRAGMENT);
...
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
transaction.remove(fragment);
transaction.commit();
Notes:
TAG_FRAGMENT is the ID for the fragment in the layout.
findFragmentByTag() is one way, another is new myFragment();
If you need to time when the Fragment is closed, use onDetach() override method of Fragment.
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.
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.
I am building a tablet app. In this app there is one activity with two fragments. First fragment is a "known" list fragment which is showing a simple one item layout list from a database query, the second fragment shows the details about the selected record (from the list fragment). The think with the second fragment is that its type depends from the records being showed in the list. For example if the records are customers then the selected customer's details are shown, if they are inventory items the selected item's details are shown etc.
In order to communicate with the Details Fragment I've created an interface which every detail fragment class implements.
The list fragment is "fixed" in the activity from the layout xml. The detail fragment however is created during the activity's creation like this:
super.onCreate(savedInstanceState);
setContentView(R.layout.act_hlpfiles_host);
...
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.laydetailsfragment, FragmentsPool.getHelperFileFragment(501), "recordDetails");
fragmentTransaction.commit();
myDetailsFragment = getFragmentManager().findFragmentByTag("recordDetails");
...
myListFragment = (frg_hlpfiles_lstrecords) getFragmentManager().findFragmentById(R.id.frg_lstrecords);
....
}
The problem with this code is that myDetailsFragment is always null. This is because the fragmentTransaction.commit() does not run immediately but it happens on the main thread the next time that thread is ready (as the android documentation states).
If I create the detail fragment in onStart() and instantiate the list fragment in onCreate everything works ok.
So the question is: How can I be sure that the fragmentTransaction.commit() has commit the transaction so I can do some work with the added fragment? Furthermore is there any way to wait until the commit happens and then continue with the rest of the code?
Try running fragmentManager.executePendingTransactions() after committing your transaction but before finding by tag and see if that works for you.
In Android API 24 FragmentTransaction has synchronous .commitNow() method.
It's in the reference now: https://developer.android.com/reference/android/app/FragmentTransaction.html#commitNow()
On the contrary, .commit() works asynchronously. It just schedules a commit of the transaction.
I was facing a similar issue.
I think the key learning here is using commitNow() instead of commit() with getSupportFragmentManager. This will disallow the main thread from executing until the fragment has been destroyed. It is imperative when building interfaces and using a shared activity. I should know it had me stumped for a while!
Here is a sample code example:
getSupportFragmentManager().beginTransaction().remove(getSupportFragmentManager().findFragmentById(R.id.fragment_frame)).commitNow();
"....so I can do some work with the added fragment? Furthermore is there any way to wait until the commit happens and then continue with the rest of the code?"
It all depends on what work you want to do. From your question I see that most of your work code should be in your fragment code anyway, for example when an inventory item is selected.
On the callback when a selection list item is selected (in order to change the details fragment) you'll be able to get hold of the details fragment comfortably enough anyway.
Furthermore, you already have the fragment from the return of FragmentsPool.getHelperFileFragment(501), so I don't see why you need to get the fragment via its tag.
I'm interested to know what work you need to do in onCreate with your added details fragment.