Set visibility view in DialogFragment at runtime - android

I'm trying to update a DialogFragment display at runtime, setting the visibility of a view to VISIBLE or GONE depending on some conditions.
Basically, I inflate my layout in the DialogFragment, show a progress bar while I do some background request, and update the DialogFragment layout depending of the result of my request.
Sometimes it works, sometimes it doesn't update my UI accordingly, even if the logs show that the method is invoked and the thread is the Main thread.
Any idea why ?
In DialogFragment
#Override
public void showData(List<MyDataViewModel> data) {
Timber.d("Fragment Show data " + (Looper.myLooper() == Looper.getMainLooper()));
Timber.d("Fragment Show data " + this);
Timber.d("Fragment Show data " + data.size());
if (mConnectedContainer.getVisibility() != View.VISIBLE) {
mConnectedContainer.setVisibility(View.VISIBLE);
}
}
In Activity
#Override
public void showDialog() {
// 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 = getSupportFragmentManager().beginTransaction();
Fragment prev = getSupportFragmentManager().findFragmentByTag("dialog");
if (prev != null) {
ft.remove(prev);
}
ft.addToBackStack(null);
// Create and show the dialog.
DialogFragment newFragment = MyDialogFragment.newInstance();
newFragment.show(ft, "dialog");
}
Also tried this way
#Override
public void showDialog() {
// Create and show the dialog.
DialogFragment newFragment = MyDialogFragment.newInstance();
newFragment.show(getSupportFragmentManager(), "dialog");
}

After some investigation, the issue was linked to my use of RxJava.
I was executing a first UseCase, and in the onNext, executing another one.
I split my code in two different dialogs to make it simpler, and have only one use case updating the UI, and no more issue.

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();

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.

Showing only one DialogFragment

I am trying to set up a dialog fragment that pops up when my android user receives a push notification. The code I have below triggers the dialog. The issue I am running into is that if my users get multiple pushes, they will see multiple dialog boxes popup.
My desired action is to show only one dialog box and if another one pops up before the current one is closed, the current one should be destroyed and then the new one shown.
public abstract class BaseActivity extends ActionBarActivity {
public void showShiftsDialog(String time) {
String DIALOG_ALERT = "dialog_alert";
FragmentTransaction transaction = getFragmentManager().beginTransaction();
android.app.Fragment prev = getFragmentManager().findFragmentByTag(DIALOG_ALERT);
if (prev != null) transaction.remove(prev);
transaction.addToBackStack(null);
// create and show the dialog
DialogFragment newFragment = ShiftsDialogFragment.newInstance(time);
newFragment.show(getSupportFragmentManager().beginTransaction(), DIALOG_ALERT);
}
}
I have tried using the code from the android docs (http://developer.android.com/reference/android/app/DialogFragment.html). When debugging, it looks like prev is always null.
From my understanding, it looks like I am attaching the DialogFragment to the SupportFragmentManager:
newFragment.show(getSupportFragmentManager().beginTransaction(), DIALOG_ALERT);
and when I try to check to see if there are any current DialogFragment, I am checking from the FragmentManager:
android.app.Fragment prev = getFragmentManager().findFragmentByTag(DIALOG_ALERT);
If I try to change the code to try to get it from the SupportFragmentManager, I get an incompatible type error where it is expecting android.app.Fragment, but I am returning a android.support.v4.app.Fragment:
android.app.Fragment prev = getSupportFragmentManager().findFragmentByTag(DIALOG_ALERT);
How can I manage my DialogFragment so that only one is shown at any given time?
Working Solution
public void showShiftsDialog(String time) {
String DIALOG_ALERT = "dialog_alert";
android.support.v4.app.FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
android.support.v4.app.Fragment prev = getSupportFragmentManager().findFragmentByTag(DIALOG_ALERT);
if (prev != null){
DialogFragment df = (DialogFragment) prev;
df.dismiss();
transaction.remove(prev);
}
transaction.addToBackStack(null);
// create and show the dialog
DialogFragment newFragment = ShiftsDialogFragment.newInstance(time);
newFragment.show(getSupportFragmentManager().beginTransaction(), DIALOG_ALERT);
}
Your issue seems to be the incompatibility of the DialogFragment. If ShiftsDialogFragment is a sub-class of android.support.v4.app.DialogFragment you can use
android.support.v4.app.Fragment prev = getSupportFragmentManager().findFragmentByTag(DIALOG_ALERT);

Android re-showing dialogfragment from separate fragment

I have an application that is using fragments. The set up is like so:
Main Activity loads, loads fragment activity into right portion of parent activity
-From the Fragment, I launch a DialogFragment which displays a list of users
-From the DialogFragment, if you click on one of the users in the list, it hides the list of users DialogFragment .hide() and shows a new DialogFragment containing the details about the user
This all works great. However, when I click the Close button on the Details DialogFragment, I'd like to dismiss() that dialog, and re-show the List of Users dialog.
I realize this is somewhat difficult to follow.
Does anyone have any insight that may help me?
UPDATE
The code I use to display the DialogFragment is the following:
MyDialogFragment dialog = new MyDialogFragment();
dialog.show(getFragmentManager(), "MyDialogFragment");
Then once in the dialog fragment, if I wanted to hide it and show the details fragment I call
dialog.hide();
MyDetailsFragment details = new MyDetailsFragment();
details.show(getFragmentManager(), "MyDetailsFragment");
Basically I need to be able to re-show the dialog above when I dismiss the details.
When a fragment transaction is performed, you can add it to the back stack which can be reversed on dismissing the dialog.
Begin a fragment transaction and use the DialogFragment.show(FragmentTransaction transaction, String tag) variant which takes the FragmentTransaction as parameter. It will take care of showing the Dialog, adding the fragment to the passed transaction and then committing the transaction. Later when the Dialog is dismissed, DialogFragment will itself take care of popping the transaction.
You can follow the first sample posted in the DialogFragment docs.
Here is the working code:
public void launchMyDialog(View v) {
// 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("mydialog");
if (prev != null) {
ft.remove(prev);
}
ft.addToBackStack(null);
MyDialogFragment dialog = new MyDialogFragment();
dialog.show(ft, "mydialog");
}
public static class MyDialogFragment extends DialogFragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_main, container, false);
Button b = (Button) v.findViewById(R.id.button);
b.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
FragmentTransaction ft = getFragmentManager().beginTransaction();
Fragment prev = getFragmentManager().findFragmentByTag("mydialog");
if (prev != null) {
ft.remove(prev);
}
ft.addToBackStack(null);
MyDetailsFragment dialog = new MyDetailsFragment();
dialog.show(ft, "mydialog");
}
});
return v;
}
}

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