Làm thế nào để lắng nghe các thay đổi về sở thích trong PreferenceFragment?


92

Như được mô tả ở đây , tôi đang phân lớp PreferenceFragment và hiển thị nó bên trong Activity. Tài liệu đó giải thích cách lắng nghe các thay đổi tùy chọn ở đây , nhưng chỉ khi bạn phân lớp PreferenceActivity. Vì tôi không làm điều đó, làm cách nào để lắng nghe các thay đổi về sở thích?

Tôi đã thử triển khai OnSharedPreferenceChangeListener trong PreferenceFragment của mình nhưng nó dường như không hoạt động ( onSharedPreferenceChangeddường như không bao giờ được gọi).

Đây là mã của tôi cho đến nay:

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

tùy chọn.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>

Ngoài ra, PreferenceFragment có phải là nơi thích hợp để lắng nghe các thay đổi về ưu tiên hay tôi nên làm điều đó trong Activity?


1
Đối với câu hỏi cuối cùng của bạn, tất cả phụ thuộc vào Khung thiết kế của bạn. Để sử dụng cách tiếp cận MVC hoặc MVP là điều khó thực hiện với Android nhưng tôi cố gắng để tất cả các hành động của mình diễn ra trong hoạt động (Bộ điều khiển) lưu trữ phân đoạn và phân đoạn chỉ là ui (View / Presenter)
John Shelley

Câu trả lời:


149

Tôi tin rằng bạn chỉ cần đăng ký / hủy đăng ký Listenertrong của bạn PreferenceFragmentvà nó sẽ hoạt động.

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

}

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

Tùy thuộc vào những gì bạn muốn làm, bạn có thể không cần sử dụng một người nghe. Các thay đổi đối với các tùy chọn được cam kết SharedPreferencestự động.


Ah tôi thấy. Điều đó hoạt động. Nhưng tôi có nên tải SharedPreferences qua getPreferenceManager(như bạn đã làm) hay getPreferenceScreenkhông? Có gì khác biệt?
XåpplI'-I0llwlg'I -

Thành thật mà nói, tôi không chắc sự khác biệt thực sự là gì, có lẽ ai đó khác có thể cân nhắc về nó, nó cũng có thể là một chủ đề tốt cho một câu hỏi khác.
antew

2
Được rồi, đây là một câu trả lời cho câu hỏi đó. Có vẻ như hoàn toàn không có sự khác biệt về chức năng, nhưng getPreferenceManagernói chung là lựa chọn ưu tiên.
XåpplI'-I0llwlg'I - 20/12/12

khi tùy chọn cài đặt được tạo lần đầu tiên, bản tóm tắt không được đặt theo giá trị tùy chọn được lưu trữ. Làm thế nào để giải quyết điều này ??
srv_sud

1
@srv_sud ý bạn là keytham số nên là gì? Đã không cố gắng này, bạn có một ví dụ trong câu trả lời của Gunnar dưới đây: onSharedPreferenceChanged(null, ""). Nó có thể không phù hợp với nhu cầu của bạn. Có lẽ bạn nên lặp lại các khóa tùy chọn cần cập nhật
Jose_GD

25

Giải pháp của antew hoạt động tốt, tại đây bạn có thể xem toàn bộ hoạt động ưu tiên cho Android v11 trở đi:

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

    }
}
}

13

Tất cả các câu trả lời khác đều đúng. Nhưng tôi thích sự thay thế này hơn bởi vì bạn ngay lập tức có thể hiện Preference đã gây ra thay đổi.

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

Đúng, mặc dù trong onSharedPreferenceChanged ()bạn có thể truy cập cá thể Sở thích dễ dàng với findPreference(key). Có thể SharedPreferencescách này được ưa thích hơn vì điều đăng ký / hủy đăng ký?
Jose_GD

2

Điều này làm việc cho tôi từ PreferenceFragment.onCreate ()

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

1

Đây là một cách để làm điều đó và tránh mọi khả năng rò rỉ bộ nhớ:

@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
    addPreferencesFromResource(R.xml.pref_movies);

    SharedPreferences sharedPreferences = getPreferenceScreen().getSharedPreferences(); 

    //starts live change listener
    sharedPreferences.registerOnSharedPreferenceChangeListener(this);
}

@Override
public void onDestroyView () {
    super.onDestroyView(); 
//Unregisters listener here
    PreferenceManager.getDefaultSharedPreferences(getContext())
            .unregisterOnSharedPreferenceChangeListener(this);
}

0

Một ví dụ hoàn chỉnh khác để bạn nhìn thấy toàn bộ bức tranh.

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

0

Gần đây tôi đã hoàn thành việc tổng hợp của riêng mình PreferenceScreenbằng cách sử dụng Preferences API, vì vậy tôi nghĩ tôi sẽ đóng góp Ví dụ đầy đủ của riêng mình .. Điều này bao gồm cập nhật SummaryGiá trị mới / đã thay đổi, cũng như Lắng nghe và phản ứng với các thay đổi.

Tái bút. Để trả lời câu hỏi cuối cùng của bạn: Để hiển thị một Giá trị mặc định Summarykhi tạo lần đầu PreferenceScreen(trước khi có bất kỳ thay đổi nào trong Giá trị), bạn có thể chỉ cần đặt thành android:summaryGiá trị mà bạn chọn, preferences.xmltrực tiếp từ trong tệp - và sau đó, khi ở đó là một thay đổi trong Giá trị, nó sẽ tự động cập nhật bằng cách sử dụng mã có trong Ví dụ của tôi bên dưới. Cá nhân , tôi sử dụng một lời giải thích ngắn gọn về , đặt trong tôiPreference như ban đầu của tôiSummarypreferences.xml , và sau đó một lần giá trị không có được thay đổi cho lần đầu tiên, nó sẽ chỉ đơn giản là hiển thị các giá trị hiện tại là Summarytừ đó trở đi ..

Nhưng dù sao , đây là đầy đủ của tôi Ví dụ:

SettingsFragment.java

public class SettingsFragment extends PreferenceFragment {

public static final String PREF_NOTIFICATION_MODE = "pref_notificationMode";
private SharedPreferences.OnSharedPreferenceChangeListener preferenceChangeListener;

@Override
public void onCreate (@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    addPreferencesFromResource(R.xml.preferences);

    final SharedPreferences getPrefs = PreferenceManager.getDefaultSharedPreferences(this.getActivity());

    preferenceChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
        @Override
        public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {

            if (key.equals(PREF_NOTIFICATION_MODE)) {
                Preference notifModePref = findPreference(key);
                notifModePref.setSummary(sharedPreferences.getString(key, ""));

                //  DO SOMETHING ELSE HERE WHEN (PREF_NOTIFICATION_MODE) IS CHANGED
            }
        }
    };
}

@Override
public void onResume() {
    super.onResume();

    getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(preferenceChangeListener);

    Preference notifModePref = findPreference(PREF_NOTIFICATION_MODE);
    notifModePref.setSummary(getPreferenceScreen().getSharedPreferences().getString(PREF_NOTIFICATION_MODE, ""));

}

@Override
public void onPause() {
    getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(preferenceChangeListener);

    super.onPause();
}
}


Tôi hi vọng cái này giúp được!
Mọi phản hồi tích cực đều được đánh giá rất cao, vì tôi còn khá mới đối với StackOverflow;)
Chúc bạn viết mã vui vẻ!


Trong onPause, nên super.onPause()gọi trước hay sau khi người nghe chưa đăng ký? Thậm chí có sự khác biệt? Tôi đã nhìn thấy cả hai trong nhiều ví dụ
Bryan W

@BryanWalsh Tôi cũng đã xem các ví dụ về cả hai cách .. Tôi nghĩ rằng hủy đăng ký trình xử lý trước khi gọi super.onPause();có lẽ là cách tốt nhất để thực hiện.
Studio2bDesigns

0

Bạn chỉ cần xóa lớp Ưu tiên được chỉ định trong onResume()phương thức của mình . Trong trường hợp của tôi, tôi đang sử dụng SwitchPreferencelớp, do đó mã sẽ giống như- SettingsActivity.class

public static class PrivacyPreferenceFragment extends PreferenceFragment
{
    public SwitchPreference switchPreference;

    @Override
    public void onResume() {
        super.onResume();
        switchPreference = (SwitchPreference) findPreference("privacy_notice_check");
    }

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

    }

Sau đó, trong hoạt động mà bạn muốn sử dụng PrefernceFragmentgiá trị, chỉ cần sử dụng SharedPreferenceđối tượng để gọi các giá trị và kích hoạt nó.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.