5

根据Android 指南,我正在尝试使用 Preference Fragments 实现首选项。在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

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

4

3 回答 3

12

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 
于 2014-08-15T08:30:39.360 回答
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;
    }
}
于 2014-08-14T14:59:59.937 回答
0

可能是片段的对象被保存在内存中,即使它被销毁。因此,当再次创建 Fragment 时,来自前一个 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);
}
于 2014-08-16T15:20:08.907 回答