Search code examples
androidandroid-fragmentsandroid-dialogfragmentandroid-support-library

Android Multiple Nested DialogFragment


I have a scenario where I show a DialogFragment (using getSupportFragmentManager()) that shows a list of values and an "Add" button. If the user clicks the add button, I display another DialogFragment (using getChildFragmentManager() that consists of an EditText and Button. When the user clicks the new Button if the value passes validation, I add it to the list. If it doesn't pass validation, I show another DialogFragment that displays an error message, and an "OK" button. I would like it that when the "OK" button is pressed, the user will still see the DialogFragment with the EditText and Button so that they could look over the bad value and possibly change it. However, when the "OK" button is pressed in the error message DialogFragment, the dialog is dismissed, and only the original DialogFragment (the one with the list of values) is displayed.

How would I keep the second DialogFragment still displayed after the third one is dismissed?

EDIT: Here are some code snippets:

//clicking this button shows the first ("outer") fragment
findViewById(R.id.voicemail_notifications_email_addresses).setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {
            AddressListDialog.newInstance( 
                   line, AddressListDialog.VOICEMAIL_NOTIFICATIONS_EMAIL)
                  .show(((FragmentActivity)getContext()).getSupportFragmentManager(), "dialog");
        }

});

public static class AddressListDialog extends DialogFragment {
    public AddressListDialog() {}

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        final View v = LayoutInflater.from(getActivity()).inflate(R.layout.vm_address_list_dialog, null);
        ListView lv = (ListView) v.findViewById(R.id.voicemail_notifications_address_list);
        final AddressAdapter adapter = new AddressAdapter();
        lv.setAdapter(adapter);

        v.findViewById(R.id.voicemail_add_address_button).setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                //first child fragment
                AddAddressDialog.newInstance(getType()).setAdapter(adapter)
                                .show(getChildFragmentManager(), "dialog");
            }

        });

        Dialog d = new AlertDialog.Builder(getActivity())
                .setTitle(titleRes)
                .setView(v)
                .setPositiveButton(R.string.global_dialog_pos, new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int whichButton) {
                            dialog.dismiss();
                        }
                    }
                )
                .create();
        return d;
    }

    private class AddressAdapter extends BaseAdapter {
        .
        .
        .
        public boolean add(LineVoicemailInfo.VmContact contact) {
            if(addresses.size() >= 3) {
                return false;
            }
            else if(!contact.isValid(isEmailType())) {
                return true;
            }
            else {
                addresses.add(contact);
                notifyDataSetChanged();
                return true;
            }
        }

public static class AddAddressDialog extends DialogFragment {
    private AddressAdapter adapter;

    public AddAddressDialog() {}

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        DialogInterface.OnClickListener onPosClick = null;

        final View v;
        v = LayoutInflater.from(getActivity()).inflate(R.layout.voicemail_add_email_address, null);
        onPosClick = new DialogInterface.OnClickListener() {                        
            @Override
            public void onClick(DialogInterface dialog, int which) {
                LineVoicemailInfo.VmContact contact = new LineVoicemailInfo.VmContact(((EditText)v.findViewById(R.id.voicemail_add_email_address)).getText().toString());
                if(!adapter.add(contact)) {
                    //second child fragment
                    BadAddressDialog.newInstance("Not a valid address")
                                    .show(getChildFragmentManager(), "dialog");
                }
            }
        };


        Dialog d = new AlertDialog.Builder(getActivity())
                .setTitle("Add Address")
                .setView(v)
                .setCancelable(false)
                .setPositiveButton(R.string.voicemail_addresses_add, onPosClick)
                .setNegativeButton(R.string.global_dialog_neg, new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int whichButton) {
                           dialog.dismiss();
                        }
                })
                .create();
        setCancelable(false);
        return d;
    }
}

public static class BadAddressDialog extends DialogFragment {           
        public BadAddressDialog() {}

        public static BadAddressDialog newInstance(String message) {
            BadAddressDialog frag = new BadAddressDialog();
            Bundle args = new Bundle();
            args.putString("message", message);
            frag.setArguments(args);
            return frag;
        }

        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {   
            String message = getArguments().getString("message");

            Dialog d = new AlertDialog.Builder(getActivity())
                    .setTitle("Error adding address")
                    .setMessage(message)
                    .setCancelable(false)
                    .setPositiveButton(R.string.global_dialog_pos, new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int whichButton) {
                            dialog.dismiss();
                        }
                    })
                    .create();
            setCancelable(false);
            return d;
        }
    }

Solution

  • I suppose the problem is that you use standard show method for displaying dialogs. According to Android sources, this method does not add fragment to backstack, so then the top-most dialog is dismissed, the middle is dismissed as well.

    You should begin trasaction on your own before calling show and add the transaction to backstack (call addToBackStack). An example is shown on DialogFragment documentation page.