SwitchPreferences llama varias veces al método onPreferenceChange ()

Según la guía de Android estoy intentando implementar preferencias usando Fragmentos de preferencia. En preferences.xml declaro:

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

Y que en la clase Thah extends PreferenceFragment en onCreate método que hago:

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

Y como resultado conseguí cuando clik en SwitchPreferene o Switch dentro de los programas de registro

 enable_wifi false enable_wifi false enable_wifi true enable_wifi true 

Así que por eso supongo que el oyente se llama varias veces. ¿Cómo manejarlo o arreglarlo?

Se debe al error en la implementación de SwitchPreference .

El callback onPreferenceChange se llama:

  • Primera vez con el método TwoStatePreference.onClick , que acaba de actualizar la SharedPreference .
  • Segunda vez por el estado de Switch widget Switch . Aquí se invoca.

No puedo comentar sobre la lógica, pero por lo menos el marco debería haber tomado el cuidado de invocar onPreferenceChange callback sólo si había un cambio en el estado. Así que la responsabilidad recae en nosotros. Utilice el método SwitchPreference.isChecked para comprobar si el estado se ha cambiado.

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

Aquí está el callstack para su referencia:

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 

Alternar conmutador de widget:

 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 

Es extraño que el código del OP carezca de la declaración de retorno para onPreferenceChange .

Asegúrese de que está invocando return true; Al final, por lo que la preferencia es realmente cambiado.

Si, sin embargo, el problema persiste, haga una comprobación dentro del oyente de cambio de preferencia para que no se actualice innecesariamente:

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

Puede ser que el objeto del fragmento esté siendo guardado en memoria incluso cuando es destruido. Por lo tanto, cuando el fragmento se crea de nuevo, el oyente del fragmento anterior todavía está allí y la devolución de llamada que ve es puede ser de dos oyentes diferentes. Para confirmar que la llamada es de hecho de dos oyentes diferentes intente por favor imprimir el método toString del objeto.

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

Si obtienes valores diferentes para el toString entonces creo que la eliminación del oyente en el fragmento onDestory podría resolver el problema para ti.

 @Override public void onDestroy() { super.onDestroy(); mEnableWifi.setOnPreferenceChangeListener(null); } 
  • ¿Cómo abrir AlertDialog desde la pantalla de preferencias?
  • Android: onSharedPreferenceChanged no cambia un resumen de PreferenceScreen
  • Aplicar tema personalizado a PreferenceFragment
  • Obtener clave, no valor, de la selección ListPreference - ¿Posible?
  • ¿Cómo puedo copiar SharedPreferences a una tarjeta SD?
  • Cómo establecer el valor predeterminado de un ListPreference
  • ClassCastException en PreferenceActivity
  • Cómo agregar un botón a PreferenceScreen
  • Método de actividad de preferencias obsoletas
  • ¿Cómo agregar un botón a una PreferenceScreen?
  • Añadir un botón en Preference Row
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.