2

I'm trying to use a SwitchPreference in my Settings page, but i can't get the listener to work. I am using a custom widget layout in order to style the switch, as I cannot find how to use themes to achieve this. In my settings.xml i have the following:

<SwitchPreference
            android:key="uitestmode"
            android:summary="Enable to display test data in fragments"
            android:title="UI Test Mode"
            android:widgetLayout="@layout/widget_custom_switch_on_off" >
</SwitchPreference>

and my SettingsFragment looks like:

SwitchPreference uiTestModePref = (SwitchPreference) findPreference("uitestmode");
uiTestModePref.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {

    @Override
    public boolean onPreferenceChange(Preference preference, Object newValue) {
        Log.d(preference.getKey(), newValue.toString());
        return false;
    }
});

and my custom layout for my switch:

<?xml version="1.0" encoding="utf-8"?>
<Switch xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/custom_switch_item"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="center_vertical"
    android:textColor="@android:color/white"
    android:textIsSelectable="false"
    android:textSize="18sp"
    android:textOn="On"
    android:textOff="Off"
    android:textStyle="bold"
    android:thumb="@drawable/carcast_switch_inner_holo_dark"
    android:track="@drawable/carcast_switch_track_holo_dark" />

The problem I have is that when I click the switch, the listener is not called. Does anybody know why and how to fix it?

Nelson.b.austin
  • 3,080
  • 6
  • 37
  • 63

3 Answers3

7

I ran into the same issue, my solution is similar to the followings:

  1. In your custom switch layout, add following code. This "uncovers" the preference, and makes the whole block clickable. That's why the listener is not triggered at all.

    android:clickable="false" android:focusable="false"

  2. Extend "SwitchPreference", and in "onBindView" class, get states from SharedPreferences, and handle switch states change there.

    public class MySwitchPreference extends SwitchPreference {
    
        public MySwitchPreference(Context context) {
            super(context, null);
        }
    
        public MySwitchPreference(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
        public MySwitchPreference(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
        }
    
        protected void onBindView(View view) {
            // Clean listener before invoke SwitchPreference.onBindView
            ViewGroup viewGroup= (ViewGroup)view;
            clearListenerInViewGroup(viewGroup);
            super.onBindView(view);
    
            final Switch mySwitch = (Switch) view.findViewById(R.id.custom_switch_item);
            Boolean initVal = this.getPersistedBoolean(false);
            if (initVal) {
                mySwitch.setChecked(true);
            }
            this.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
                @Override
                public boolean onPreferenceChange(Preference preference, Object newValue) {
                    //Do your things here, toggling the switch and so on
                    return true;
                }
            });
        }
    
        private void clearListenerInViewGroup(ViewGroup viewGroup) {
            if (null == viewGroup) {
                return;
            }
    
            int count = viewGroup.getChildCount();
            for(int n = 0; n < count; ++n) {
                View childView = viewGroup.getChildAt(n);
                if(childView instanceof Switch) {
                    final Switch switchView = (Switch) childView;
                    switchView.setOnCheckedChangeListener(null);
                    return;
                } else if (childView instanceof ViewGroup){
                    ViewGroup childGroup = (ViewGroup)childView;
                    clearListenerInViewGroup(childGroup);
                }
            }
        }
    }
    

Hopefully this can help you. I was not able to find any solution for my case until I figured it out this way.

RickCase
  • 111
  • 4
  • 1
    Hey RickCase, I've used this and you're solution works like a charm. But I want some extra functionality. Not sure if I need to make a new topic for it but maybe Nelson b. Austin also wants this. What I want is for my preference to change when it's clicked (what your change does) but also to be slidable and make this change my preference. Making it slidable can be done by changing android:clickable to true. But his undoes your previous solution. Do you know a way to do this? Kind regards, Jeroen – Jeroen Aug 18 '14 at 09:14
  • 1
    @Jeroen I haven't got a chance to try it myself, but I think you may want to try followings: 1. Set android:clickable to true. 2 .Set [OnCheckedChangeListener](http://developer.android.com/reference/android/widget/CompoundButton.OnCheckedChangeListener.html) to the Switch, and manually update the preference. This [link](http://stackoverflow.com/questions/552070/android-how-do-i-set-a-preference-in-code) might be helpful. – RickCase Aug 19 '14 at 20:08
  • Thanks, that was the solution – Jeroen Aug 28 '14 at 07:11
2

Solution – based on @RickCase answer, but works with both Switch and SwitchCompat.

Oleksii Malovanyi
  • 6,780
  • 6
  • 24
  • 27
1

The id of SwitchView should be @android:id/switch_widget instead of @+id/custom_switch_item or any other custom id.

In the source of SwitchPreference class, we can find the code:

View switchView = view.findViewById(AndroidResources.ANDROID_R_SWITCH_WIDGET);

and in AndroidResources:

static final int ANDROID_R_SWITCH_WIDGET = android.R.id.switch_widget;

so only id = @android:id/switch_widget can be found correctly.

Here's a demo about SwitchPreference's widgetLayout:

<Switch xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/switch_widget"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:gravity="center_vertical" />
Chenhe
  • 924
  • 8
  • 19