Prevent DialogFragment from dismissing when button is clicked

I have a DialogFragment with a custom view which contains two text fields where the user is to input their username and password. When the positive button is clicked, I want to validate that the user actually did input something before dismissing the dialog.

public class AuthenticationDialog extends DialogFragment {

    public Dialog onCreateDialog(Bundle savedInstanceState) {
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        LayoutInflater inflater = getActivity().getLayoutInflater();
        builder.setView(inflater.inflate(R.layout.authentication_dialog, null))
            .setPositiveButton(getResources().getString(R.string.login), new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    // TODO
                }
            })
            .setNegativeButton(getResources().getString(R.string.reset), new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    // TODO
                }
            });

        return builder.create();
    }
}

So how can I prevent the dialog from dismissing? Is there some method I should override?

  • Wifi Authentication Error in Android
  • Generate Signed APK in Android Studio
  • onViewCreated - wrong place to replace fragment?
  • Scroll to last line of TableLayout within a ScrollView
  • Android: After building platform source, how to sign arbitrary APK with platform key?
  • Android Immediate Auto Complete
  • Related posts:

    Android: How do I set the zoom level of map view to 1 km radius around my current location?
    InflateException: Couldn't resolve menu item onClick handler
    Android, Blur Bitmap instantly?
    Cannot resolve symbol AppCompatActivity - Support v7 libraries aren't recognized?
    Google Map crashing with Resources$NotFoundException when replaced in FrameLayout
    Which permissions can be granted to rooted devices?
  • Determining the size of an Android view at runtime
  • What is the life cycle of a public static variable in Android?
  • ArrayIndexOutOfBoundsException while dismissing Snackbar/ViewDragHelper
  • How to change the style progerssbar in SwipeRefreshLayout?
  • PDF Viewer Widget in Android
  • Android: Executing code only on first run and every subsequent upgrade
  • 4 Solutions collect form web for “Prevent DialogFragment from dismissing when button is clicked”

    Override the default button handlers in OnStart() to do this.

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState)
    {
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setMessage("Test for preventing dialog close");
        builder.setPositiveButton("Test", 
            new DialogInterface.OnClickListener()
            {
                @Override
                public void onClick(DialogInterface dialog, int which)
                {
                    //Do nothing here because we override this button later to change the close behaviour. 
                    //However, we still need this because on older versions of Android unless we 
                    //pass a handler the button doesn't get instantiated
                }
            });
        return builder.create();
    }
    
    @Override
    public void onStart()
    {
        super.onStart();    //super.onStart() is where dialog.show() is actually called on the underlying dialog, so we have to do it after this point
        AlertDialog d = (AlertDialog)getDialog();
        if(d != null)
        {
            Button positiveButton = (Button) d.getButton(Dialog.BUTTON_POSITIVE);
            positiveButton.setOnClickListener(new View.OnClickListener()
                    {
                        @Override
                        public void onClick(View v)
                        {
                            Boolean wantToCloseDialog = false;
                            //Do stuff, possibly set wantToCloseDialog to true then...
                            if(wantToCloseDialog)
                                dismiss();
                            //else dialog stays open. Make sure you have an obvious way to close the dialog especially if you set cancellable to false.
                        }
                    });
        }
    }
    

    See my answer here https://stackoverflow.com/a/15619098/579234 for more explanation and examples on other dialog types too.

    Thanks to Luksprog, I was able to find a solution.

    AuthenticationDialog.java:

    public class AuthenticationDialog extends DialogFragment implements OnClickListener {
    
        public interface AuthenticationDialogListener {
            void onAuthenticationLoginClicked(String username, String password);
            void onAuthenticationResetClicked(String username);
        }
    
        private AuthenticationDialogListener mListener;
    
        private EditText mUsername;
        private EditText mPassword;
        private Button mReset;
        private Button mLogin;
    
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            View view = inflater.inflate(R.layout.authentication_dialog, container);
            this.getDialog().setTitle(R.string.login_title);
    
            mUsername = (EditText) view.findViewById(R.id.username_field);
            mPassword = (EditText) view.findViewById(R.id.password_field);
            mReset = (Button) view.findViewById(R.id.reset_button);
            mLogin = (Button) view.findViewById(R.id.login_button);
    
            mReset.setOnClickListener(this);
            mLogin.setOnClickListener(this);
    
            return view;
        }
    
        public void onAttach(Activity activity) {
            super.onAttach(activity);
            // Verify that the host activity implements the callback interface
            try {
                // Instantiate the NoticeDialogListener so we can send events to the host
                mListener = (AuthenticationDialogListener) activity;
            } catch (ClassCastException e) {
                // The activity doesn't implement the interface, throw exception
                throw new ClassCastException(activity.toString()
                        + " must implement AuthenticationDialogListener");
            }
        }
    
        public void onClick(View v) {
            if (v.equals(mLogin)) {
                if (mUsername.getText().toString().length() < 1 || !mUsername.getText().toString().contains("@")) {
                    Toast.makeText(getActivity(), R.string.invalid_email, Toast.LENGTH_SHORT).show();
                    return;
                } else if (mPassword.getText().toString().length() < 1) {
                    Toast.makeText(getActivity(), R.string.invalid_password, Toast.LENGTH_SHORT).show();
                    return;
                } else {
                    mListener.onAuthenticationLoginClicked(mUsername.getText().toString(), mPassword.getText().toString());
                    this.dismiss();
                }
            } else if (v.equals(mReset)) {
                mListener.onAuthenticationResetClicked(mUsername.getText().toString());
            }
        }
    }
    

    authentication_dialog.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical" >
        <EditText
            android:id="@+id/username_field"
            android:inputType="textEmailAddress"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:layout_marginLeft="4dp"
            android:layout_marginRight="4dp"
            android:layout_marginBottom="4dp"
            android:hint="@string/username"
            />
        <EditText
            android:id="@+id/password_field"
            android:inputType="textPassword"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="4dp"
            android:layout_marginLeft="4dp"
            android:layout_marginRight="4dp"
            android:layout_marginBottom="12dp"
            android:fontFamily="sans-serif"
            android:hint="@string/password"
            />
        <View
            android:layout_width="fill_parent"
            android:layout_height="1dip"
            android:background="?android:attr/dividerVertical" 
            />
        <LinearLayout 
            style="?android:attr/buttonBarStyle"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:paddingTop="0dp"
            android:measureWithLargestChild="true" >
            <Button 
                android:id="@+id/reset_button"
                style="?android:attr/buttonBarButtonStyle"
                android:layout_height="wrap_content"
                android:layout_width="0dp"
                android:layout_weight="1.0"
                android:text="@string/reset"
                />
            <Button 
                android:id="@+id/login_button"
                style="?android:attr/buttonBarButtonStyle"
                android:layout_height="wrap_content"
                android:layout_width="0dp"
                android:layout_weight="1.0"
                android:text="@string/login"
                />
        </LinearLayout>
    </LinearLayout>
    

    This is the “sweetspot” solution of both Karakuri’s and Sogger’s answers. Karakuri was on the right track, however you can only get the button that way if is already shown (it’s null otherwise, as stated in the comments). This is why Sogger’s answer works, however I prefer the setup in the same method, which is onCreateDialog, and not additionally in onStart. The solution is to wrap the fetching of the buttons into the OnShowListener of the dialog.

    public Dialog onCreateDialog(Bundle savedInstanceState) {
      AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
      // your dialog setup, just leave the OnClick-listeners empty here and use the ones below
    
      final AlertDialog dialog = builder.create();
      dialog.setOnShowListener(new DialogInterface.OnShowListener() {
        @Override
        public void onShow(final DialogInterface dialog) {
          Button positiveButton = ((AlertDialog) dialog).getButton(DialogInterface.BUTTON_POSITIVE);
          positiveButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(final View v) {
              // TODO - call 'dismiss()' only if you need it
            }
          });
          Button negativeButton = ((AlertDialog) dialog).getButton(DialogInterface.BUTTON_NEGATIVE);
          // same for negative (and/or neutral) button if required
        }
      });
    
      return dialog;
    }
    

    You could just pop up the dialog again. Or, you could keep the positive button disabled until there is input in both fields. This is easy enough if you are creating the layout in onCreateVew(). If you are using the AlertDialog.Builder class instead, you can get a handle to the button like so:

    AlertDialog.Builder builder = new AlertDialog.Builder(context);
    /* ... */
    Dialog dialog = builder.create();
    Button positiveButton = ((AlertDialog) dialog).getButton(DialogInterface.BUTTON_POSITIVE);
    /* now you can affect the button */
    
    Android Babe is a Google Android Fan, All about Android Phones, Android Wear, Android Dev and Android Games Apps and so on.