In my application, I used PreferenceFragment to create a nice application on Tablets and smartphones.
So, in my main activity, I use:
#Override
public void onBuildHeaders(List<Header> target) {
loadHeadersFromResource(R.xml.preference_headers, target);
}
My xml file looks like this:
<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
<header android:fragment="tof.cv.mpp.view.StockPreferenceFragment"
android:title="#string/btn_home_settings" android:summary="">
<extra android:name="resource" android:value="activity_preferences" />
</header>
<header android:fragment="tof.cv.mpp.view.StockPreferenceFragment"
android:title="#string/btn_home_planner" android:summary="">
<extra android:name="resource" android:value="activity_planner_preferences" />
</header>
<header android:fragment="tof.cv.mpp.view.StockPreferenceFragment"
android:title="#string/btn_home_twitter" android:summary="">
<extra android:name="resource" android:value="activity_twitter_preferences" />
</header>
</preference-headers>
The problem is now shen I want to use a OnSharedPreferenceChangeListener in order to update the summary of some of my preferences.
I use:
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
String key) {
Log.i("", "PREFChanged "+getActivity());
if (key.contentEquals("prefPseudo")) {
Log.i("", "PseudoChanged");
Preference pref = findPreference("prefPseudo");
pref.setSummary(((EditTextPreference) pref).getText());
}
if (key.contentEquals(getString(R.string.key_activity))) {
Log.i("", "FirstChanged");
Preference pref = findPreference(getString(R.string.key_activity));
pref.setSummary(((ListPreference) pref).getEntry());
}
if (key.contentEquals(getString(R.string.key_planner_da))) {
Preference pref = findPreference(getString(R.string.key_planner_da));
Log.i("", "PlannerChanged"+pref);
pref.setSummary(((ListPreference) pref).getEntry());
}
}
The big problem I am facing is that getActivity() is null when I have multiple categories in my xml header!
The first one I open is always correct and non null, but when I press back, I come back to the Preference list automatically generated, I click on a second one and there, activity is null!
OUCH! That was an hard one.
I succeed to fix that.
In fact, the Listener was always belonging to the first Fragment.
So when you change the Preference from an other category, the listener of the first Fragment is called when you change a Preference of the second Fragment!
So the Activity is null.
The solution is to remove the listener when you leave a Fragment, so the correct Listener can do his job:
#Override
public void onPause() {
super.onPause();
Log.i("", "PAUSE");
preferences.unregisterOnSharedPreferenceChangeListener(this);
}
If your Listener is in a Fragment MyFragment use the following code to get the activity instance. We need to use .this.getActivity()
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
String key) {
Log.i("", "PREFChanged "+ MyFragment.this.getActivity());
.....
}
Related
I have a Preference Screen in my android app which contains a call to the system settings via an inner Preference and some Switches options as below:
<PreferenceCategory
android:title="#string/preferences_activity_supervisor_settings_title">
<Preference
android:key="#+id/instrument_preference"
android:title="#string/instrument_settings"
android:summary="#string/instrument_settings">
</Preference>
<com.CustomSwitchPreference
android:key="#string/prefs_super_autosend_key"
android:title="#string/prefs_super_autosend_title"
android:summary="#string/prefs_super_autosend_summary"
android:defaultValue="false"/>
...
...
My class extends PreferenceFragment and I added the resources using addPreferencesFromResource(). It works without problems.
However, the method onPreferenceTreeClick(), when I click in the Fragment (keyID = instrument_preference) the preferences always brings me key as NULL. All other options in the preference fragment I can read the key.
The question is why and how I detect the user clicked in the first element of my preference list ?
First remove + from #+id, because every time it create a new key due to #+id, thats why you get null.
And to detect SharedPreference is changed use this simple code:
public class MyPreferences extends PreferenceActivity implements SharedPreferences.OnSharedPreferenceChangeListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.fw_preferences); //deprecated
PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(this);
}
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
// handle the preference change here
}
}
Try removing #+id before the preference key name
It should be like
<Preference
android:key="instrument_preference"
android:title="#string/instrument_settings"
android:summary="#string/instrument_settings">
</Preference>
I looked around but couldn't find a single tutorial that tells me how to use the switchpreference in my preference activity and in my main activity. I want to know how to implement it in my application using sharedpreference. Sample code would be appreciated. Thanks in advance
preference.xml:
<SwitchPreference
android:key="test"
android:title="Test" />
PreferenceActivity:
public class TestPrefActivity extends PreferenceActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.sample);
}
}
Register the shared preference object for the changes
SharedPreferences sharedPrefs = PreferenceManager
.getDefaultSharedPreferences(MainActivity.this);
sharedPrefs.registerOnSharedPreferenceChangeListener(new OnSharedPreferenceChangeListener() {
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
boolean test = sharedPreferences.getBoolean("test", false);
Log.e(TAG, "Value:" + test);
}
});
I'm having a comparable issue with another establishment of mine. I fixed it by using <SwitchPreference android:key="test" android:title="Test" />
As I was following an old tutorial (Créez des applications pour Android -> openclassroom) I got stuck on this deprecated method addPreferencesFromResource(int id) from the PreferenceActivity class.
So my question is :
What is the new way of creating Preferences in Android ?
I found this post (What to use instead of “addPreferencesFromResource” in a PreferenceActivity?) that help me understand that you have to go through a PreferenceFragment in order to do it.
In the following explanation I use your.package. just to show that you have to put the package name. Everybody has its own package so please replace it with your package.
lets begin :
1. Preference Fragment
Create your PreferenceFragment class
MyPreferenceFragment
public class MyPreferenceFragment extends PreferenceFragment
{
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.fragment_preference);
}
}
Then the associated xml resource
fragment_preference.xml (in the folder res/xml of your project)
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:title="FOO">
<CheckBoxPreference
android:key="checkBoxPref"
android:title="check it out"
android:summary="click this little box"/>
</PreferenceCategory>
</PreferenceScreen>
That's all for the Fragment part.
2. Preference Activity
Create the PreferenceActivity class
MyPreferenceActivity
public class MyPreferenceActivity extends PreferenceActivity
{
#Override
public void onBuildHeaders(List<Header> target)
{
loadHeadersFromResource(R.xml.headers_preference, target);
}
#Override
protected boolean isValidFragment(String fragmentName)
{
return MyPreferenceFragment.class.getName().equals(fragmentName);
}
}
Do not forget to override isValidFragment(String fragmentName) method as you will get punched in the face by your application ! ;) More seriously I have no idea why you need to do this but it is needed. If someone has an explanation about this I'd gladly read it :)
EDIT :
Thanks to kirtan403 I now know why it is needed : it has to be set because of an (android framework fragment injection).
As you can see in the onBuildHeaders(List<Header> target) we load another xml file that contain the headers of the preference. In short, headers are the left part of the preference and the fragment are the right part (for tablet). For a phone you will first have the headers and when you click on an item the corresponding fragment will be put on top of the headers list.
Read this article (Multi-pane development in Android with Fragments - Tutorial) the images explain themselves.
Then the associated xml resource
headers_preference.xml (in the folder res/xml of your project)
<?xml version="1.0" encoding="utf-8"?>
<preference-headers
xmlns:android="http://schemas.android.com/apk/res/android">
<header
android:fragment="your.package.MyPreferenceFragment"
android:title="Goto: Preference fragment"
android:summary="An example of some preferences." />
</preference-headers>
As you may have noticed in the header section you have :
android:fragment="your.package.MyPreferenceFragment"
This will act as a Link to the fragment you want to show. On Tablet it will load on the right part and on the phone it will load on top of the current view.
3. Android Manifest
Now what you should do is to add your Activity to the AndroidManifest.xml file.
Inside the application section add these lines :
<activity
android:name="your.package.MyPreferenceActivity"
android:label="Preferences">
</activity>
You will probably tell me :
"Oh darling you forgot to put android:launchMode="singleTask" in your actvity"
But DO NOT PUT THIS as you will never load your fragment on phone. This error was solved by a great man ! This is the link to his blog (Android header preferences on small screen/phone).
4. Start the Preferences from Menu
Finally you need to add the ability to show this Preference !! To do so you will need 3 things :
The Menu
menu.xml (in the folder res/menu of your project)
<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="#+id/preferences"
android:title="Preferences" />
</menu>
Loading this Menu in your Main activity (not the PreferenceActivity) under the method onCreateOptionsMenu(Menu menu)
#Override
public boolean onCreateOptionsMenu(Menu menu)
{
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu, menu);
return true;
}
Starting the MyPreferenceActivity Activity when you click on that button.
For that you will need to override the onOptionsItemSelected(MenuItem item) method in your Main activity.
#Override
public boolean onOptionsItemSelected(MenuItem item)
{
switch(item.getItemId())
{
case R.id.preferences:
{
Intent intent = new Intent();
intent.setClassName(this, "your.package.MyPreferenceActivity");
startActivity(intent);
return true;
}
}
return super.onOptionsItemSelected(item);
}
Et voila les amis !
I haven't tested this code. I took it and modified it from my own code so I may have not well copy pasted things. If you encounter errors tell me, I'll try to figure out the problem and fix this.
I hope this post will help some people out there :D
Cheers !
I liked the solution from this post: http://alvinalexander.com/android/android-tutorial-preferencescreen-preferenceactivity-preferencefragment
.. because it seems the most compact for someone that just needs something very basic up and running quickly. It has only one .java file and two small xml files.
Activity Config REMINDERS
After adding the 3 files to your project, Don't forget to
A) Add the Prefs Activity to Manifest file
B) Add some way to launch the Prefs Activity .. e.g., a Button or Menu item
Add the following files to your project. Use the order they are listed in to avoid compile errors.
Add /res/values/array.xml
<resources>
<string-array name="listArray">
<item>Ace</item>
<item>Club</item>
</string-array>
<string-array name="listValues">
<item>Ace</item>
<item>Club</item>
</string-array>
</resources>
Add /res/xml/preferences.xml
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<EditTextPreference android:title="Your Name"
android:key="username"
android:summary="Please provide your username"></EditTextPreference>
<CheckBoxPreference android:title="Application Updates"
android:defaultValue="false"
android:summary="This option if selected will allow the application to check for latest versions."
android:key="applicationUpdates" />
<ListPreference android:title="Download Details"
android:summary="Select the kind of data that you would like to download"
android:key="downloadType"
android:defaultValue="Ace"
android:entries="#array/listArray"
android:entryValues="#array/listValues" />
</PreferenceScreen>
Add the Activity code
public class AppPreferenceActivity extends PreferenceActivity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
getFragmentManager().beginTransaction().replace(android.R.id.content, new MyPreferenceFragment()).commit();
checkValues();
}
public static class MyPreferenceFragment extends PreferenceFragment
{
#Override
public void onCreate(final Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
}
}
private void checkValues()
{
SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
String strUserName = sharedPrefs.getString("username", "NA");
boolean bAppUpdates = sharedPrefs.getBoolean("applicationUpdates",false);
String downloadType = sharedPrefs.getString("downloadType","1");
String msg = "Cur Values: ";
msg += "\n userName = " + strUserName;
msg += "\n bAppUpdates = " + bAppUpdates;
msg += "\n downloadType = " + downloadType;
Toaster.shortDebug(msg);
}
}
I'm using the code below to print text to logcat, but the setOnPreferenceClickListener function doesn't catch the event.
I'm using Android API Level 8 to test the code.
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
<CheckBoxPreference
android:defaultValue="true"
android:key="settings_use_cache"
android:summary="Use cache"
android:title="Use Cache" />
<Preference
android:defaultValue="true"
android:key="settings_delete_cache"
android:summary="Delete all cache data"
android:title="Clear Cache" />
</PreferenceScreen>
Here is the code
public static class CachePreferenceFragment extends
PreferenceActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
Log.w("DBG", "Oncreate started");
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.pref_cache);
Preference settings_delete_cache=findPreference("settings_delete_cache");
settings_delete_cache.setOnPreferenceClickListener(new OnPreferenceClickListener() {
#Override
public boolean onPreferenceClick(Preference preference) {
Log.w("Prefence", "Deleting Cache");
return false;
}
});
}
}
What can I do to ensure the listener catches the event?
EDIT:
After talking with this dev outside of the thread, it was identified that the problem was that he was putting his listener inside of the fragment. So this code actually works, but it will only run if you are using a two-panel layout. In fact this is stated right above the method:
/**
* This fragment shows notification preferences only. It is used when the
* activity is showing a two-pane settings UI.
*/
public static class CachePreferenceFragment extends
PreferenceActivity {
The solution is to also set the onPreferenceClick listener in setupSimplePreferencesScreen()!
I am building a Preference Activity where most of the preferences in the list will be executing code and not modifying a SharedPreference directly. My preferences.xml file looks like this.
<PreferenceCategory
android:title="Connection" >
<Preference
android:id="#+id/settings_connectToNewComputer"
android:key="connectToNewComputer"
android:summary="Currently connected to:"
android:title="Connect to new computer" />
<Preference
android:id="#+id/removeDevice"
android:key="removeDevice"
android:summary="Remove this device from the computer's whitelist"
android:title="Remove this device from computer" />
</PreferenceCategory>
<PreferenceCategory
android:title="About" >
<Preference
android:id="#+id/settings_About"
android:key="about"
android:summary="About me and my thanks to those who made this app great"
android:title="About Hue Pro" />
<Preference
android:id="#+id/contact"
android:key="contact"
android:summary="Contact me with comments, bugs, and suggestions for updates"
android:title="Contact me" />
</PreferenceCategory>
My goal is to have a block of code executed when a one of these preferences are clicked. Similar to the "Clear search history" in the Google Play settings preference menu. (http://i.imgur.com/qnHbJX9.png)
Does anyone know how to make this possible?
I have to add that I have tried using findPreference("KeyNameHere") but it always returns null.
Thank you!
Edit:
I added in this code and implemented OnPreferenceClickListener:
#Override
public boolean onPreferenceClick(Preference preference) {
return false;
}
But this method never gets called. Is there another way to do this?
Edit 2:
I have found that if I take out the PreferenceCategory tags so I am left with this:
<Preference
android:id="#+id/settings_connectToNewComputer"
android:key="connectToNewComputer"
android:summary="Currently connected to:"
android:title="Connect to new computer" />
<Preference
android:id="#+id/removeDevice"
android:key="removeDevice"
android:summary="Remove this device from the computer's whitelist"
android:title="Remove this device from computer" />
<Preference
android:id="#+id/settings_About"
android:key="about"
android:summary="About me and my thanks to those who made this app great"
android:title="About Hue Pro" />
<Preference
android:id="#+id/contact"
android:key="contact"
android:summary="Contact me with comments, bugs, and suggestions for updates"
android:title="Contact me" />
and call this:
getPreferenceScreen().setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
#Override
public boolean onPreferenceClick(Preference preference) {
return false;
}
});
then I actually get a response from the click event. The only down side is I have to remove the preference grouping. Anyone know why this is and any way to fix it?
Implement OnPreferenceClickListener and in the onPreferenceClick
#Override
public boolean onPreferenceClick (Preference preference)
{
String key = preference.getKey();
// do what ever you want with this key
}
Maybe this could not be useful for OP, but could be useful for someone else.
I'd like to write a sort of summary; in general, you can follow mainly three ways:
1) you can find your preference somewhere in your code with
Preference examplePreference = findPreference(KEY_EXAMPLE_PREFERENCE);
and then you can add a click listener and override its on click method with
examplePreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
#Override
public boolean onPreferenceClick(Preference preference) {
// handle click here
}
});
This has to be done for every preference whose clicks you want to listen to
2) You can implement Preference.OnPreferenceClickListener interface in your settings fragment/activity and override onPreferenceClick just once, by using a switch construct or a if-else if-else if-... construct and merging all the single handlings; it should be something like:
#Override
public boolean onPreferenceClick(Preference preference) {
switch (preference.getKey()) {
case KEY_EXAMPLE_PREFERENCE: {
// handle click here
}
break;
case ...
}
}
Then, you still have to find each preference but you can simply call on each of them
setOnPreferenceClickListener(this);
(I think the OP's implementation didn't work (his method wasn't called) because of this last part)
we pass "this" as parameter because we implemented the click listener interface
3) (which I think is the easiest) you can override
onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference)
in your preference fragment/activity without implementing any other interface and there you can copy the switch of the if-else if-... construct of option 2); the main advantage in that you shouldn't need to find each preference and to call on them setOnPreferenceClickListener.
Hope this will be useful for someone!
Just override:
#Override
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
String key = preference.getKey();
...
return super.onPreferenceTreeClick(preferenceScreen, preference);
}
You could also find the preference and set the click listener.
Preference connectToNewComputer= findPreference("connectToNewComputer");
connectToNewComputer.setOnPreferenceClickListener(this);
For Androidx in Feb 2020
Others answers were not worked in Androidx for me. I implemented Settings from Android Developers guides
See below guide for implementing click listener
1) Implement PreferenceManager.OnPreferenceTreeClickListener in your settings fragment, like below code
import androidx.preference.PreferenceManager;
class SettingsFragment extends PreferenceFragmentCompat implements PreferenceManager.OnPreferenceTreeClickListener {
2) Override onPreferenceTreeClick inside your SettingsFragment
#Override
public boolean onPreferenceTreeClick(Preference preference) {
String key = preference.getKey();
switch (key) {
case "key1":
return true;
case "key2":
return true;
//codes
}
}
I came up with my own (what I believe is really messed up) solution; but it works.
for(int x = 0; x < getPreferenceScreen().getPreferenceCount(); x++){
PreferenceCategory lol = (PreferenceCategory) getPreferenceScreen().getPreference(x);
for(int y = 0; y < lol.getPreferenceCount(); y++){
Preference pref = lol.getPreference(y);
pref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener(){
#Override
public boolean onPreferenceClick(Preference preference) {
return false;
}
});
}
}
So what I have learned is there is a hierarchical system that works like: PreferenceScreen has children PreferenceCategory has children Preference, as you can see in the XML file. My problem was I could not set the preferences' onClickListeners directly from the PreferenceScreen. So I made two for loops that will get down to each Preference and set an OnPreferenceClickListener for each and every one of them. Messy, but works finally.
Your Preference object wont get null if you will find followings
(copypasting from the project):
public class ImePreferences extends PreferenceActivity {
.....
#Override
protected boolean isValidFragment(String fragmentName) {
return Settings.class.getName().equals(fragmentName);
}
.....
public static class Settings extends InputMethodSettingsFragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setInputMethodSettingsCategoryTitle(R.string.language_selection_title);
setSubtypeEnablerTitle(R.string.select_language);
// Load the preferences from an XML resource
addPreferencesFromResource(R.xml.ime_preferences);
Preference pLcl = getPreferenceScreen().findPreference(getResources().getString(
R.string.dictionary_button));
pLcl.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
#Override
public boolean onPreferenceClick(Preference preference) {
// handle click here
l.a("this is the click");
return true;
}
});
if(pLcl != null)
l.a(6576);
}
}
.....
}
For Kotlin
your xml should look like this
<Preference
app:title="Contact me"
app:key="contact"/>
do not forget the key.
Then in your Settings Activity find this class
class SettingsFragment : PreferenceFragmentCompat()
and add this code
override fun onPreferenceTreeClick(preference: Preference): Boolean {
val key = preference.key
return super.onPreferenceTreeClick(preference)
}
When you are done your code should look like this
class SettingsFragment : PreferenceFragmentCompat() {
override fun onPreferenceTreeClick(preference: Preference): Boolean {
val key = preference.key
return super.onPreferenceTreeClick(preference)
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.root_preferences, rootKey)
}
}