Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android SwitchPreferences change together in PreferenceActivity

Tags:

android

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

like image 569
Guangming Mao Avatar asked Jan 03 '14 14:01

Guangming Mao


1 Answers

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.

like image 161
Guangming Mao Avatar answered Nov 14 '22 23:11

Guangming Mao