DialogFragment and back button - android

Is there any possibility to intercept the key button in DialogFragment? sorry for the naive question.. the onBackPressed of my FragmentActivity is never called.
thanks in advance
if (imageFile.exists()) {
ShowPicDialog newFragment = ShowPicDialog.newInstance();
FragmentTransaction ft = manager.beginTransaction();
Fragment prev = manager.findFragmentByTag("picDialog");
if (prev != null) {
ft.remove(prev);
}
ft.addToBackStack("picDialog");
newFragment.getArguments().putString("path", imageFile.getAbsolutePath());
newFragment.show(ft, "picDialog");
}
sorry I added the snip of code I use to show the dialog.

It's hard to say for sure what the issue is, since you haven't posted any code. But my first guess is that you haven't added the DialogFragment to the back stack by calling the addToBackStack method of the FragmentTransaction that you're using to add your fragment to the activity.
There are examples right in the Android documentation pages that give examples of a good pattern for using a DialogFragment in your Activity.
Since you are displaying a Dialog, the created Dialog will receive the key events, not the parent Activity. So, set a Dialog.OnKeyListener when you create the Dialog's fragment, and call setCancelable(false) on the Dialog to prevent the back key from dismissing it. You can then handle the back key in your OnKeyListener's onkey method.

Best way to Handle DialogFragment with back button:
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new Dialog(getActivity(), getTheme()){
#Override
public void onBackPressed() {
// On backpress, do your stuff here.
}
};
}

Rahul Pundhir's answer works great if you aren't using the builder pattern. If you are using the Builder pattern on your dialog you can instead do this:
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog alertDialog = new AlertDialog.Builder(getContext())
.setTitle(...)
.setPositiveButton(...)
.setNegativeButton(...)
.setMessage(...)
.create();
alertDialog.setOnKeyListener((dialog, keyCode, event) -> {
if (keyCode == KeyEvent.KEYCODE_BACK
&& event.getAction() == KeyEvent.ACTION_UP) {
// TODO do the "back pressed" work here
return true;
}
return false;
});
return alertDialog;
}
This works by mimicking how the system calls onBackPressed() in the first place (ignoring the tracking and listening for ACTION_UP). See the source on Dialog

Related

DialogFragment will not dismiss when using BottomNavigationBar after switching fragments and returning

I have a custom dialog which extends AppCompatDialogFragment. I am calling it from within one Fragment of a BottomNavigationBar. It gets dismissed when a message is returned from a ListenerService. It all works well until I switch to a different fragment using one of the icons on the BottomNavigationBar and then back to the fragment with Dialog functionality. The next time I show the dialog the message does not dismiss it.
I have a member variable:
SendingMessageDialog sendingMessageDialog;
When I click a button it calls this to show the dialog:
sendingMessageDialog = new SendingMessageDialog();
sendingMessageDialog.show(getActivity().getSupportFragmentManager(), "Sending Message");
When the message is receive I call this:
sendingMessageDialog.dismiss();
sendingMessageDialog = null;
I have also tried it this way:
private void dismissDialog() {
Fragment prev = getActivity().getSupportFragmentManager().findFragmentByTag("Sending Message");
if (prev != null) {
Log.d(TAG, "dismissDialog: prev is not null");
SendingMessageDialog df = (SendingMessageDialog) prev;
df.dismiss();
}
}
My SendingMessageDialog which extends AppCompatDialogFragment, I have this code in my onCreateDialog:
#NonNull
#Override
public Dialog onCreateDialog(#Nullable Bundle savedInstanceState) {
Log.d(TAG, "onCreateDialog: starting");
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
LayoutInflater inflater = getActivity().getLayoutInflater();
View view = inflater.inflate(R.layout.send_message_dialog, null);
builder.setView(view)
.setTitle("Sending Token")
.setNegativeButton("cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
}
});
return builder.create();
}
I have looked up a few post on stackoverflow but none seem to address this specific issue. I saw this one, but there is no cancel() method available. I saw this one but I have to dismiss the dialog when I get a message from another part of the system, so I cannot just do this in-line sort of thing. This one just seemed like a simple mistake of calling show on the wrong dialog. I couldn't quite get a handle on this one, but it's 8 years old and seems to be about the coordination of two dialogs.
Many of the examples identify the dismiss is not working. In my case it is, until I switch fragments using the bottom navigation and then return to the fragment where the dialog process exists.
Nothing special happening in the navigation:
selectedFragment = new RunScenarioListFragment();
and:
getActivity().getSupportFragmentManager()
.beginTransaction()
.replace(R.id.frame_layout_run_scenario,selectedFragment)
.addToBackStack(null)
.commit();

Backing out of an full-screen dialog

if I launch a full-screen dialog like such
FragmentManager fragmentManager = getActivity().getFragmentManager();
DialogStyleCreator editor = new DialogStyleCreator();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
transaction.add(android.R.id.content, editor).commit();
How can I make it so that if I hit the android back arrow (in the top left of the menu) or the back button it closes the dialog instead of going back to the previous activity like in alert dialogs?
You may simply put your transaction in stack of fragment manager and override the function of back button
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (Integer.parseInt(android.os.Build.VERSION.SDK) > 5
&& keyCode == KeyEvent.KEYCODE_BACK
&& event.getRepeatCount() == 0) {
// Simply pop back your fragment stack here
return true;
}
return super.onKeyDown(keyCode, event);
}
#Override
public void onBackPressed() {
// Simply pop back your fragment stack here
}
Since you have added the DialogFragment as transaction.add(android.R.id.content, editor).commit(); you may not have option to listen the dismiss call back.
I suggest you to create the DialogFragment instance and start it like
fm.beginTransaction().add(sampleDialog, "Dialog").commit();
and set the call back interface for Activity Back Button Click and call dismiss();

Sending data between fragments without creating new fragment

So I have a fragment (WifiSetupFragment) that calls a DialogFragment, and that DialogFragment needs to pass a string back to the original fragment. I know to do this you have an interface in the activity that will send data to the original fragment like so, which I am already doing:
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.addToBackStack(null);
transaction.replace(R.id.content_frag, WifiSetupFragment.newInstance(password));
transaction.commit();
So the first time I call WifiSetupFragment, I haven't created a DialogFragment yet because I haven't clicked on an item to open the dialog. My question is should I just call
WifiSetupFragment.newInstance(null)
and have a null check for the password string in my fragment? Because I don't have a password unless the DialogFragment is open, and it's not always open. If this made no sense, please tell me and I'll try to explain more clearly. I guess it just seems strange to me to have a parameter for a string that might only be sent to this fragment occasionally since the data isn't constantly being passed in.
You don't need to communicate between these Fragments through the Activity. What you can do instead:
Make your WifiSetupFragment.newInstance() accept no parameters.
Make WifiSetupFragment implement a new interface, let's call it OnPasswordSuppliedListener.
Once you create your DialogFragment instance, attach it to a getChildFragmentManager() instead of getFragmentManager().
Now inside of your DialogFragment subclass you can reference WifiSetupFragment by calling getParentFragment().
Cast getParentFragment() to your interface and voila!
Note: I'm assuming you're using Fragments from the support library. Otherwise please be aware that nested Fragments feature was introduced in the API 17.
Your dialog can define an interface allowing to send input password back to parent fragment / activity:
public class TestDialog extends DialogFragment {
private TextView mPasswordView;
private OnPasswordDefinedCallback mCallback;
public static TestDialog newInstance() {
TestDialog dialog = new TestDialog();
return dialog;
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// inflate layout for your dialog (it must include edit text for password)
LayoutInflater inflater = getActivity().getLayoutInflater();
View layout = inflater.inflate(R.layout.dialog_test, null);
// getting ui elements from layout
mPasswordView = (TextView) layout.findViewById(R.id.txt_password);
// building dialog
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setView(layout);
builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
try {
mCallback = (OnPasswordDefinedCallback) getTargetFragment();
} catch (ClassCastException e) {
throw new ClassCastException("must implement OnPasswordDefinedCallback");
}
if (mCallback != null) {
// send password back to parent
mCallback.doPasswordDefined(mPasswordView.getText().toString());
}
dismiss();
}
});
return builder.create();
}
public interface OnPasswordDefinedCallback {
void doPasswordDefined(String password);
}
}
Then in WifiSetupFragment you can proceed as follows for opening PasswordDialog:
TestDialog dialog = TestDialog.newInstance();
dialog.setTargetFragment(WifiSetupFragment.this, 1);
dialog.show(getChildFragmentManager(), null);
WifiSetupFragment must of course implement interface OnPasswordDefinedCallback.

Dismiss popup window from Fragment like onBackPressed

I would like to dismiss a popup window when the user presses the back button. Since i am in the context of a Fragment i don't have the method onBackPressed() available.
Dismissing the popup shouldn't be something difficult as i just need to call the dismiss() method. Problem is i don't know how to detect the pressing of the back button
Could i use something similar for a Fragment or is there any other way i can detect the pressing of the Back button from this Fragment?
Thanks!
LATER EDIT => WHAT I TRIED TO DO
In my main activity i implemented the onBackPressed() method like this:
#Override
public void onBackPressed() {
//isThePopupShowing() is a method in the target fragment which returns true if the PopupWindow is currently showing
if (secondFragment.isThePoupShowing()) {
// dismissPopup is a method in the same fragment which closes the PopupWindow with the dismiss() method
secondFragment.dismissPopup();
Log.d("DismissPopup", "And finally here!");
} else {
super.onBackPressed();
}
}
when i create the fragment here:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.home);
SharedPreferences user_details = getSharedPreferences(
ro.gebs.captoom.utils.Constants.PREFS_NAME, 0);
LoginFragment firstFragment = new LoginFragment();
secondFragment = new HomeScreenFragment();
String userid = user_details.getString("userid", null);
manager = getSupportFragmentManager();
if (userid == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, firstFragment).commit();
} else {
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, secondFragment).commit();
}
}
This is the code in my fragment:
public boolean isThePoupShowing() {
return sync_popup != null && sync_popup.isShowing();
}
//
public void dismissPopup() {
Log.d("DismissPopup", "I got here, dismissing");
sync_popup.dismissPopup();
}
And this is the dismiss method:
public void dismissPopup(){
layout.setVisibility(View.GONE);
dismiss();
Log.d("DismissPopup", "and in SyncQuickAction");
}
The back button works normally as the application closes once i press the back button when the fragment is open but the popup is not dismissed when i press the back button... any suggestions as to what i might be doing wrong?
Thanks
Replace
popupWindow.setOutsideTouchable(false);
with this
popupWindow.setOutsideTouchable(true);
popupWindow.setFocusable(true);
The fragment is paused (lifecycle of fragment) when the popup window is showing, so sync_popup gets null value and isThePoupShowing method always gets false value when it's called from the activity.
When you make the popupWindow as a static member, the system will not "recycle" the member when the fragment is paused and you can dismiss it correctly.

How to correctly dismiss a DialogFragment?

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

Categories

Resources