73

正如here所述,我將PreferenceFragment繼承並在Activity中顯示它。該文件解釋瞭如何傾聽偏好更改here,但僅限於PreferenceActivity的子類。由於我沒有這樣做,我如何傾聽偏好變化?如何傾聽PreferenceFragment中的偏好更改?

我試過在我的PreferenceFragment中實現OnSharedPreferenceChangeListener,但它似乎不工作(onSharedPreferenceChanged似乎永遠不會被調用)。

這是到目前爲止我的代碼:

SettingsActivity.java

public class SettingsActivity extends Activity 
{ 
    @Override 
    protected void onCreate(Bundle savedInstanceState) 
    { 
     super.onCreate(savedInstanceState); 

     // Display the fragment as the main content. 
     getFragmentManager().beginTransaction().replace(android.R.id.content, new SettingsFragment()).commit(); 
    } 
} 

SettingsFragment.java

public class SettingsFragment extends PreferenceFragment implements OnSharedPreferenceChangeListener 
{ 
    public static final String KEY_PREF_EXERCISES = "pref_number_of_exercises"; 

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

     // Load the preferences from an XML resource 
     addPreferencesFromResource(R.xml.preferences); 
    } 

    @Override 
    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) 
    { 
     //IT NEVER GETS IN HERE! 
     if (key.equals(KEY_PREF_EXERCISES)) 
     { 
      // Set summary to be the user-description for the selected value 
      Preference exercisesPref = findPreference(key); 
      exercisesPref.setSummary(sharedPreferences.getString(key, "")); 
     } 
    } 
} 

的preferences.xml

<?xml version="1.0" encoding="utf-8"?> 
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" > 

    <EditTextPreference 
     android:defaultValue="15" 
     android:enabled="true" 
     android:key="pref_number_of_exercises" 
     android:numeric="integer" 
     android:title="Number of exercises" /> 

</PreferenceScreen> 

另外,PreferenceFragment是否適合傾聽偏好更改,或者我應該在活動內部執行嗎?

+1

對於你最後的問題,這一切都取決於你的設計框架。要使用MVC或MVP方法很難處理Android,但我嘗試讓所有操作都發生在託管該片段的活動(Controller)中,並且片段僅作爲UI(查看/演示者) –

回答

131

我相信你只需要在你的PreferenceFragment中註冊/取消註冊Listener,它就可以工作。

@Override 
public void onResume() { 
    super.onResume(); 
    getPreferenceManager().getSharedPreferences().registerOnSharedPreferenceChangeListener(this); 

} 

@Override 
public void onPause() { 
    getPreferenceManager().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this); 
    super.onPause(); 
} 

取決於你想做的事,你可能不需要使用監聽器的。對首選項的更改自動承諾至SharedPreferences

+0

Ah,I看到。這樣可行。但是,我應該通過'getPreferenceManager'(就像你所做的那樣)或'getPreferenceScreen'獲得SharedPreferences?有什麼不同? –

+0

說實話,我不確定真正的區別是什麼,也許別人可以對此加以衡量,對於另一個問題它可能是一個很好的話題。 – antew

+2

好的,[這裏](http://stackoverflow.com/q/13618335/963396)是對這個問題的回答。它看起來像絕對沒有功能差異,但getPreferenceManager通常是首選選項。 –

21

antew的解決方案效果很好,在這裏你可以看到Android的V11起一個完整的偏好活動:

import android.app.Activity; 
import android.content.SharedPreferences; 
import android.content.SharedPreferences.OnSharedPreferenceChangeListener; 
import android.os.Bundle; 
import android.preference.ListPreference; 
import android.preference.PreferenceFragment; 

public class UserPreferencesV11 extends Activity { 

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

    // Display the fragment as the main content. 
    getFragmentManager().beginTransaction().replace(android.R.id.content, 
      new PrefsFragment()).commit(); 
} 

public static class PrefsFragment extends PreferenceFragment implements OnSharedPreferenceChangeListener { 

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

     // Load the preferences from an XML resource 
     addPreferencesFromResource(R.xml.preferences); 

     // set texts correctly 
     onSharedPreferenceChanged(null, ""); 

    } 

    @Override 
    public void onResume() { 
     super.onResume(); 
     // Set up a listener whenever a key changes 
     getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this); 
    } 

    @Override 
    public void onPause() { 
     super.onPause(); 
     // Set up a listener whenever a key changes 
     getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this); 
    } 

    @Override 
    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { 
     // just update all 
     ListPreference lp = (ListPreference) findPreference(PREF_YOUR_KEY); 
     lp.setSummary("dummy"); // required or will not update 
     lp.setSummary(getString(R.string.pref_yourKey) + ": %s"); 

    } 
} 
} 
12

所有其他的答案是正確的。但是我更喜歡這個選擇,因爲你立即擁有引起變化的偏好實例。

@Override 
public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    Preference pref = findPreference(getString(R.string.key_of_pref));   
    pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { 
     @Override 
     public boolean onPreferenceChange(Preference preference, Object newValue) { 
      // do whatever you want with new value 

      // true to update the state of the Preference with the new value 
      // in case you want to disallow the change return false 
      return true; 
     } 
    }); 
} 
+0

更正,儘管在'onSharedPreferenceChanged()'中,您可以通過'findPreference(key)'輕鬆訪問Preference實例。也許'SharedPreferences'方法是首選的,因爲註冊/註銷的東西? –

2

這爲我工作從PreferenceFragment.onCreate()

OnSharedPreferenceChangeListener listener = 
    new SharedPreferences.OnSharedPreferenceChangeListener() 
    { 
     public void onSharedPreferenceChanged(SharedPreferences prefs, String key) 
     { 
     showDialog(); 
     } 
    }; 
0

另一個完整的例子讓你看到整個畫面。

public class SettingsActivity extends AppCompatPreferenceActivity { 


    /** 
    * A preference value change listener that updates the preference's summary 
    * to reflect its new value. 
    */ 
    private static Preference.OnPreferenceChangeListener 
      sBindPreferenceSummaryToValueListener = 
      new Preference.OnPreferenceChangeListener() { 

       @Override 
       public boolean onPreferenceChange(Preference preference, Object value) { 
        String stringValue = value.toString(); 

        if (preference instanceof ListPreference) { 
         // For list preferences, look up the correct display value in 
         // the preference's 'entries' list. 
         ListPreference listPreference = (ListPreference) preference; 
         int index = listPreference.findIndexOfValue(stringValue); 

         // Set the summary to reflect the new value. 
         preference.setSummary(
           index >= 0 
             ? listPreference.getEntries()[index] 
             : null); 

        } else if (preference instanceof RingtonePreference) { 
         // For ringtone preferences, look up the correct display value 
         // using RingtoneManager. 
         if (TextUtils.isEmpty(stringValue)) { 
          // Empty values correspond to 'silent' (no ringtone). 
          preference.setSummary(R.string.pref_ringtone_silent); 
         } else { 
          Ringtone ringtone = RingtoneManager.getRingtone(
            preference.getContext(), Uri.parse(stringValue)); 
          if (ringtone == null) { 
           // Clear the summary if there was a lookup error. 
           preference.setSummary(null); 
          } else { 
           // Set the summary to reflect the new ringtone display 
           // name. 
           String name = ringtone.getTitle(preference.getContext()); 
           preference.setSummary(name); 
          } 
         } 

        } else { 
         // For all other preferences, set the summary to the value's 
         // simple string representation. 
         preference.setSummary(stringValue); 
        } 
        return true; 
       } 
      }; 

    /** 
    * Helper method to determine if the device has an extra-large screen. For 
    * example, 10" tablets are extra-large. 
    */ 
    private static boolean isXLargeTablet(Context context) { 
     return (context.getResources().getConfiguration().screenLayout 
       & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_XLARGE; 
    } 

    /** 
    * Binds a preference's summary to its value. More specifically, when the 
    * preference's value is changed, its summary (line of text below the 
    * preference title) is updated to reflect the value. The summary is also 
    * immediately updated upon calling this method. The exact display format is 
    * dependent on the type of preference. 
    * 
    * @see #sBindPreferenceSummaryToValueListener 
    */ 
    private static void bindPreferenceSummaryToValue(Preference preference) { 
     // Set the listener to watch for value changes. 
     preference.setOnPreferenceChangeListener(sBindPreferenceSummaryToValueListener); 

     // Trigger the listener immediately with the preference's current value. 
     sBindPreferenceSummaryToValueListener.onPreferenceChange(preference, 
       PreferenceManager 
         .getDefaultSharedPreferences(preference.getContext()) 
         .getString(preference.getKey(), "")); 
    } 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setupActionBar(); 
    } 

    /** 
    * Set up the {@link android.app.ActionBar}, if the API is available. 
    */ 
    private void setupActionBar() { 
     ActionBar actionBar = getSupportActionBar(); 
     if (actionBar != null) { 
      // Show the Up button in the action bar. 
      actionBar.setDisplayHomeAsUpEnabled(true); 
     } 
    } 

    @Override 
    public boolean onMenuItemSelected(int featureId, MenuItem item) { 
     int id = item.getItemId(); 
     if (id == android.R.id.home) { 
      if (!super.onMenuItemSelected(featureId, item)) { 
       NavUtils.navigateUpFromSameTask(this); 
      } 
      return true; 
     } 
     return super.onMenuItemSelected(featureId, item); 
    } 

    /** 
    * {@inheritDoc} 
    */ 
    @Override 
    public boolean onIsMultiPane() { 
     return isXLargeTablet(this); 
    } 

    /** 
    * {@inheritDoc} 
    */ 
    @Override 
    @TargetApi(Build.VERSION_CODES.HONEYCOMB) 
    public void onBuildHeaders(List<Header> target) { 
     loadHeadersFromResource(R.xml.pref_headers, target); 
    } 

    /** 
    * This method stops fragment injection in malicious applications. 
    * Make sure to deny any unknown fragments here. 
    */ 
    protected boolean isValidFragment(String fragmentName) { 
     return PreferenceFragment.class.getName().equals(fragmentName) 
       || GPSLocationPreferenceFragment.class.getName().equals(fragmentName) 
       || DataSyncPreferenceFragment.class.getName().equals(fragmentName) 
       || NotificationPreferenceFragment.class.getName().equals(fragmentName); 
    } 

    ////////////////// NEW PREFERENCES //////////////////////////// 

    @TargetApi(Build.VERSION_CODES.HONEYCOMB) 
    public static class GPSLocationPreferenceFragment extends PreferenceFragment { 

     Preference prefGPSServerAddr, prefGPSASDID, prefIsGPSSwitch; 

     @Override 
     public void onCreate(Bundle savedInstanceState) { 
      super.onCreate(savedInstanceState); 
      addPreferencesFromResource(R.xml.pref_gps_location); 
      setHasOptionsMenu(true); 

      // Bind the summaries of EditText/List/Dialog/Ringtone preferences 
      // to their values. When their values change, their summaries are 
      // updated to reflect the new value, per the Android Design 
      // guidelines. 

      bindPreferenceSummaryToValue(findPreference("gpsServer_Addr")); 
      bindPreferenceSummaryToValue(findPreference("gpsASD_ID")); 


      prefGPSServerAddr = findPreference("gpsServer_Addr"); 
      prefGPSServerAddr.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { 
       @Override 
       public boolean onPreferenceChange(Preference preference, Object newValue) { 

        try { 
         // do whatever you want with new value 

        } 
        catch (Exception ex) 
        { 
         Log.e("Preferences", ex.getMessage()); 
        } 

        // true to update the state of the Preference with the new value 
        // in case you want to disallow the change return false 
        return true; 
       } 
      }); 

      prefGPSASDID = findPreference("gpsASD_ID"); 
      prefGPSASDID.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { 
       @Override 
       public boolean onPreferenceChange(Preference preference, Object newValue) { 

        try { 
         // do whatever you want with new value 

        } 
        catch (Exception ex) 
        { 
         Log.e("Preferences", ex.getMessage()); 
        } 

        // true to update the state of the Preference with the new value 
        // in case you want to disallow the change return false 
        return true; 
       } 
      }); 

      prefIsGPSSwitch = findPreference("isGPS_Switch"); 
      prefIsGPSSwitch.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { 
       @Override 
       public boolean onPreferenceChange(Preference preference, Object newValue) { 

        try { 
         // do whatever you want with new value 

        } 
        catch (Exception ex) 
        { 
         Log.e("Preferences", ex.getMessage()); 
        } 

        // true to update the state of the Preference with the new value 
        // in case you want to disallow the change return false 
        return true; 
       } 
      }); 
     } 

     @Override 
     public boolean onOptionsItemSelected(MenuItem item) { 
      int id = item.getItemId(); 
      if (id == android.R.id.home) { 
       boolean tabletSize = getResources().getBoolean(R.bool.isTablet); 
       if (tabletSize) { 
        startActivity(new Intent(getActivity(), MainActivity.class)); 
       } else { 
        startActivity(new Intent(getActivity(), SettingsActivity.class)); 
       } 
       return true; 
      } 
      return super.onOptionsItemSelected(item); 
     } 
    } 

    /////////////////////////////////////////////////////////////// 

    /** 
    * This fragment shows notification preferences only. It is used when the 
    * activity is showing a two-pane settings UI. 
    */ 
    @TargetApi(Build.VERSION_CODES.HONEYCOMB) 
    public static class NotificationPreferenceFragment extends PreferenceFragment { 
     @Override 
     public void onCreate(Bundle savedInstanceState) { 
      super.onCreate(savedInstanceState); 
      addPreferencesFromResource(R.xml.pref_notification); 
      setHasOptionsMenu(true); 

      // Bind the summaries of EditText/List/Dialog/Ringtone preferences 
      // to their values. When their values change, their summaries are 
      // updated to reflect the new value, per the Android Design 
      // guidelines. 
      bindPreferenceSummaryToValue(findPreference("notifications_new_message_ringtone")); 
     } 

     @Override 
     public boolean onOptionsItemSelected(MenuItem item) { 
      int id = item.getItemId(); 
      if (id == android.R.id.home) { 
       boolean tabletSize = getResources().getBoolean(R.bool.isTablet); 
       if (tabletSize) { 
        startActivity(new Intent(getActivity(), MainActivity.class)); 
       } else { 
        startActivity(new Intent(getActivity(), SettingsActivity.class)); 
       } 
       return true; 
      } 
      return super.onOptionsItemSelected(item); 
     } 
    } 

    /** 
    * This fragment shows data and sync preferences only. It is used when the 
    * activity is showing a two-pane settings UI. 
    */ 
    @TargetApi(Build.VERSION_CODES.HONEYCOMB) 
    public static class DataSyncPreferenceFragment extends PreferenceFragment { 
     @Override 
     public void onCreate(Bundle savedInstanceState) { 
      super.onCreate(savedInstanceState); 
      addPreferencesFromResource(R.xml.pref_data_sync); 
      setHasOptionsMenu(true); 

      // Bind the summaries of EditText/List/Dialog/Ringtone preferences 
      // to their values. When their values change, their summaries are 
      // updated to reflect the new value, per the Android Design 
      // guidelines. 
      bindPreferenceSummaryToValue(findPreference("sync_frequency")); 
     } 

     @Override 
     public boolean onOptionsItemSelected(MenuItem item) { 
      int id = item.getItemId(); 
      if (id == android.R.id.home) { 
       boolean tabletSize = getResources().getBoolean(R.bool.isTablet); 
       if (tabletSize) { 
        startActivity(new Intent(getActivity(), MainActivity.class)); 
       } else { 
        startActivity(new Intent(getActivity(), SettingsActivity.class)); 
       } 
       return true; 
      } 
      return super.onOptionsItemSelected(item); 
     } 
    } 
}