0

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?

gruszczy
  • 40,948
  • 31
  • 128
  • 181
  • I guess you should use interface and activity has to implement its methods. You can pass all usefull object in it , even dialog object. – Aleksandr Nov 02 '13 at 04:41

4 Answers4

0

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!
     }
}
Martin Marconcini
  • 26,875
  • 19
  • 106
  • 144
0

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.

Rick Falck
  • 1,778
  • 3
  • 15
  • 19
0

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:

  1. Used by a single Activity: @Martin's solution will work just fine
  2. 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.

Kai
  • 15,284
  • 6
  • 51
  • 82
0

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.

Community
  • 1
  • 1
Steve Benett
  • 12,843
  • 7
  • 59
  • 79