So i'm having one dialog that i'm trying to show from an Activity
this activity has a navigation graph (JETPACK)
for now i can show this popup only once if i'm in my Activity
then if i close the activity and come back again to it, it's won't show the Dialog because the state of the activity is already saved. and i'm using commitAllowingStateLoss to commit this dialog fragment if i use commit it's crashing. i searched everywhere in stackoverflow couldn't find a single solution for this problem.
code of how im showing the fragment:
public void show(Context context) {
FragmentManager fragmentManager = ((AppCompatActivity) context).getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
Fragment prev = fragmentManager.findFragmentByTag(TAG_DIALOG);
if (prev != null) {
fragmentTransaction.remove(prev);
}
fragmentTransaction.add(this, TAG_DIALOG);
fragmentTransaction.commitAllowingStateLoss();
}
Please show how you open the fragment.
Do you save anything when the activity closes? I can only imagine problems with saving the actual fragment instance, but it's all guess work without seeing any code.
Hi I want to keep a Fragment alive even if it is not shown anymore. Because I have some AsyncTasks going there.
Firstly I am adding a starting point Fragment
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.add(R.id.fragmentMenu, menuFragment);
fragmentTransaction.commit();
Later on I replace menuFragment with the Fragment which should stay alive
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.fragmentMenu, btFragment).addToBackStack(null).commit();
Lastly I override the onBackPressed() method for using popBackStack()
#Override
public void onBackPressed() {
if(getFragmentManager().getBackStackEntryCount() != 0){
getFragmentManager().popBackStackImmediate();
}else{
super.onBackPressed();
}
}
Inside my btFragment which should stay alive even if I pop another Fragment I have set setRetainInstance(true) inside the onCreate() method.
But it is getting destroyed as soon as I pop the backstack.
Am I doing something wrong? thx
The documentation for setRetainInstance() explains that it applies only to activity recreation:
Control whether a fragment instance is retained across Activity
re-creation (such as from a configuration change)
I solved it with a little bit of a workaround. Here is the code if anyone is interested in it.
// little work-around to not let btFragment die.
Fragment fragment = getFragmentManager().findFragmentById(R.id.fragmentMenu);
if (fragment instanceof BTFragment) {
getFragmentManager().beginTransaction().hide(fragment).add(R.id.fragmentMenu, menuFragment).commit();
} else if (getFragmentManager().getBackStackEntryCount() != 0) {
getFragmentManager().popBackStackImmediate();
} else {
super.onBackPressed();
}
Basically I'm hiding the Fragment instead of removing it and by checking the FrameLayout for the current Fragment I can handle different Fragments. With this way, the onDestroy() method is not called on my btFragment.
I have used the following codes for showing and canceling dialogfragment :
public static void showDialogFragment(FragmentManager fm,String type){
FragmentTransaction ft = fm.beginTransaction();
MyDialogFragment prev = (MyDialogFragment)fm.findFragmentByTag(type);
if (prev != null) {
prev.dismissAllowingStateLoss();
ft.remove(prev);
}
ft.addToBackStack(null);
MyDialogFragment newFragment = MyDialogFragment.newInstance();
try{
newFragment.show(ft,type);
}catch(IllegalStateException e){
return;
}
}
public static void cancelDialogFragment(FragmentManager fm,String tag){
FragmentTransaction ft = fm.beginTransaction();
MyDialogFragment prev = (MyDialogFragment )fm.findFragmentByTag(tag);
if (prev != null) {
prev.dismiss();
ft.remove(prev);
}
ft.addToBackStack(null);
ft.commit();
}
when I open the activity I show a dialogFragment and after receiving the data from internet I cancel it and show the recieved data, But if I press back button again it shows the dialogFragment and I have to press back button again to dismiss it and one more time to finish the activity. I know I can override onBackPressed but I want to know why this happens? why dose it again show the dialogfragment?
What is wrong with my code?
What you do is in showDialogFragment() you add this fragment to FragmentManager to backstack. Then in cancelDialogFragment() method you remove it from backstack with ft.remove(prev);
So now, your backstack is as it was before showing DialogFragment.
But what you do next is, that you add this DialogFragment again to backstack. It is not shown, but it is on the top of backstack. That means, if you press backButton, the top item in backstack, your DialogFragment, will be shown. On the next BackPress, your DialogFragment will be dismissed.
So dont add the fragment to backstack in your cancelDialogFragment() method.
Remove this line:
ft.addToBackStack(null);
Replace your entire cancelDialogFragment with this:
public static void cancelDialogFragment(FragmentManager fm,String tag){
fm.popBackStack();
}
Finally I have found the reason and the correct answer. the problem is with:
ft.addToBackStack(null);
From document:
Add this transaction to the back stack. This means that the
transaction will be remembered after it is committed, and will reverse
its operation when later popped off the stack.
Parameters name An optional name for this back stack state, or null.
that menas:
hey android I have removed dialogfragment from backstack (so there is nothing on the top of the backStack and the answer of #Vojtaaa9 is wrong because as I added the comment when you run MyDialogFragment prev = (MyDialogFragment )fm.findFragmentByTag(tag); after calling cancel you will get null, this means the backStack dose not have any dialogfragment) but remmber my action, remember that there was a dialogfragment but now it has removed. When user presses the back button the transaction reverses, it means that now there is nothing on the top of the backStack but then android pushes a dialogFragment to the backStack to do the transaction in a reverse order.
The docs say this for the dismiss() method from the Dialog class:
Dismiss this dialog, removing it from the screen. This method can be invoked
safely from any thread. Note that you should not override this method to do
cleanup when the dialog is dismissed, instead implement that in onStop().
In my code, all I do is call getDialog().dismiss() to dismiss it. But I am not doing anything else or even using onStop(). So I am asking exactly how to correctly dismiss a DialogFragment to avoid any memory leaks, etc..
tl;dr: The correct way to close a DialogFragment is to use dismiss() directly on the DialogFragment.
Details: The documentation of DialogFragment states
Control of the dialog (deciding when to show, hide, dismiss it) should be done through the API here, not with direct calls on the dialog.
Thus, you should not use getDialog().dismiss(), since that would invoke dismiss() on the dialog. Instead, you should use the dismiss() method of the DialogFragment itself:
public void 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.
As you can see, this takes care not only of closing the dialog but also of handling the fragment transactions involved in the process.
You only need to use onStop if you explicitly created any resources that require manual cleanup (closing files, closing cursors, etc.). Even then, I would override onStop of the DialogFragment rather than onStop of the underlying Dialog.
I think a better way to close a DialogFragment is this:
Fragment prev = getSupportFragmentManager().findFragmentByTag("fragment_dialog");
if (prev != null) {
DialogFragment df = (DialogFragment) prev;
df.dismiss();
}
This way you dont have to hold a reference to the DialogFragment and can close it from everywhere.
Why don't you try using only this code:
dismiss();
If you want to dismiss the Dialog Fragment by its own. You can simply put this code inside the dialog fragment where you want to dismiss the Dialog.
For example:
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
dismiss();
}
});
This will close the recent Dialog Fragment that is shown on the screen.
Hope it helps for you.
I gave an upvote to Terel's answer. I just wanted to post this for any Kotlin users:
supportFragmentManager.findFragmentByTag(TAG_DIALOG)?.let {
(it as DialogFragment).dismiss()
}
Kotlin Version of Terel answer
(fragmentManager.findFragmentByTag(TAG) as? DialogFragment)?.dismiss()
You should dismiss you Dialog in onPause() so override it.
Also before dismissing you can check for null and is showing like below snippet:
#Override
protected void onPause() {
super.onPause();
if (dialog != null && dialog.isShowing()) {
dialog.dismiss();
}
}
There are references to the official docs (DialogFragment Reference) in other answers, but no mention of the example given there:
void showDialog() {
mStackLevel++;
// 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");
}
This removes any currently shown dialog, creates a new DialogFragment
with an argument, and shows it as a new state on the back stack. When
the transaction is popped, the current DialogFragment and its Dialog
will be destroyed, and the previous one (if any) re-shown. Note that
in this case DialogFragment will take care of popping the transaction
of the Dialog is dismissed separately from it.
For my needs I changed it to:
FragmentManager manager = getSupportFragmentManager();
Fragment prev = manager.findFragmentByTag(TAG);
if (prev != null) {
manager.beginTransaction().remove(prev).commit();
}
MyDialogFragment fragment = new MyDialogFragment();
fragment.show(manager, TAG);
CustomFragment dialog = (CustomDataFragment) getSupportFragmentManager().findFragmentByTag("Fragment_TAG");
if (dialog != null) {
dialog.dismiss();
}
Adding to the other answers, when having a DialogFragment that is full screen calling dismiss() won't pop the DialogFragment from the fragment backstack. A workaround is to call onBackPressed() on the parent activity.
Something like this:
CustomDialogFragment.kt
closeButton.onClick {
requireActivity().onBackPressed()
}
I found that when my fragment was defined in the navigation graph with a <fragment> tag (for a full screen dialogfragment), the dialogfragment would not dismiss with the dismiss() command. Instead, I had to pop the back stack:
findNavController(getActivity(), R.id.nav_host_fragment).popBackStack();
However, if the same dialogfragment was defined in the navigation graph with a <dialog> tag, dismiss() works fine.
Just call dismiss() from the fragment you want to dismiss.
imageView3.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
dismiss();
}
});
Consider the below sample code snippet which demonstrates how to dismiss a dialog fragment safely:
DialogFragment dialogFragment = new DialogFragment();
/**
* do something
*/
// Now you want to dismiss the dialog fragment
if (dialogFragment.getDialog() != null && dialogFragment.getDialog().isShowing())
{
// Dismiss the dialog
dialogFragment.dismiss();
}
Happy Coding!
Here is a simple AppCompatActivity extension function, which closes opened Dialog Fragment:
fun AppCompatActivity.whenDialogOpenDismiss(
tag: String
) {
supportFragmentManager.findFragmentByTag(tag)?.let {
if(it is DialogFragment) it.dismiss() }
}
Of course you can call it from any activity directly.
If you need to call it from a Fragment just make the same extension function about Fragment class
I recently added Fragments to my applications. For a new application i'll need to get
notified as soon as my fragment is shown. So i can do some calculations as soon as my
fragment is shown again.
My Fragment is used with a TabIndicator and it's only one FragmentClass which is used
a few times.
Here's the normal standard override class:
#Override
public void onHiddenChanged(boolean hidden) {
super.onHiddenChanged(hidden);
}
I had same problem.
I used standart guideline practic work with fragment (Building a Flexible UI).
I have two fragment (ListItemsFragment and InfoItemFragment).
When used normal screen size, I replace ListItemsFragment at InfoItemFragment and
the method onHiddenChanged doesn't call automatic.
FragmentTransaction mFragmentTransaction = getFragmentManager().beginTransaction();
mFragmentTransaction.replace(R.id.container_fragment, new InfoItemFragment(), "tag_fr_infoItem");
mFragmentTransaction.addToBackStack(null);
mFragmentTransaction.commit();
I think we must called in hide method FragmentTransaction. For example:
ListItemsFragment mListItemsFragment;
FragmentTransaction mFragmentTransaction = getFragmentManager().beginTransaction();
mFragmentTransaction.replace(R.id.container_fragment, new InfoItemFragment(), "tag_fr_infoItem");
if (mListItemsFragment != null) {
mFragmentTransaction.hide(mListItemsFragment);
}
mFragmentTransaction.addToBackStack(null);
mFragmentTransaction.commit();
And now the method onHiddenChanged work fine. When user click back button mListItemsFragment again show and method onHiddenChanged called automatic.
In documentation said:
this will be called whenever the fragment changes state from that
I think we must manual change value then method will be called.
Still looking for an answer? onHiddenChanged doesn't get called the first time an fragment is shown. Only when it changes state.
From the documentation:
Called when the hidden state (as returned by isHidden()) of the fragment has changed. Fragments start out not hidden; this will be called whenever the fragment changes state from that.
You can use setUserVisibleHint method to solve some similar problem. Hope it can help you.
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
// Do some your work
} else {
// Do your Work
}
}
if you use hide() and show() to hide or show your fragment, Any lifecycle method dont't work.so is setUserVisibleHint() .