22

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. (https://i.stack.imgur.com/HgEE4.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?

BarryBostwick
  • 919
  • 2
  • 12
  • 21

8 Answers8

17

Implement OnPreferenceClickListener and in the onPreferenceClick

@Override
public boolean onPreferenceClick (Preference preference)
{
    String key = preference.getKey();
    // do what ever you want with this key
}
Hoan Nguyen
  • 18,033
  • 3
  • 50
  • 54
  • Thank you for the quick response! Please refer to my first edit in the original post. – BarryBostwick Mar 21 '13 at 00:34
  • 1
    Did you implement OnPreferenceClickListener then? – Hoan Nguyen Mar 21 '13 at 00:41
  • I have tried to implement that, but the problem I ran into was I could never load any of the Preference objects from the code. As I wrote in my original post, findPreference("KeyNameHere") always returns null so I couldn't ever find a way to implement OnPreferenceClickListener. – BarryBostwick Mar 21 '13 at 01:06
  • When you implement OnPreferenceClickListener, the method onPreferenceClick(Preference preference) will give the preference clicked. So all you have to do is to call preference.getKey() to get the String name of this preference. – Hoan Nguyen Mar 21 '13 at 01:17
  • I realize this. But the setOnPreferenceClickListener needs to be applied to a Preference object. I have no way of getting that object in code because my implementations of findPreference("KeyNameHere") to get that object have always returned null. – BarryBostwick Mar 21 '13 at 01:24
  • I declare my class as public MySettings extends PreferenceActivity implements OnPreferenceClickListener and I have no problem – Hoan Nguyen Mar 21 '13 at 01:34
  • 7
    I see what you mean now, based on your edit. Okay, so I tried this. I added implements onPreferenceClickListener onto my class and implemented and overrode the method onPreferenceClick(Preference arg0). But this method never gets called when I click on any of the preferences. And I have no idea why... – BarryBostwick Mar 21 '13 at 01:56
  • Worked for me. I have used setOnPreferenceChangeListener() :) – Naresh Sharma Nov 24 '14 at 07:44
17

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!

bd95
  • 173
  • 1
  • 8
15

Just override:

@Override
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {

    String key = preference.getKey();
    ...

    return super.onPreferenceTreeClick(preferenceScreen, preference);
}
marmor
  • 27,641
  • 11
  • 107
  • 150
10

You could also find the preference and set the click listener.

Preference connectToNewComputer= findPreference("connectToNewComputer");
connectToNewComputer.setOnPreferenceClickListener(this);
Apirak Lunla
  • 647
  • 7
  • 5
9

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    
        }
}
majurageerthan
  • 2,169
  • 3
  • 17
  • 30
  • 2
    Recently I noticed that my preferences aren't working anymore. It took a lot of time to find out -- onPreferenceClick is not called anymore and only overriding onPreferenceTreeClick helped. – Grrruk Mar 24 '20 at 12:58
  • @Grrruk Strange thing is (at least for me), onPreferenceClick works in debug builds. When I build for production, it doesn't. I think something is broken with R8/ProGuard. onPreferenceTreeClick works as expected tho. – EvanBlack Mar 01 '22 at 04:32
8

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.

BarryBostwick
  • 919
  • 2
  • 12
  • 21
  • 2
    +1 Your solution hinted me towards what I chose in my app. I make a `String` array of keys and then get `Preference` for each one and set the `onPreferenceClick`. It's a code of few lines. Thanks. – Sufian Aug 12 '14 at 06:15
  • though this may seem right, but the method that was use was deprecated. If you have an updated answer please edit your post. Note getPreferenceScreen() is deprecated. Thanks – ralphgabb May 04 '15 at 00:43
0

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

.....

}
CodeToLife
  • 3,672
  • 2
  • 41
  • 29
0

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