I have activity A which have a ListView with custom adapter.
The custom adapter (which aplies to each list view item) have a button which invokes a custom dialog.
in this dialog an action is being performed which in response i want to invoke UI update on activity A.
This is my activity on resume code:
#Override
public void onResume()
{
super.onResume();
setUI();
}
But when the i call
dialog.dismiss();
The dialog closes without the Activity A OnResume method benig invoked.
How can i catch and update the activity ui?
You can set an OnDismissListener to your dialog to achieve this:
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setOnDismissListener(listener);
...
Dialog dialog = builder.create();
You can let your Activity impelement DialogInterface.OnDismissListener and set them as the listener, so they get notified in the method onDismiss(DialogInterface dialog). In there, you can update your UI.
Actually its very easy - you can cast the Context inside the adapter to the calling containing activity:
ActivityName activity = (ActivityName )con;
activity.setUI();
Related
Disclaimer: I've checked the documentation and since 2.1.0 the navigation components has supported Dialog Fragments. (https://developer.android.com/jetpack/androidx/releases/navigation#2.1.0)
Error That I'm Getting
I'm getting this error when trying to go from a DialogFragment to my Start Destination:
java.lang.IllegalStateException: Fragment PostDistressDialog{829f5d1} (bbbc4926-684b-491b-9772-e0f0ffebe0af)} not associated with a fragment manager.
PostDistressDialog is a DialogFragment called from JournalEntryFragment(can be seen in map below) using the navigation component. PostDistressDialog is not an inner class of JournalEntryFragment. It is in a class of its own extending DialogFragment
Picture of my Navigation Graph
Function Calling NavController
public class PostDistressDialog extends DialogFragment implements ISaveDatabase {
...
#NonNull
#Override
public Dialog onCreateDialog(#Nullable Bundle savedInstanceState) {
if (getArguments()!=null) {
...
// Set up the Alert Dialog
AlertDialog.Builder alertDialog = new AlertDialog.Builder(getContext());
alertDialog.setTitle(R.string.distressed_levels);
alertDialog.setMessage(R.string.distressed_how_feel_post);
// Inflate and set the layout for the dialog
View layout = View.inflate(getActivity(), R.layout.dialog_seekbar, null);
alertDialog.setView(layout);
....
// Add okay button
alertDialog.setPositiveButton(R.string.okay, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// Save post distress value in Journal Entry
mJournalEntry.setPostDistress(mTempDistressValue);
// Save to Journal Entry to database
// Check if journal entry empty
if(isJournalEntryEmpty(mJournalEntry)){
...
}
else{
// Give title if empty
if(mJournalEntry.getTitle().isEmpty()) {
....
// Save to database
new SaveDatabase(getContext(),PostDistressDialog.this).execute(mJournalEntry);
}
// Go to main menu
}
});
return alertDialog.create();
}
return null;
}
...
#Override
public void databaseSavingCompleted(){
NavHostFragment.findNavController(this).navigate(PostDistressDialogDirections.postDistressDialogToJournalListAction());
}
}
Where this is public class PostDistressDialog extends DialogFragment
Dialog in my Navigation XML File
<dialog
android:id="#+id/postDistressDialog"
android:name="com.dgrullon.cbtjourney.dialogs.PostDistressDialog"
android:label="PostDistressDialog" >
<argument
android:name="postDistressDialogArguments"
app:argType="com.dgrullon.cbtjourney.pojo.JournalEntries"/>
<action
android:id="#+id/postDistressDialog_to_journalListAction"
app:destination="#id/journalList"
app:popUpTo="#id/journalList"
app:popUpToInclusive="true" />
</dialog>
AlertDialog automatically dismisses the Dialog (and hence, removes your DialogFragment) when the callback you add to setPositiveButton is fired. Because you're doing work asynchronously, your databaseSavingCompleted method is called after the DialogFragment is destroyed, detached from the FragmentManager, and removed from the NavController - you're leaking a reference to your DialogFragment (as it would otherwise be garbage collected).
Therefore when NavHostFragment.findNavController(this) fires, all hooks that would let it access the NavController are already cleaned up.
If you don't want your button to immediately dismiss the dialog, you need to pass in null to setPositiveButton() and instead get a reference to the button after the dialog has been created by calling its getButton() API and manually setting an OnClickListener that would kick off your AsyncTask (and disable the button to prevent it from being clicked more than once).
I have a dialog with onDismiss handler:
public class TextReaderDialog extends DialogFragment {
...
public void onDismiss() {
}
I show this dialog and add some styles to a part of text from the fragment:
TextReaderDialog d = new TextReaderDialog();
d.show(getFragmentManager(), "sample");
Spannable spannableText = new SpannableString(tv.getText());
spannableText.setSpan(new BackgroundColorSpan(Color.LTGRAY), startOffset, startOffset + w.word.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
tv.setText(spannableText);
Whenever a dialog is dismissed, I want to remove styles from the text. How can I do that? What is the correct way to do that?
The simplest way to go about this would be to add a method to your fragment like so:
public void dismissStyles(){
//do your style dismissing here
}
Now, I assume in the dialog you are overriding DialogFragment.onDismiss(DialogInterface dialog). As long as that is the case, once you have completed that method, in your dialog's onDismiss function, you can do something to the effect of:
#Override
public void onDismiss(DialogInterface dialog) {
super.onDismiss(dialog);
MyFragment fragment = (MyFragment) activity
.getFragmentManager()
.findFragmentByID(R.id.containerOfYourFragment);
if(fragment != null){
fragment.dismissStyles();
}
}
Here, activity should be the current activity that your fragment and dialog are hosted in. You can pass this to the dialog in a constructor, or depending on where the dialog is located. You could also just pass the current fragment to the dialog in the constructor as well, and then it would simply be called by myFragment.dismissStyles();.
I recently created a standard list DialogFragment to build an AlertDialog in my Activity as can be seen as the answer here:
What is the best way to recreate an AlertDialog when the screen is rotated?
Now I would like to re-use this fragment for 3 different "Pop Up" selection lists in my activity. For each of the three buttons I need to identify the calling button to determine what action to take when the item from the list is selected.
What is the best way to achieve this?
Currently I am thinking that I need to pass the calling button ID to the DialogFragment and then pass it back to the activity with the result when the dialog completes. Is there a better way to achieve this goal?
I think probably the easiest way to achieve what you're going for is to just have three different listeners inside of your DialogFragment, and then have setters for each. Then when you build the alert dialog as a fragment, you can define what the onClick method for each listener will do in the calling method. So something like this:
protected DialogInterface.OnClickListener mListener1;
protected DialogInterface.OnClickListener mListener2;
protected DialogInterface.OnClickListener mListener3;
public void setListener1(final YourDialogFragment.OnClickListener passedListener) {
mListener1 = new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
listener.onClick(getActivity(), dialog, which);
}
};
}
Then inside of the code that calls the DialogFragment, call something like:
// Building the Dialog Fragment here
YourDialogFragment.setListener1(new YourDialogFragment.OnClickListener() {
#Override
public void onClick(FragmentActivity activity, DialogInterface dialog, int which) {
// Whatever you want to happen when you click goes here
}
});
Ideally you make some sort of helper to just take parameters so you're not explicitly calling the set methods from an activity, but that's the gist of it.
I would recommend you to show the dialog fragment from another fragment where you can implement the onClick listeners and use setTargetFragment() to tell the dialog fragment that who it is working with..
dialogFragment.setTargetFragment(this, 0);
and use getTargetFragment() to get the parent fragment from DialogFragment.
here is some code snippets from sample programs..
// Retrieve the progress bar from the target's view hierarchy.
mProgressBar = (ProgressBar)getTargetFragment().getView().findViewById(
R.id.progress_horizontal);
And also you can use setRetainInstance(true) in onCreate() method to tell the framework to try to keep this fragment around during a configuration changes
See this answer to get more idea, hope this helps..
I am using a custom DialogFragment to let a user change his login credentials. There are some text fields and two buttons (save/cancel). The layout is set in DialogFragment's onCreateView method.
If I open the dialog text fields are filled with default values. When the user changes text in a text field and clicks the cancel button the dialog is dismissed. Next time the dialog opens the text field changed before does not contain the default value as i expected but the text the user changed before. The text fields are not reset. This is almost the same problem mentioned here Reset an Android Dialog. The problem is that the solution provided refers to a Dialog which is deprecated in API level 11 and i cannot use onPrepareDialog with a DialogFragment.
Is there a similar way to reset the content of a DialogFragment?
You can override onResume() in your class, which extends DialogFragmet, as follows:
private static class MyDialogFragment extends DialogFragment {
public static MyDialogFragment newInstance() {
// ...
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// ...
}
#Override
public void onResume() {
super.onResume();
Dialog dialog = getDialog();
// reset code goes here - use dialog as you would have in onPrepareDialog()
}
}
You can also use .setText() method in Your activity as reaction after negative button click. Eg:
In DialogFragment.java, onCreateDialog(...)define AlertDialog.Builder
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
then
//this is better than creating button in layout
builder.setNegativeButton(R.string.button_cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
((MainAct) getActivity()).cancelDialog(DialogFragment.this);
}
}
);
In MainActivity.java create method cancelDialog(DialogFragment df) {
//here use df to reset text fields
}
I have a BaseActivity that opens Dialogs on it's code, but I can't change this class code, so I extended it, on my subclass, how to know when a Dialog is being open?
You could try to override onCreateDialog(). This passes a reference ID that is used when the Activity calls showDialog(id). If you just need to know if any Dialog is going to be shown, then I suppose you could call the super which will return the dialog that will be shown.
#Override
protected Dialog onCreateDialog(int id){
Dialog dialogToBeShown = super.onCreateDialog(id);
if(dialogToBeShown != null){
***Do whatever you have to with the dialog***
}
return dialogToBeShown;
}
EDIT:
This will only work the first time the Dialog is first created. You can do something similar with onPrepareDialog(int id, Dialog dialog, Bundle args) which is always called when a dialog is opening.
Dialog has an isShowing() method that should return if the dialog is currently visible. So you can use that to see if a dialog is showing and hide it with dismissDialog(). You just have to keep a reference to the Dialogs you create in onCreateDialog().