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..
Related
I have fragment which contains recycleview with list. in fragment i'm calling another bottom sheet dialog fragment, i Want to know when i dismiss that bottom sheet dialog fragment how to refresh base fragments list.
I Have Tried onpause and onresume method in base fragment. Please help me to solve the issue.
This method trigger when dialog fragment dismissed. To override the method in dialog fragment.
#Override
public void onDismiss(#NonNull DialogInterface dialog) {
super.onDismiss(dialog);
// use interface to callback method in base fragment
}
I'm not sure, but I tried blow code and it worked.
#Override
public void dismiss() {
super.dismiss();
((MyActivity)context).myRefreshFunction();//
}
I've got a bit of a problem here.
Basically, I have an activity. In the centre of the activity, I inflate a Viewpager fragment with some buttons inside of it. So I have two classes. Now, When the user clicks on the button inside the viewpager, I need to open a custom dialog. I couldn't find a way to implement the custom dialog in within my viewpager class, so I implemented it in within my activity. It does work here when I open the dialog from any button in the activity EXCEPT for the ones that come from the viewpager. Since this is a bit complicated, here an image I drew nicely in the paint:
So, I decided to leave the dialog inside the activity and just open it up via the viewpager. This looked like this:
Fragment:
button1.Click += delegate
{
var xy = new Activity_Categories();
xy.click();
};
The activity (Activity_Categories):
public void click()
{
ShowDialog(0);
}
protected override Dialog OnCreateDialog(int id)
{
return new AlertDialog.Builder(this)
.SetIcon(Resource.Drawable.btn_bookoflifeMainMenu)
.SetTitle("test")
.SetPositiveButton("ok", this)
.SetNegativeButton("no", this)
.SetItems(choices, this)
.Create();
return base.OnCreateDialog(id);
}
public void OnClick(IDialogInterface dialog, int which)
{
Toast.MakeText(this, which.ToString(), ToastLength.Short).Show();
}
So, what I tried is to open the method "click" from the viewpager that is then opening the dialog. But I get this error:
Unhandled Exception:
Java.Lang.NullPointerException: Attempt to invoke virtual method 'android.content.pm.ApplicationInfo android.content.Context.getApplicationInfo()' on a null object reference
I'm guessing it has something to do with the context "this". But even when I defined a Context context, and in Oncreate went: context = this; . I got the same error.
So the two questions:
How could I open up the dialog from the viewpager fragment? OR
Why am I getting this error?
If you know a better way of doing it, I'd be more than happy to hear! :)
THANK YOU!
I have fragment with EditTexts that user can change.
As well I have back button on Toolbar (and physical back button too)
When the user hits back, I check if the data was changed and if it was - I need to open dialog and ask the user "Do you want to save changes?". Get the click and act accordingly (positive or negative answer).
The best place to save the data (maybe I wrong) is in onPause of this fragment.
The problem is with the dialog - it is not modal and while it's showing the question and waits for user reaction - the fragment under it disappears and previous come back from stack.
I need to "pause the onPause" with the dialog until the user make his choice. What the easy (or most correct) way to do it?
#Override
public void onPause() {
if (!(text.getText().toString().equals(user.getName())))
{
new MaterialDialog.Builder(getActivity())
.title("Save changes?")
.content("You changed you personal details, save changes?")
.cancelable(false)
.positiveText("Save")
.negativeText("Discard")
.onPositive(new MaterialDialog.SingleButtonCallback() {
#Override
public void onClick(#NonNull MaterialDialog dialog, #NonNull DialogAction which) {
save();
}})
.show();
}
super.onPause();
}
If I want to do it before onPause - I'll need to catch the Toolbar's back button and physical back button - seems too much work for this. Looking for elegant way.
Thank you
onPause is called when the activity moves into the background (not being shown on the screen anymore).
To show a dialog before the app goes off screen, you can override the "onBackPressed" function in an activity.
#Override
public void onBackPressed() {
performBackPressed();
}
public void performBackPressed(){
//show your dialog here
//call finish(); when done to close app
}
You can call the performBackPressed() method whenever the Toolbar's back button is pressed too.
If you're trying to show a popup from a fragment when back is pressed, then you still have to override that method in the activity, then notifiy the fragment whenever back is pressed.
In my app I have a container in the activity that holds all my fragments. I use this code to get the currently active fragment:
YourFragmentClass curFrag = (YourFragmentClass) fragmentManager.findFragmentById(R.id.fragment_container);
then I have a method in the fragment called onBackPressed() and I just call that on the fragment I just got.
I have a simple DialogFragment that calls dismiss when exits, according to the documentation:
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.
however, I found that the fragment is still on the backstack after calling dismiss() so I have to click back button to clear it. Does anyone know why ?
here's my code:
public void onCreate(Bundle b) {
super.onCreate(b);
setContentView(R.layout.test_layout);
class MyDialogFragment extends DialogFragment implements OnClickListener{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.hello_world, container, false);
Button b = (Button)v.findViewById(R.id.btn);
b.setOnClickListener(this);
return v;
}
#Override
public void onClick(View v) {
dismiss();
}
}
getFragmentManager().beginTransaction().add(android.R.id.content, new MyDialogFragment(), "test").addToBackStack("b").commit();
}
#Override
public void onBackPressed() {
if (getFragmentManager().getBackStackEntryCount() > 0 ){
getFragmentManager().popBackStack();
} else {
super.onBackPressed();
}
}
}
I also found out that if I don't override onBackPressed(), the back button simple doesn't work, no matter how many fragments I add to the activity, the back button always exits the activity right away.
I can confirm what #Luksprog said in his comment: the dialog must be started through show(FragmentTransaction, String).
Note after looking the source: make sure to call addToBackStack(String) on the supplied transaction or else it still won't work.
That it's a wrong way to create a DialogFragment.
Never ever use the FragmentManager to show a DialogFragment. To be shown there are a method called show(FragmentTransacion, String).
In java:
MyDialogFragment mDialogFragment = new MyDialogFragment();
mDialogFragment.show(getFragmentManager(), "MyDialogFragment");
For another hand, to dismiss the dialog just do this:
mDialogFragment.dismiss()
Another think that I would like to highlight is that the MyDialogFragment class is defined inner onCreate method :'(
Please, define the class outside the method or in another file if you want :)
Good Look!
dismiss()
findNavController().navigate(FirstBottomSheetDialogDirections.actionFirstSheetToSecondSheet())
This code is always the wrong thing to do: dismiss() is an asynchronous operation that doesn't actually dismiss anything immediately. That is unlike the navigate() which does immediately update the NavController's state, stacking the new dialog destination on top of the previous one.
This means that when the asynchronous dismiss actually happens, it correctly removes the dialog and, because it is a navigation stack, removes everything on top of it - including your second dialog. However, due to a bug in the DialogFragmentNavigator, we don't actually dismiss that second dialog, which is why it appears to work, despite everything actually already being internally out of sync (thus causing the later crash).
The correct way to pop a destination and navigate to a new destination as an atomic, immediate operation is to use popUpTo and popUpToInclusive. Therefore you can fix the sample app by removing the call to dismiss() and updating the action to pop the first dialog as part of the navigate call:
<action
android:id="#+id/action_firstSheet_to_secondSheet"
app:destination="#id/secondSheet"
app:popUpTo="#id/firstSheet"
app:popUpToInclusive="true"/>
This correctly pops the first dialog off the back stack and then navigates to the new dialog destination.
please refer this link : https://issuetracker.google.com/issues/191073055
Our application uses nested dialogs, and we've been successfully making one dialog that sits on top of another dialog by constructing it from the first dialog's getContext() method. So:
Activity:
//...
Dialog1 dialog = new Dialog1(this);
dialog.show();
//...
Dialog1:
//...
Dialog1(Context context) {
super(context);
//etc.
}
public void onSomeCondition() {
Dialog2 dialog2 = new Dialog2(getContext());
dialog2.show();
//etc.
}
However, there is a circumstance where we want to launch Dialog2 directly from the Activity while Dialog1 is still visible. So we put this method in the Activity:
public void onSomeOtherCondition() {
Dialog2 dialog = new Dialog2(this); //crunch
dialog.show();
//etc.
}
The window manager doesn't like this at all. So is it that getContext() is actually NOT exactly the same as the ContextWrapper-ness in the Activity? If so exactly how does this secondary context differ from the primary one, and if (for example) you passed back getContext() from a dialog to a calling Activity, would that create the same leak risk as holding on to a Context reference elsewhere can do?
If it's not the context, what's causing the problem?
I suspect the problem with starting Dialog 2 from Activity 1 when Dialog 1 is visible is because Dialog 1 (not Activity 1) is on the top of the activity stack. I'm by no means an expert, but I suspect that only the Activity at the top of the Activity stack can start new Activities.
I'm not entirely certain if the contexts are different (it would appear they are), but I suspect the problem is that you are not dismissing Dialog1 before attempting to start Dialog2 from your Activity. The WindowManager is probably angry because you are attempting to start a dialog on top of your Activity, but Dialog1 is already there.
Long story short, I think you need:
public void onSomeOtherCondition() {
this.dismiss();
mActivity.onSomeOtherCondition(); //we have a reference to the activity
}
EDIT
The solution I proposed in the comments is to pass Dialog1's context to mActivity.onSomeOtherCondition so that you can create Dialog2 with the context that is at the top of the stack.