4

My problem is - when I click on a <Switch>, it gets toggled first and then the OnCheckedChangeListener is called.

What I would like is this:

<Switch> is clicked --> I show an AlertDialog --> If pressed yes or no --> Then flip ` with setChecked(boolean), [boolean = true if pressed yes, and false if pressed no].

Problem: When <Switch> is clicked, it gets flipped automatically. I want to qualify it with a yes or no from a AlertDialog first.

sw_enableDisable
                .setOnCheckedChangeListener(new OnCheckedChangeListener() {

                    @Override
                    public void onCheckedChanged(CompoundButton buttonView,
                                                 boolean isChecked) {
                        if (isChecked) {

                            AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(
                                    getActivity());

                            alertDialogBuilder
                                    .setMessage(
                                            "Sure you want to enable?. ")
                                    .setCancelable(true)
                                    .setPositiveButton(
                                            getString("YES"),
                                            new DialogInterface.OnClickListener() {
                                                @Override
                                                public void onClick(DialogInterface dialog,
                                                                    int which) {

                                                    dialog.cancel();
                                                }
                                            })

                                    .setNegativeButton(
                                            getString("NO"),
                                            new DialogInterface.OnClickListener() {

                                                @Override
                                                public void onClick(DialogInterface dialog,
                                                                    int which) {

                                                    dialog.cancel();
                                                }
                                            });

                            AlertDialog alertDialog = alertDialogBuilder.create();
                            alertDialog.show();
                            sw_enDis_alreadyClicked = true;

                        } else {
                            AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(
                                    getActivity());
                            alertDialogBuilder
                                    .setMessage(
                                            "Sure you want to disable?")
                                    .setCancelable(true)
                                    .setPositiveButton(
                                            getString("YES"),
                                            new DialogInterface.OnClickListener() {
                                                @Override
                                                public void onClick(DialogInterface dialog,
                                                                    int which) {
                                                    dialog.cancel();
                                                }
                                            })

                                    .setNegativeButton(
                                            getString("NO"),
                                            new DialogInterface.OnClickListener() {

                                                @Override
                                                public void onClick(DialogInterface dialog,
                                                                    int which) {

                                                    dialog.cancel();
                                                }
                                            });

                            AlertDialog alertDialog = alertDialogBuilder.create();
                            alertDialog.show();
                        }
                    }
                });
user1406716
  • 9,565
  • 22
  • 96
  • 151

5 Answers5

6

Try this code. It worked for me:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // ...
    Switch switchButton = (Switch) findViewById(R.id.my_switch_id);

    switchButton.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {
            final Switch btn = (Switch) v;
            final boolean switchChecked = btn.isChecked();

            if (btn.isChecked()) {
                btn.setChecked(false);
            } else {
                btn.setChecked(true);
            }

            String message = "Are you sure you want to disable this setting?";
            if (!btn.isChecked()) {
                message = "Are you sure you want to enable this setting?";
            }
            AlertDialog.Builder builder = new AlertDialog.Builder(this); // Change "this" to `getActivity()` if you're using this on a fragment
            builder.setMessage(message)
                   .setPositiveButton("Ok", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int i) {
                            // "Yes" button was clicked
                            if (switchChecked) {
                                btn.setChecked(true);
                            } else {
                                btn.setChecked(false);
                            }
                        }
                    })
                   .setNegativeButton("Cancel", null)
                   .show();
        }
    });
Edric
  • 24,639
  • 13
  • 81
  • 91
  • This won't work when you slide the switch, onClick event is not triggered. Besides that, what happens on rotation? Do you think that's ok for user experience? – vladimir123 Jan 23 '17 at 13:49
  • at least, this is the real accepted answer and solved our problem as well. – mochadwi Feb 07 '19 at 10:57
  • I do not know why this is accepted answer, honestly. If you are hoping to do something very important before checking ( and you probably do, otherwise you wouldn't be asking for confirmation), missing the swipe event is a bug waiting to happen. I suggest handling the swipe of the switch ( or disabling it ), or using another control. – H.A.H. May 25 '21 at 08:23
3

Intercepting touch event will solve the problem. Return TRUE if don't want to send event forward to the system. I came up with below approach. Have tested, working as expected.

switchButton.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View view, MotionEvent motionEvent) {
            switch (motionEvent.getAction()) {
                case ACTION_DOWN:
                    if (!switchButton.isChecked()) {
                        AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(
                                ActivityLogin.this);

                        alertDialogBuilder
                                .setMessage(
                                        "Sure you want to enable?. ")
                                .setCancelable(true)
                                .setPositiveButton("YES",
                                        new DialogInterface.OnClickListener() {
                                            @Override
                                            public void onClick(DialogInterface dialog,
                                                                int which) {
                                                dialog.cancel();
                                                switchButton.performClick();
                                            }
                                        })

                                .setNegativeButton(
                                        "NO",
                                        new DialogInterface.OnClickListener() {

                                            @Override
                                            public void onClick(DialogInterface dialog,
                                                                int which) {

                                                dialog.cancel();
                                            }
                                        });

                        AlertDialog alertDialog = alertDialogBuilder.create();
                        alertDialog.show();
                        return true;
                    } else{
                        // show dialog Sure you want to disable? and handle the button events accordingly
                    }
            }
            return false;
        }
    });
Krishna Sharma
  • 2,828
  • 1
  • 12
  • 23
  • 1
    Would you be able to add more information as to why this would work, and how it addresses the issues asked in the question? Whilst providing a solution is great, giving the explanation will help others better. See [How do I write a good answer](http://stackoverflow.com/help/how-to-answer) for more details. – Adrian Sanguineti Oct 27 '16 at 08:38
  • Intercepting touch event serves our purpose. Simply consume the `ACTION_DOWN` touch event and call `performaClick` to change the switch button status once user done with dialog button events. – Krishna Sharma Oct 27 '16 at 10:03
2

prevent switching by setting back state.

    Switch switchButton=(Switch)rootView.findViewById(R.id.switch_id);


    switchButton.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {
            final Switch btn=(Switch)v;
            final boolean estado=btn.isChecked();

            if(btn.isChecked())btn.setChecked(false);
            else btn.setChecked(true);


            DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    switch (which){
                    case DialogInterface.BUTTON_POSITIVE:
                        if(estado)btn.setChecked(true);
                        else btn.setChecked(false);
                        break;

                    case DialogInterface.BUTTON_NEGATIVE:
                        //No button clicked
                        break;
                    }
                }
            };

            String message="Disable function?";
            if(!btn.isChecked()) message="Enable function?";

            AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
            builder.setMessage(message).setPositiveButton("YES", dialogClickListener)
                .setNegativeButton("NO", dialogClickListener).show();


        }
    });
1

Use the following updated code.

sw_enableDisable
                .setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {

                    @Override
                    public void onCheckedChanged(CompoundButton buttonView,
                                                 boolean isChecked) {
                        if (toogledProgrammatically) {
                            toogledProgrammatically = false;
                        } else {
                            if (isChecked) {

                                AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(
                                        MainActivity.this);

                                alertDialogBuilder
                                        .setMessage(
                                                "Sure you want to enable?. ")
                                        .setCancelable(true)
                                        .setPositiveButton(
                                                "YES",
                                                new DialogInterface.OnClickListener() {
                                                    @Override
                                                    public void onClick(DialogInterface dialog,
                                                                        int which) {

                                                        dialog.cancel();
                                                    }
                                                })

                                        .setNegativeButton(
                                                "NO",
                                                new DialogInterface.OnClickListener() {

                                                    @Override
                                                    public void onClick(DialogInterface dialog,
                                                                        int which) {

                                                        dialog.cancel();
                                                        toogledProgrammatically = true;
                                                        sw_enableDisable.toggle();
                                                    }
                                                });

                                AlertDialog alertDialog = alertDialogBuilder.create();
                                alertDialog.show();
                            } else {
                                AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(
                                        MainActivity.this);
                                alertDialogBuilder
                                        .setMessage(
                                                "Sure you want to disable?")
                                        .setCancelable(true)
                                        .setPositiveButton(
                                                "YES",
                                                new DialogInterface.OnClickListener() {
                                                    @Override
                                                    public void onClick(DialogInterface dialog,
                                                                        int which) {
                                                        dialog.cancel();
                                                    }
                                                })

                                        .setNegativeButton(
                                                "NO",
                                                new DialogInterface.OnClickListener() {

                                                    @Override
                                                    public void onClick(DialogInterface dialog,
                                                                        int which) {

                                                        dialog.cancel();
                                                        toogledProgrammatically = true;
                                                        sw_enableDisable.toggle();
                                                    }
                                                });

                                AlertDialog alertDialog = alertDialogBuilder.create();
                                alertDialog.show();
                            }
                        }
                    }
                });

So whenever the user clicks on No of the AlertDialog, you're supposed to toggle the switch again by using

sw_enableDisable.toggle();

But this would in turn call onCheckedChanged() and then it'd be cycle all over again. So to handle that, maintain a boolean toogledProgrammatically and set it to true whenever you're toggling it in the code. And then when the onCheckedChanged() is called, just check if it was toggled programmatically or not. If yes, then don't do anything, else show the alert.

if (toogledProgrammatically) {
                            toogledProgrammatically = false;
                        } else {
                            if (isChecked)
.
.
.
Antrromet
  • 15,294
  • 10
  • 60
  • 75
0

Try to use onClick:

switch.setOnClickListener(new OnCLickListener(){
    Here_is_createing_of_dialog...
    dialogBuilder.setPositiveButton(
    getString("YES"),
    new DialogInterface.OnClickListener() { 
    @Override
    public void onClick(DialogInterface dialog, int which) {
        dialog.cancel();
        switch.setChecked(true);
    })
});
Beyka
  • 1,372
  • 1
  • 10
  • 15
  • 1
    This wont work because `switch.setChecked(true);` will again call onCheckedChange – Antrromet Jul 26 '15 at 19:25
  • Yes, but i told that user should use only onClick without onChackedChange. In begin of onClick method you can add checking of switch status if(switch.isChecked()) – Beyka Jul 26 '15 at 19:37
  • **This is better and less confusing than using the `setOnCheckedChangeListener`** method since there is a lot of maintenance programmatically. You explicitly have to create a flag that defines if the call is made programmatically because, it creates a loop. – sud007 Sep 27 '16 at 07:06
  • less confusing yes, but this doesn't work when the user slides the switch instead of clicking it. – Tyler Mar 31 '19 at 21:52