SwitchPreferences多次调用onPreferenceChange()方法

时间:2013-10-21 20:20:47

标签: android android-fragments android-preferences

根据the Android guide我正在尝试使用首选项碎片实现首选项。在preferences.xml我声明:

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

并且在课堂上thah在onCreate方法中扩展了PreferenceFragment:

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));
    }
}

结果当我登录SwitchPreferene或Switch里面的日志显示时,我得到了

enable_wifi false
enable_wifi false
enable_wifi true
enable_wifi true

这就是为什么我认为监听器被多次调用。如何处理或修复它?

3 个答案:

答案 0 :(得分:11)

这是由SwitchPreference实施中的错误造成的。

调用回调onPreferenceChange

无法对逻辑发表评论,但只有在状态发生变化时,框架才应该注意调用onPreferenceChange回调。所以责任在于我们。使用SwitchPreference.isChecked方法检查状态是否已更改。

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;
}   

以下是供您参考的callstack:

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 

切换小部件切换:

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 

答案 1 :(得分:1)

OP的代码缺少onPreferenceChange的返回语句,这很奇怪。

确保最后调用return true;,因此确实更改了首选项。

如果问题仍然存在,请在首选项更改侦听器内部进行检查,以便它不会不必要地更新:

@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
    if (preference.isEnabled() != newValue) {
        // Do something on normal switch
        return true;
    } else {
        // Preference wasn't changed, do nothing and don't update it
        return false;
    }
}

答案 2 :(得分:0)

可能是片段的对象被保存在内存中,即使它被销毁。因此,当再次创建Fragment时,前一个片段的侦听器仍然存在,并且您看到的回调可能来自两个不同的侦听器。为了确认调用确实是来自两个不同的听众,请尝试打印对象的toString方法。

@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
     Log.i(getClass().getName(), preference.getKey() + String.valueOf(newValue));
     Log.i(getClass().getName(), this.toString());
}

如果你为toString获得不同的值,那么我认为删除片段的onDestory中的监听器可能会为你解决问题。

@Override
public void onDestroy() {
    super.onDestroy();
    mEnableWifi.setOnPreferenceChangeListener(null);
}