I want to launch a dialog with a custom layout, which I've implemented via a DialogFragment. (I basically just changed onCreateView() and added button handlers). The dialog lets the user quickly change an important setting.
This dialog will be launched from several different activities. The different activities don't have much in common, except that they need to refresh after the user makes a change to the setting. They don't need to get any information from the dialog; they merely need to know when it's closed (dismissed).
What I've Tried
I tried having the activity refresh in onResume(), but launching and dismissing a dialog never seems to call this method. (So I'm not sure why it even exists, but that's probably a topic for another question.)
Next, I tried adding a DialogInterface.OnDismissListener to the dialog:
public static void showMyDialog(OnDismissListener listener, Activity activity)
{
DialogFragment fragment = new MyDialogFragment();
fragment.show(activity.getFragmentManager(), "date");
activity.getFragmentManager().executePendingTransactions();//A
fragment.getDialog().setOnDismissListener(listener);//B
}
When I originally left out the line A, I got a NullPointerException on line B because the dialog is null at that point. Following the advice of this SO answer, I put in the call to executePendingTransaction(). This causes an IllegalStateException on line B, with the message "OnDismissListener is already taken by DialogFragment and cannot be replaced." I also tried putting setOnDismissListener() before the call to show(), but that always caused a NullPointerException.
I then read this other SO answer, which says the original asker was "calling getDialog() too early in the DialogFragment's life cycle." So I tried adding a constructor to my DialogFragment:
public MyDialogFragment(SomeCallback illTakeAnythingICanGet)
{
//I'll store the callback here and call it later
}
Unfortunately, adding a constructor made Android Lint freak out with a fatal warning, and when I looked it up, I found a comment in this question that seems to say this approach will make it impossible to deal with the user rotating the screen while the dialog is open.
The Question
How can an activity figure out when a DialogFragment has closed (been dismissed) in a way that won't break my app if the user rotates the screen? Should I be using something else besides a DialogFragment?
This is just a longer explanation of harism's comment in case anyone else has the same problem I did.
You can accomplish what I wanted by creating an interface like this:
public interface MyDialogCloseListener
{
public void handleDialogClose(DialogInterface dialog);//or whatever args you want
}
Have the activity that launches your dialog (DialogFragment) implement this interface. Then give that DialogFragment the following method:
public void onDismiss(DialogInterface dialog)
{
Activity activity = getActivity();
if(activity instanceof MyDialogCloseListener)
((MyDialogCloseListener)activity).handleDialogClose(dialog);
}
More explanatory code for someone to do the same.
Create the interface as:
package com.example.dialoglistener;
import android.content.DialogInterface;
public interface MyDialogCloseListener {
public void handleDialogClose(DialogInterface dialog);
}
Implement the interface in activity as:
MyDialogCloseListener closeListener = new MyDialogCloseListener() {
#Override
public void handleDialogClose(DialogInterface dialog) {
//do here whatever you want to do on Dialog dismiss
}
};
Write a DismissListener in DialogFragement as
public void DismissListener(MyDialogCloseListener closeListener) {
this.closeListener = closeListener;
}
call DismissListener from your activity as:
dialogFragementObject.DismissListener(closeListener);
and finally write onDismiss method
#Override
public void onDismiss(DialogInterface dialog) {
super.onDismiss(dialog);
if(closeListener != null) {
closeListener.handleDialogClose(null);
}
}
Tyler's example was the only example I could find that actually worked. The only thing that needs changed for the example to work is the call to the DismissListner method in the DialogFragment class. He has it as:
dialogFragementObject.DismissListner(closeListener);
This just needs to be a cast to whatever your class name of that DialogFragment is. For example:
((MyDialogFragment)dialogFragementObject).DismissListner(closeListener);
Related
As i mentioned on title what is the difference between
this ..
public void dismissDialog(MyDialog dialog){
dialog.dismiss();
}
and this ..
public void dismissDialog(MyDialog dialog){
dialog.getDialog().dismiss();
}
Which one should i use ? Or is there even a difference between them ?
Edit 1: MyDialog is a DialogFragment
From 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.
So dismiss() method not only close the dialog but also do the management of fragment transactions involved in the process. But dialog.getDialog().dismiss() will just dismiss dialog only.
The correct way to close a DialogFragment is to use dismiss().
I need to clear password field on exit of app,i am exiting the app in other activity and on exit it goes to mainActivity which has login details in which i need to clear password field,how will i do this in other activity ,i tried using setText("") but in vain.
public void backButtonHandler() {
AlertDialog.Builder alertDialog = new AlertDialog.Builder(
ReminderActivity.this);
// Setting Dialog Title
alertDialog.setTitle("Leave application?");
// Setting Dialog Message
alertDialog.setMessage("Are you sure you want to leave the application?");
// Setting Positive "Yes" Button
alertDialog.setPositiveButton("YES",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
//I need to clear here all pwd data present in MainActivity in edittext
finish();
}
});
// Setting Negative "NO" Button
alertDialog.setNegativeButton("NO",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// Write your code here to invoke NO event
dialog.cancel();
}
});
// Showing Alert Message
alertDialog.show();
}
In your MainActivity do like this:
#Override
public void onResume() {
super.onResume(); // Always call the superclass method first
editText.SetText("");
}
So, it will clear editText value, whenever your MainActivity will get resumed.
Or when you click the login button do
editText.SetText("");
Then it will be cleared before another activity starts
Using setText won't work and may even throw some type of NPE if your activity is no longer in memory.
There are a couple of ways you can do this depending on your requirements:
You can simply clear the text field in the activity/fragment's onStop or onDestroy overrides
If you absolutely have to set the text field from a different activity, then I suggest the following design:
In your Application class, create a nested class that implements ActivityLifeCycleCallbacks. This is a great class to implement as it allows you to monitor the state of all of your activities. It resolved a lot of problems for me
If you have a singleton model, or static String value that binds to your password TextView then you're almost there (other workarounds available if you give us more info).
So let's look at some code:
Design 1:
public void onStop()
{
super.onStop();
myTextField.setText("");
}
Your Application class:
public class MyApplication extends Application
{
...
private static final class MyActivityLifeCycleCallback implements ActivityLifecycleCallbacks
{
#Override
public void onActivityStopped(Activity activity)
{
//Here, I am assuming that your class name is MainActivity
Log.i(TAG, "onActivityStopped:" + activity.getLocalClassName());
if("MainActivity".isEqual(activity.getLocalClassName())
{
myDataModel.getInstance().setPassword("");
//or if your password String member is static
//((MainActivity)activity).myPasswordMember = "";
}
//Or if you want to only clear the password text when RAActivity stops, simply replace "MainActivity" with the RAActivity class name.
}
#Override
public void onActivityDestroyed(Activity activity,
Bundle savedInstanceState)
{
Log.i(TAG, "onActivityDestroyed:" + activity.getLocalClassName());
if(activity.getLocalClassName().contains("MySecondActivity"))
{
//reset your password here - implementation will depend on how you have your data model organized (private, singleton, static, etc.)
}
}
....
}
Remember that this design would work well with some type of a singleton or central data model (think MVC architecture) so that you can propagate the change in data to your components.
Hope it helps
EDIT:
I have added the code according to your comment. But to be honest, I think it's a better idea to simply call the setText("") method in the MainActivity's onStop function like I suggested. This is a simple problem and my second design might be a bit too much. Anyway, the code is updated, so if you like it, mark it as an answer :)
Here's another idea. IF RAActivity calls MainActivity again (probably not) using startActivity, you can simply pass a bundle value to MainActivity that tells it that it's coming from RAActivity. That way, MainActivity can clear the password if it was called from RAActivity. Lots of options.
Here is what I would like to do:
1) Inside an Activity a dialog is shown. I use DialogFragment and FragmentManager for this, by calling:
dialogFragment.show(fragmentManager, "edit_task_list");
2) Inside the Dialog I have layout with a custom Button. I would like to perform some action when the button is clicked and later close the dialog.
How should I connect everything? I see two options:
1) onclick attribute in the Button and a method inside the Actvity. That was my original plan, but I don't how to get the Dialog from the Activity to dismiss it. Even if this is not the right way, how could this be done? I would like to understand how this works.
2) set on click listener on the button when the Dialog is created in DialogFragment. This will require me to pass some context from the Activity to the DialogFragment, so I would like to avoid it (and keep the DialogFragment as simple as possible).
Which of those options should I take?
Number 2 Doesn't require you to pass any context (and you shouldn't). You define an interface that can act as a contract between fragments and activities and make your activity implement it.
From your dialog and in your button.onClick(), you do something like this (untested code):
if ( getActivity() != null
&& !getActivity().finishing()
&& getActivity() instanceOf YourInterface) {
((YourInterface)getActivity()).onSomeNiceMethod();
dismiss(); // close the dialog (if this is what you want).
}
The interface looks like:
public interface YourInterface {
void onSomeNiceMethod();
}
And your Activity…
public class YourActivity implements YourInterface {
void onSomeNiceMethod() {
// Hey! The Button In The Dialog Has Been Pressed!
}
}
All Activity and Fragment classes have a built-in callback method for you to use when you start another Activity, Fragment, Dialog, or DialogFragment.
void onActivityResult(int requestCode, int resultCode, Intent data)
Since you want to start the Dialog from an Activity, using the Dialog class is better than the DialogFragment class. The latter is better for starting a dialog from a Fragment, because it has two methods for communicating back to the Fragment (get/set TargetFragment())
The Dialog class has the getOwnerActivity() method. This is the Activity you use when creating the Dialog with one of its constructors.
You set a onClickListener on the button in the Dialog class. To pass the result back to the Activity:
getOwnerActivity().onActivityResult(intIdentifyingme, Activity.RESULT_OK,
intent);
dismiss(); // close the dialog
You put additional info you want to send in an Intent.
1) onclick attribute in the Button and a method inside the Actvity.
That was my original plan, but I don't how to get the Dialog from the
Activity to dismiss it. Even if this is not the right way, how could
this be done? I would like to understand how this works.
Basically your Activity has to remember/know which dialog is active at the moment with something like curDialog=dialogFragment;, then when handling the button onclick action you'll know which dialog to dismiss. But this is really not a good idea since basically the Button View would "leak" from your DialogFragment to your Activity, which breaks object encapsulation.
2) set on click listener on the button when the Dialog is created in
DialogFragment. This will require me to pass some context from the
Activity to the DialogFragment, so I would like to avoid it (and keep
the DialogFragment as simple as possible).
As a previous answer mentioned, you don't need to pass any Context to it, especially since you can get the Activity by calling getActivity().
The solution depends on whether or not this dialog would be used by multiple Activities:
Used by a single Activity: #Martin's solution will work just fine
Used by multiple Activity: abstraction can be used such that only the user's decision is passed to a listener. This is a (modified) solution I came up for the same problem:
public class BaseDialogFragment extends DialogFragment {
protected TextView dialogEn;
protected Button dialogYes;
private Button dialogNo;
protected OnSelectListener listener;
public interface OnSelectListener {
public void onSelect(int type, boolean yes);
}
public void setOnSelectListener(OnSelectListener listener) {
this.listener = listener;
}
public BaseDialogFragment() {
super();
}
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.dialog_confirm, container, false);
dialogYes = (Button) v.findViewById(R.id.yes);
dialogNo = (Button) v.findViewById(R.id.no);
dialogEn = (TextView) view.findViewById(R.id.dialog_en);
dialogEn.setText(getArguments().getString("text_en"));
dialogYes.setOnClickListener(this);
dialogNo.setOnClickListener(this);
return v;
}
public void onClick(View v) {
if (listener != null) {
listener.onSelect(getArguments().getInt("type"),
v == dialogYes ? true : false);
}
getDialog().dismiss();
}
}
To use it some additional info needs to be provided:
Bundle bundle = new Bundle();
bundle.putInt("type", type); //type: an unique integer value that helps differentiate result from different dialogs
bundle.putString("text_en", en); //en: String to be displayed
dialog.setArguments(bundle);
dialog.setOnSelectListener(this);
So if the type value above is set to 115, then a dialogYes button click would trigger public void onSelect(int type, boolean yes) method to be called with 115 and true as the 1st & 2nd parameters.
Your first point about the onClick attribute in the xml should be avoided. Because handling a Dialog that way could be really painfull if you respect events like screen rotation or a setup with multiple dialogs. This leads into leaked window errors most of the time and needs unnecessary code overhead to avoid this. Because you have to keep track of the Dialog which is actually shown yourself.
To be able to dismiss the Dialog this way you can use the Tag you setted as you called dialogFragment.show(fragmentManager, "edit_task_list");
DialogFragment frag = (DialogFragment)getFragmentManager().findFragmentByTag("edit_task_list");
if(frag != null)
frag.dismiss();
The proper solution is to use an interface as a callback for the communication between the DialogFragment and the Activity. This keeps the Dialog modular and the code easy. Here is an example from the docs. For this you don't need a Context. You simply pass the interface to the dialog in the onAttach() callback. It has a reference of the Activity as a parameter, which called that Dialog.
// Example interface for the communication
public interface OnArticleSelectedListener {
public void onButtonClicked(/*any Parameters*/);
}
public static class FragmentA extends DialogFragment {
OnArticleSelectedListener mListener;
...
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mListener = (OnArticleSelectedListener) activity; // get the interface of the Activity
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnArticleSelectedListener");
}
}
...
}
Handle the Button click in the Dialog and call dismiss() in it, that the Dialog can dismiss itself. Have a look at this question why to use dismiss() instead of getDialog().dismiss().
yourButton.setOnClickListener(new OnClickListener(){
#Override
public void onClick(View v){
if(mListener != null) // check if the listener is still valid
mListener.onButtonClicked(...); // calls the Activity implementation of this callback
dismiss(); // dismiss the Dialog
}
});
In onPause() of the Dialog set the reference of the interface to null. This way you can be sure that the callback will only be used if the Dialog is showing.
Your Activity looks something like this to be able to handle the callback:
public class MyActivity extends Activity implements OnArticleSelectedListener{
...
#Override
public void onButtonClicked(...){
// your implementation here
}
}
I don't know your overall setup but if you would use an AlertDialog a click on the Buttons dismiss the Dialog automatically when the method returns.
I'm converting some of my project to use fragments. How do we communicate with a fragment dialog? I want to create a fragment dialog just to get some text input from the user. When the dialog is dismissed, I'd like to pass the entered text back to the "parent" fragment (the one that started it). Example:
public class MyFragment extends Fragment {
public void onBtnClick() {
// What's a good way to get data back from this dialog
// once it's dismissed?
DialogFragment dlgFrag = MyFragmentDialog.newInstance();
dlgFrag.show(getFragmentManager(), "dialog");
}
}
Thanks
As eternalmatt said the given solution does not really answer the question. The way to communicate the dialog with the fragment is calling:
dialog.setTargetFragment(myCallingFragment, requestCode);
The way I do this is by creating the FragmentDialog with an static method where the listener is instanciated an then do the setFragmentTarget() stuff:
public mySuperFragmentDialog extends DialogFragment {
public interface SuperListener{
void onSomethingHappened();
}
public static mySuperFragmentDialog newInstance(SuperListener listener){
MySuperFagmentDialog f = new MySuperFragmentDialog();
f.setTargetFragment((Fragment) listener, /*requestCode*/ 1234);
return f;
}
}
To create the dialog from the fragment just do as usual:
Dialog dialog = MySuperFragmentDialog.newInstance(parentFragment);
dialog.show();
Then when you want to comunicate with the fragment which calls the dialog just:
Fragment parentFragment = getTargetFragment();
((SuperListener) parentFragment).onSomethingHappened();
This solution works only when dialog is gonna be created from Fragments and not from Activities, but you can combine both methods ('setFragmentTarget()' and the 'onAttach()' one) plus some Class checks to provide a full solution.
A great way to pass this kind of Events is a Callback Interface like descripted in the Android Developers Guide
Your Fragment define a Callback Interface like
public class MyFragment extends Fragment {
...
// Container Activity must implement this interface
public interface OnArticleSelectedListener {
public void onArticleSelected(Uri articleUri);
}
...
}
Then you check inside your onAttach Method if the Parent implemented the Callback Interface and save the Instance.
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mListener = (OnArticleSelectedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement OnArticleSelectedListener");
}
}
when your Event inside the Fragment happens you simply call the Callback Handler
mListener.onArticleSelected(...);
Hope that helps, further infos here
I had this problem once and after I solved it, I created a project that would remind me how I did it. I put the project on github so anyone can see the solution. Here is the link: https://github.com/mumasaba/FragmentFragmentBoss
In this project, we have a simple app with a TextView displaying the words 'Hello World'. This text view is on a fragment which is hosted by the main app activity. This fragment needs to display a new word that the user can enter after they click on the add options menu icon. When clicked, the options menu item calls up a dialog allowing the user to type in their new word. After the user is done, they can click ok to dismiss the dialog and display their new input on the fragment's text view. Therefore, Fragment to DialogFragment communication is illustrated.
There is a new pattern possible which is to share a ViewModel instance between fragments. When instantiating a ViewModelFactory where to get your ViewModels, you have to specify a context as parameter. If the context is the same for both fragments (i.e: the parent activity or parent fragment) and you instantiate the same ViewModel from both fragments, you will get the same instance.
This opens a new range of possibilities but also challenges.
Im writing an Android application in which a user selection triggers a custom Dialog, from which a selection may trigger a second Dialog.
When showing the initial Dialog from the Activity class, I'm setting an onDismissListener on it to pull out user selections which works fine other in cases where the 2nd Dialog is not triggered. The issue that I'm having is that I can't figure out how to have the first one Dialog remain open until the 2nd one is dismissed, so that the information from both is sent back to the Activity class.
Hopefully some code will make this a little more clear:
MainActivity class where I am launching the initial CustomDialog:
customDialog = new CustomDialog(this);
customDialog.show();
customDialog.setOnDismissListener(new OnDismissListener(){
public void onDismiss(DialogInterface di){
slection = customDialog.getselection();
updateUI(); //updates a listview with the results
}
});
Within the CustumDialog class where I am launching the SecondDialog on top of it:
if(specify){
SecondDialog secondDialog = new SecondDialog(context);
secondDialog.show();
secondDialog.setOnDismissListener( new OnDissmissListener(){
public void onDismiss(DialogInterface di){
// this is where I want to call the CustomDialog's dismiss() method
// so that they both close at the same time and the data from here
// can be sent back to the MainActiivty through the CustomDialog's
// onDismissListener
}
});
}
dismiss();
So, to reiterate: I'm trying to prevent the CustomDialog's dismiss() method to be called until the SecondDialog is also dismissed. Is there a way that I can call it from the SecondDialog's OnDismissListener?
You should create customDialog as an instance level variable. You then it will be accessible with onDismiss(...) of second dialog. There you can call customDialog.dismiss();
// Instance level variable
private Dialog customDialog = null;
Instanciate your customDialog, then create second dialog from within your customDialog. Your Second dialog's code would look like this.
if(specify){
SecondDialog secondDialog = new SecondDialog(context);
secondDialog.show();
secondDialog.setOnDismissListener( new OnDissmissListener(){
public void onDismiss(DialogInterface di){
// customDialog is accessible as it is declared as instance level variable
MyClassName.this.customDialog.dismiss();
}
});
}
dismiss();
I prefer to save the data in 1st dialog before going to send one and when dismiss the 2nd dialog, open the 1st dialog again with saved data .. i used this way in my developing and its effective ..