Guangming Mao
Guangming Mao

Reputation: 775

Android SwitchPreferences change together in PreferenceActivity

I'm using SwitchPreference in my Android app and found something so weird. There is more than one SwitchPreference in the preference.

When I use the default layout of PreferenceActivity, everything works so well. But after I set custom layout to the preference activity, those Switches begin change together when you click any of them. I'm testing it on a arm-based tablet. Also I test it on my Android phone, It works just so the same.

How does it happens!

Here is my custom layout (setting.xml) for preference:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <Button
            android:id="@+id/button1"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Button" />

        <Button
            android:id="@+id/button2"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Button" />
    </LinearLayout>

    <ListView
        android:id="@android:id/list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >
    </ListView>

</LinearLayout>

and the PreferenceScreen

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
    <SwitchPreference
        android:key="switch1"
        android:summary="This is switch 1"
        android:title="Switch 1" />
    <SwitchPreference
        android:key="switch2"
        android:summary="This is switch 2"
        android:title="Switch 2" />
</PreferenceScreen>

In the code, I just set the custom layout

public class SettingsActivity extends PreferenceActivity {
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.setting);
    }

    @Override
    protected void onPostCreate(Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);

        setupSimplePreferencesScreen();
    }

    private void setupSimplePreferencesScreen() {
        addPreferencesFromResource(R.xml.pref_general);
    }
}

Here is the screenshot of the preference, and the two switch always change at the same time what ever you click each of them. The screenshot of the preference

Upvotes: 5

Views: 4018

Answers (1)

Guangming Mao
Guangming Mao

Reputation: 775

It seems that SwitchPreference's onBindView will use other Switch view as the parameters.

Here is a bug issue in Android project in Google Code. It's NOT this problem, but similar, both describes switch views will act according to other switches' states.

So I found two way to solve the problem:

  1. Use CheckBoxPreference if there are more than one boolean preference in your screen.
  2. Use this workaround

    public class CustomSwitchPreference extends SwitchPreference {
    
        /**
         * Construct a new SwitchPreference with the given style options.
         *
         * @param context The Context that will style this preference
         * @param attrs Style attributes that differ from the default
         * @param defStyle Theme attribute defining the default style options
         */
        public CustomSwitchPreference(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
        }
    
        /**
         * Construct a new SwitchPreference with the given style options.
         *
         * @param context The Context that will style this preference
         * @param attrs Style attributes that differ from the default
         */
        public CustomSwitchPreference(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        /**
         * Construct a new SwitchPreference with default style options.
         *
         * @param context The Context that will style this preference
         */
        public CustomSwitchPreference(Context context) {
            super(context, null);
        }
    
        @Override
        protected void onBindView(View view) {
            // Clean listener before invoke SwitchPreference.onBindView
            ViewGroup viewGroup= (ViewGroup)view;
            clearListenerInViewGroup(viewGroup);
            super.onBindView(view);
        }
    
        /**
         * Clear listener in Switch for specify ViewGroup.
         *
         * @param viewGroup The ViewGroup that will need to clear the listener.
         */
        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);
                }
            }
        }
    
    }
    

I've tested both of the two solutions. They works.

Upvotes: 7

Related Questions