Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwitchPreferences calls multiple times the onPreferenceChange() method

According to the Android guide I'm trying to implement preferences using Preference Fragments. In preferences.xml I declare:

<SwitchPreference 
        android:key="enable_wifi"
        android:title="Enable WiFi"
        />

And than in class thah extends PreferenceFragment in onCreate method I do:

public class FragmentSettings extends PreferenceFragment {

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    addPreferencesFromResource(R.xml.preferences);

    mEnableWifi = (SwitchPreference) findPreference(enable_wifi);
    mEnableWiFi.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {

    @Override
    public boolean onPreferenceChange(Preference preference, Object newValue) {

        Log.i(getClass().getName(), preference.getKey()
            + String.valueOf(newValue));
    }
}

And as a result I got when I clik on SwitchPreferene or Switch inside log shows

enable_wifi false
enable_wifi false
enable_wifi true
enable_wifi true

So that's why I suppose that listener is called multiple times. How to handle with it or fix it ?

like image 861
ghost Avatar asked Oct 21 '13 20:10

ghost


1 Answers

It is due to the bug in SwitchPreference implementation.

The callback onPreferenceChange is called:

  • First time by TwoStatePreference.onClick method, which just updates the SharedPreference.
  • Second time by the toggle state of the Switch widget. Here it is invoked.

Can't comment on the logic, but atleast the framework should have taken care of invoking onPreferenceChange callback only if there was a change in the state. So the responsibility lies with us. Use SwitchPreference.isChecked method to check whether the state got changed.

public boolean onPreferenceChange(Preference preference, Object newValue) {     
    if(((SwitchPreference) preference).isChecked() != (Boolean) newValue) {
        // State got changed
        Log.i("Testing", preference.getKey() + " : " + String.valueOf(newValue));

        // If you don't want to save the preference change return false from this if block.
    }               
    return true;
}   

Here is the callstack for your reference:

TwoStatePreference.onClick :

MainActivity$SettingsFragment$1.onPreferenceChange(Preference, Object) line: 45 
SwitchPreference(Preference).callChangeListener(Object) line: 895   
SwitchPreference(TwoStatePreference).onClick() line: 65 
SwitchPreference(Preference).performClick(PreferenceScreen) line: 950   
PreferenceScreen.onItemClick(AdapterView, View, int, long) line: 215    
ListView(AdapterView).performItemClick(View, int, long) line: 298   
ListView(AbsListView).performItemClick(View, int, long) line: 1100  
AbsListView$PerformClick.run() line: 2788   
AbsListView$1.run() line: 3463  
Handler.handleCallback(Message) line: 730   
ViewRootImpl$ViewRootHandler(Handler).dispatchMessage(Message) line: 92 
Looper.loop() line: 137 

Switch widget toggle :

MainActivity$SettingsFragment$1.onPreferenceChange(Preference, Object) line: 45 
SwitchPreference(Preference).callChangeListener(Object) line: 895   
SwitchPreference$Listener.onCheckedChanged(CompoundButton, boolean) line: 47    
Switch(CompoundButton).setChecked(boolean) line: 126    
Switch.setChecked(boolean) line: 666    
SwitchPreference.onBindView(View) line: 106 
like image 193
Manish Mulimani Avatar answered Sep 28 '22 06:09

Manish Mulimani