Have a disabled onClick?
Asked Answered
C

10

23

I want to be able to respond to a click event on a disabled switch, is that possible?

I have a switch that is not enabled until the user fills in some information, so it looks like this:

enter image description here

I want to prompt the user to fill out the information if they click on the disabled switch with a dialog, like so:

 mySwitch.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

            if (!userInfo.isFilled){
                new AlertDialog.Builder(MainActivity.this)
                        .setTitle("Fill out info first!")
                        .setMessage("You must first fill out info before turning on this featurel")
                        .setNeutralButton("Okay", null)
                        .show();
            }
        }
    });

However, the onClick() is not triggered when I click on the disabled switch, so how do I get when the user clicks on it?

Contemptible answered 7/8, 2016 at 21:26 Comment(3)
What is lightSwitch? Is its isEnabled() method automatically updated, or do you need to update it yourself (i.e. lightSwitch.setEnabled(!lightSwitch.isEnabled())?Curious
Also, what is mySwitch? Please include the declarations for your variables.Curious
any response ? did you check my code?Doublequick
K
13

You could place a transparent View on top of the Switch and toggle its enabled state opposite the Switch, and show the message when this overlaid View is clicked.

Kinson answered 8/8, 2016 at 20:17 Comment(0)
E
10

From the View.java source code,

public boolean dispatchTouchEvent(MotionEvent event) {
    // If the event should be handled by accessibility focus first.
    if (event.isTargetAccessibilityFocus()) {
        // We don't have focus or no virtual descendant has it, do not handle the event.
        if (!isAccessibilityFocusedViewOrHost()) {
            return false;
        }
        // We have focus and got the event, then use normal event dispatch.
        event.setTargetAccessibilityFocus(false);
    }

    boolean result = false;

    if (mInputEventConsistencyVerifier != null) {
        mInputEventConsistencyVerifier.onTouchEvent(event, 0);
    }

    final int actionMasked = event.getActionMasked();
    if (actionMasked == MotionEvent.ACTION_DOWN) {
        // Defensive cleanup for new gesture
        stopNestedScroll();
    }

    if (onFilterTouchEventForSecurity(event)) {
        //noinspection SimplifiableIfStatement
        ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnTouchListener != null
                && (mViewFlags & ENABLED_MASK) == ENABLED
                && li.mOnTouchListener.onTouch(this, event)) {
            result = true;
        }

        if (!result && onTouchEvent(event)) {
            result = true;
        }
    }

    if (!result && mInputEventConsistencyVerifier != null) {
        mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
    }

    // Clean up after nested scrolls if this is the end of a gesture;
    // also cancel it if we tried an ACTION_DOWN but we didn't want the rest
    // of the gesture.
    if (actionMasked == MotionEvent.ACTION_UP ||
            actionMasked == MotionEvent.ACTION_CANCEL ||
            (actionMasked == MotionEvent.ACTION_DOWN && !result)) {
        stopNestedScroll();
    }

    return result;
}

the enabled flag ensures the UnhandledEvents are consumed however not passed along to the listeners,thereby bypassing all your possible code.So it is not possible to listen to events on a disabled view.

That said, your options are,

  1. Change the style to mimic that of a disabled view as mentioned here,and then add your required functionality.
  2. Add a overlay invisible view to perform your required functionality which you can set to Gone once the view should be enabled.
  3. Use something apart from enabled,(you could setClickable(false) and consume touch events)
Evvy answered 18/8, 2016 at 10:22 Comment(1)
i went with option 2, so that i don't need to style all the different controls that i needed to disable... i used a relative layout so that i can put the overlay on top of the other controls. this answer helped me create an overlay.... https://mcmap.net/q/584465/-child-of-relativelayout-is-not-filling-the-height-of-the-relativelayoutCoverage
M
8

You can set onTouchListener and react to boolean (e.g isToggleEnable) reference with respect to the user's previous actions:

 mySwitch.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if(!isToggleEnable){
                //Taost here
                }
                //If isToggleEnable = false on return OnClickListener won't be called
                return isToggleEnable; 
            }
        });
Musick answered 21/8, 2016 at 11:42 Comment(0)
C
6

When it is disabled, setEnabled(false), these listeners won't work.

Try this way: don't disable it, use the setOnCheckedChangeListener and check against your is-entry-filled in there:

use setOnCheckedChangeListener

    switch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            if (!isEntryFilled) {
                buttonView.setChecked(false);
                // your alert dialog
            } else {
            }
        }
    });

this will re-check it back to off and pop your alert, until isEntryFilled is met.

EDIT

OR instead of setEnabled(false), use setClickable(false) or android:clickable="false" since docs say setClickable() is tied to click-events.

and instead of OnClickListener, try OnTouchListener. It will register your on-down-touch (and ignore your on-up-touch), since a click consists of down+up.

    switch.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            if (!isEntryFilled) {
                buttonView.setChecked(false);
                // your alert dialog
            }
            return false;
        }
    });

then somewhere else, where you check for isEntryFilled, reactivate your switch with switch.setClickable(true)

Chrischrism answered 7/8, 2016 at 21:29 Comment(4)
No, onCheckedChanged() is only to be called if the switch is enabled, and in this case it is not.Contemptible
When it is disabled, setEnabled(false), these listeners won't work, try my other suggestion.Chrischrism
I knew about the option to just uncheck it in the onCheckChanged if needed, but my question is if I can specifically detect a click on a disabled item. If it is not possible, I may need to resort to that other method, but I'm sure there is a way to find out if the user clicked on the disabled switch.Contemptible
disabled will disable all event listeners, but... try my new suggestionChrischrism
O
5

Try setting setFocusable(false) and setEnabled(true) on your switch. That way, click events will be fired while the switch still being "disabled". Taken from this answer.

Olibanum answered 18/8, 2016 at 9:11 Comment(0)
K
4
mySwitch.setOnClickListener(new View.OnClickListener() {
         @Override
         public void onClick(View v) {
                if (isClick()){
                   //Your Valid Code
                }else{
                  //Make our switch to false
                  new AlertDialog.Builder(MainActivity.this)
                           .setTitle("Fill out info first!")
                           .setMessage("You must first fill out info before turning on this featurel")
                            .setNeutralButton("Okay", null)
                            .show();
                   }
                }
});

    public Boolean isClick(){
         //check condition that user fill details or not
         //if yes then return true
         // else return false
    }
Killerdiller answered 19/8, 2016 at 13:27 Comment(3)
OnClickListerns don't work if the switch is disabled, that is what my question is about, how can I see if the disabled switch is clicked?Contemptible
dont disable switch, make it enable and apply this code.Killerdiller
no the whole point is to keep it disabled and find a work aroundContemptible
D
4

Let the Parent View intercept ClickEvents or TouchEvents, when its detected check if the receiving View is disabled, and do what you have to do.

Edit

"it doesn't work when disabled?"

try these codes, Im use LinearLayout for easy aligment. but overall it should give you an example

this is a full example

XML

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="70dp"
android:background="#273746">   

<FrameLayout
    android:layout_width="match_parent"        
    android:id="@+id/ass"
    android:background="@drawable/abc_popup_background_mtrl_mult"
    android:layout_height="match_parent">
</FrameLayout>

MainActivity onCreate

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_entry_screen);
    FrameLayout fl = (FrameLayout)findViewById(R.id.ass);
    Test t = new Test(this);
    FrameLayout.LayoutParams lp = (LayoutParams) fl.getLayoutParams();
    lp.height = LayoutParams.MATCH_PARENT;
    lp.width = LayoutParams.MATCH_PARENT;
    t.setOrientation(LinearLayout.VERTICAL);
    t.setLayoutParams(lp);
    fl.addView(t);
    t.setBackgroundColor(Color.YELLOW);
    Button b = new Button(this);
    b.setText("patricia");
    t.addView(b);
     b = new Button(this);
    b.setText("monica");
    t.addView(b);

    b = new Button(this);
    b.setText("rebecca");
    t.addView(b);

}

Test.java

public class Test extends LinearLayout {

public Test(Context context) {
    super(context);
}

@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
    StringBuilder sb = new StringBuilder();
    sb.append("intercept \n\r");
    int x = (int)event.getX(),
            y= (int)event.getY();

    for(int i =0; i< getChildCount(); i++){
        int[] pos = new int[]{getChildAt(i).getLeft(),getChildAt(i).getTop(),
                getChildAt(i).getMeasuredWidth(),
                getChildAt(i).getMeasuredHeight()};

        sb.append(getChildAt(i).getLeft()+", ");
        sb.append(getChildAt(i).getTop()+", ");
        sb.append(getChildAt(i).getMeasuredWidth()+", ");
        sb.append(getChildAt(i).getMeasuredHeight());
        sb.append("\n\r");
        sb.append(isInBounds(pos, x, y));
        sb.append("\n\r");
    }
    sb.append("x is ");
    sb.append(x);
    sb.append("y is ");
    sb.append(y);
    Toast.makeText(getContext(),sb.toString() , Toast.LENGTH_LONG).show();
    return super.onInterceptTouchEvent(event);
}

private boolean isInBounds(int[] dimen, int x, int y){
    return ((x >= dimen[0] && x < (dimen[0] + dimen[2]))
            && (y >= dimen[1] && y < (dimen[1] + dimen[3])));
}

}

Now The one you click will check out to be true, that is the child, now when it checks out to be true you can do something like this

View v = getchildAt(pos);
//its the one that is tapped or clicked
if(!v.isEnabled()){
    //this is the guy you want now, do what you want to do

for click event i am not try this, but you could just do View.performClick() or put your Dialog in the ViewGroup class and call it

actually you could use the View..getClipBounds() to save yourself from int array

Doublequick answered 20/8, 2016 at 9:30 Comment(2)
doesn't work if disabled another way to ask me for snippet? if yes you could ask@RuchirBaroniaDoublequick
check my edit bro and let me know if its helpful @RuchirBaroniaDoublequick
C
4

Set the disable switches on click listener to change the listeners of the other switches. For example:

Switch s = (Switch) findViewById(R.id.SwitchID);

if (s != null) {
    s.setOnCheckedChangeListener(this);
}

/* ... */

public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
    Toast.makeText(this, "The Switch is " + (isChecked ? "on" : "off"),
                   Toast.LENGTH_SHORT).show();
    if(isChecked) {
        //do stuff when Switch is ON
        //this is where you set your normal state OnClickListner
    } else {
            mySwitch.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {

                        if (!userInfo.isFilled){
                            new AlertDialog.Builder(MainActivity.this)
                            .setTitle("Fill out info first!")
                            .setMessage("You must first fill out info before turning on this featurel")
                            .setNeutralButton("Okay", null)
                            .show();
                        }
                    }
             });
      }
}
Capybara answered 23/8, 2016 at 9:26 Comment(0)
P
4

I'm guessing you've disabled the switch using switch.setEnabled(false). If so, the onclick event will not trigger. If you still want to handle a click action when the switch is disabled, you can use .setOnTouchListener()...

You're best bet however would be to use .setOnCheckedChangeListener() and keeping the switch enabled. Basically when onCheckChanged() gets called, you can popup your dialog if the switch value is on and when the user click ok, you default the switch back to off.

    mSwitched.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {

        @Override
        public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {

            if (checked && !userInfo.isFilled){
                new AlertDialog.Builder(Activity.this)
                        .setTitle("Fill out info first!")
                        .setMessage("You must first fill out info before turning on this featurel")
                        .setNeutralButton("Okay", new DialogInterface.OnClickListener() {

                            @Override
                            public void onClick(DialogInterface dialogInterface, int i) {

                                mSwitched.setChecked(false);
                            }
                        })
                        .show();
            }
        }
    });
Phonometer answered 23/8, 2016 at 21:17 Comment(0)
C
2

You can do this in a different way,Give a root layout to toggle button with same width and height of toggle button

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
    <!--Root layout to toggle button with same height and width 
        of toggle button-->
   <LinearLayout
       android:layout_width="wrap_content"
       android:id="@+id/linear"
       android:layout_height="wrap_content">
    <ToggleButton
        style="?android:attr/buttonStyleSmall"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/button"
         />
   </LinearLayout>

</RelativeLayout>

When you disable the button,make the button as not focasable and clickable .Then os will handover touch functionality to rootlayout.In the root layout click listner we can write the click logic when the button is not enabled

public class MainActivity extends AppCompatActivity {
    ToggleButton button;
    LinearLayout linearLayout;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        button= (ToggleButton) findViewById(R.id.button);
        linearLayout= (LinearLayout) findViewById(R.id.linear);
        //disabling button
        button.setEnabled(false);
        button.setClickable(false);
        button.setFocusableInTouchMode(false);
        button.setFocusable(false);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //write the logic here which will execute when button is enabled
            }
        });
        linearLayout.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //write the logic here which will execute when button is disabled
            }
        });
    }

}

When you enable the button,make button to clickable and focausable.

//enabling button
  button.setEnabled(true);
  button.setClickable(true);
  button.setFocusableInTouchMode(true);
  button.setFocusable(true);
Concretion answered 24/8, 2016 at 9:47 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.