Bagaimana cara mendengarkan perubahan preferensi dalam PreferenceFragment?

92

Seperti dijelaskan di sini , saya membuat subclass PreferenceFragment dan menampilkannya di dalam Activity. Dokumen itu menjelaskan cara mendengarkan perubahan preferensi di sini , tetapi hanya jika Anda membuat subkelas PreferenceActivity. Karena saya tidak melakukan itu, bagaimana cara mendengarkan perubahan preferensi?

Saya telah mencoba menerapkan OnSharedPreferenceChangeListener di PreferenceFragment saya tetapi tampaknya tidak berhasil ( onSharedPreferenceChangedsepertinya tidak pernah dipanggil).

Ini adalah kode saya sejauh ini:

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

preferensi.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>

Juga, apakah PreferenceFragment bahkan merupakan tempat yang tepat untuk mendengarkan perubahan preferensi atau haruskah saya melakukannya dalam Aktivitas?

XåpplI'-I0llwlg'I -
sumber
1
Untuk pertanyaan terakhir Anda, semuanya tergantung pada kerangka Desain Anda. Untuk menggunakan pendekatan MVC atau MVP sulit dilakukan dengan Android, tetapi saya mencoba untuk membiarkan semua tindakan saya terjadi di aktivitas (Pengontrol) yang menghosting fragmen, dan fragmen hanya menjadi ui (View / Presenter)
John Shelley

Jawaban:

149

Saya yakin Anda hanya perlu mendaftar / membatalkan pendaftaran Listenerdi Anda PreferenceFragmentdan itu akan berhasil.

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

}

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

Bergantung pada apa yang ingin Anda lakukan, Anda mungkin tidak perlu menggunakan pendengar. Perubahan pada preferensi dilakukan SharedPreferencessecara otomatis.

antew
sumber
Ah, begitu. Itu bekerja. Tetapi haruskah saya mendapatkan SharedPreferences melalui getPreferenceManager(seperti yang Anda lakukan) atau getPreferenceScreen? Apa bedanya?
XåpplI'-I0llwlg'I -
Sejujurnya saya tidak yakin apa perbedaan sebenarnya, mungkin orang lain bisa mempertimbangkannya, ini bisa menjadi topik yang bagus untuk pertanyaan lain juga.
antew
2
Oke, berikut adalah jawaban untuk pertanyaan itu. Sepertinya sama sekali tidak ada perbedaan fungsional, tetapi getPreferenceManagerumumnya merupakan opsi yang disukai.
XåpplI'-I0llwlg'I -
ketika pengaturan-preferensi dibuat untuk pertama kalinya, ringkasan tidak diatur sesuai dengan nilai preferensi yang disimpan. Bagaimana mengatasi ini ??
srv_sud
1
@srv_sud maksud Anda apa keyparameternya? Belum mencoba ini, Anda memiliki contoh dalam jawaban Gunnar di bawah ini: onSharedPreferenceChanged(null, ""). Ini mungkin tidak sesuai dengan kebutuhan Anda. Mungkin Anda harus mengulang pada kunci preferensi Anda yang memerlukan pembaruan
Jose_GD
24

Solusi antew berfungsi dengan baik, di sini Anda dapat melihat aktivitas preferensi lengkap untuk Android v11 dan seterusnya:

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

    }
}
}
Gunnar Bernstein
sumber
Tidak ada ini di kelas statis!
josef
14

Semua jawaban lainnya benar. Tetapi saya lebih menyukai alternatif ini karena Anda segera memiliki instance Preferensi yang menyebabkan perubahan.

@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;
        }
    });
}
Zsolt Safrany
sumber
Benar, meskipun onSharedPreferenceChanged ()Anda dapat mengakses instance Preferensi dengan mudah findPreference(key). Mungkin SharedPreferencescara tersebut lebih disukai karena hal registering / unregistering?
Jose_GD
2

Ini berhasil untuk saya dari PreferenceFragment.onCreate ()

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

Berikut salah satu cara untuk melakukannya dan menghindari potensi kebocoran memori:

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

Contoh lengkap lainnya sehingga Anda melihat keseluruhan gambar.

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);
        }
    }
}
Pengembang
sumber
0

Saya baru saja selesai menyusun sendiri PreferenceScreenmenggunakan Preferences API, jadi saya pikir saya akan menyumbangkan Contoh lengkap saya sendiri .. Ini termasuk memperbarui Summaryke Nilai baru / diubah, serta Mendengarkan dan bereaksi terhadap perubahan.

PS. Untuk menjawab pertanyaan terakhir Anda: Untuk menampilkan nilai default untuk Summarysaat pembuatan awal PreferenceScreen(sebelum perubahan dalam nilai), Anda bisa hanya mengatur android:summaryuntuk Nilai yang Anda pilih, dari dalam preferences.xmlberkas langsung - dan kemudian, setelah ada adalah perubahan Nilai, itu akan diperbarui secara otomatis dengan menggunakan kode yang terdapat dalam Contoh saya di bawah ini. Secara pribadi , saya menggunakan penjelasan singkat tentang Preferenceinisial sayaSummary, Diatur dalam saya preferences.xml, dan kemudian setelah Nilai tidak bisa berubah untuk pertama kalinya, itu hanya akan menunjukkan Nilai saat ini sebagai Summarydari saat itu ..

Bagaimanapun , di sini adalah lengkap saya Contoh:

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


Saya harap ini membantu!
Setiap umpan balik positif sangat dihargai, karena saya cukup baru di StackOverflow;)
Selamat membuat kode!

Studio2bDesigns
sumber
Dalam onPause, haruskah super.onPause()dipanggil sebelum atau setelah pendengar tidak terdaftar? Apakah ada perbedaan? Saya telah melihat keduanya dalam banyak contoh
Bryan W
@BryanWalsh Saya juga telah melihat contoh dari kedua cara tersebut .. Menurut saya, membatalkan pendaftaran pendengar sebelum menelepon super.onPause();mungkin adalah cara terbaik untuk melakukannya.
Studio2bDesigns
0

Anda hanya perlu menghapus kelas Prefernce yang ditentukan dalam onResume()metode Anda . Dalam kasus saya, saya menggunakan SwitchPreferencekelas, oleh karena itu kodenya akan seperti- 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);

    }

Kemudian dalam aktivitas di mana Anda ingin menggunakan PrefernceFragmentnilai, cukup gunakan SharedPreferenceobjek untuk memanggil nilai dan memicunya.

Prajwal W.
sumber