Switch Preference - Handling both onPreferenceChange and onPreferenceClick

14,054

Solution 1

After searching for hours more I came across a couple posts that will be helpful to others in this situation.

This one was the solution I opted for given my problem: How do I create one Preference with an EditTextPreference and a Togglebutton?

It's a very detailed answer and is very helpful in understanding preferences.

The other post I came across was this one: http://xgouchet.fr/android/index.php?article4/master-on-off-preferences-with-ice-cream-sandwich

It will give you pretty much the same look and feel as the one above, but requires more work and because of my requirements wouldn't work for me.

Solution 2

I have been working with the same issue for ages now and you can go about it two ways.

Implementing a switchpreference with custom actions for every event:

  • forevercrashed made some good points. I tried follow them, but for me they didn't do it. I bet they work, but I needed more functionallity in an easier way. Xgouchet (second Link) uses Headers and custom xml layouts which uses custom placements and measurements (height, witdth, padding etc.). I needed a solution without altering Googles built in auto-generated layout.

  • The super easy and powerful way: implement your own SwitchPreference! Just make a class extend SwitchPreference and then implement/override like so:

    public class AutoUploadSwitchPreference extends SwitchPreference {
    public AutoUploadSwitchPreference(Context context) {
        super(context);
    }
    public AutoUploadSwitchPreference(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    public AutoUploadSwitchPreference(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }
    
    @Override
    protected void onClick() {
        //super.onClick(); THIS IS THE IMPORTANT PART!
    }
    

By overriding onClick() and commenting out / deleting super.onClick() makes the SwitchPreference NOT call callChangeListener(Object newValue). Now you can click the preference and nothing happens, not until you want it to. (One bug that would occur otherwise was having multiple calls to onPreferenceChange in the fragment)


Now! To make things happen: Here is the structure I have used.

  • Create a SettingsActivity
    • In it make sure you fetch preferences, resources etc.
  • in onCreate() in your Activity - launch a PreferenceFragment
    • This needs to be a custom class extending PreferenceFragment, see how here : PreferenceFragment
  • In your custom Fragment, get hold of your custom-preference. You can use findPreference("custom_switch_key").

    • add an OnPreferenceChangeListener on the preference
    • I personally make my fragment implement the listener and pass this as argument.
    • The return statement is important. This is what makes the actual change in the switch. If you return true the switch will change into the newValue. If you return false, it will not. If you use return false; you can change the value with setChecked(true|false) on the switchpreference.
  • when you implement onPreferenceChange(Preference preference, Object newValue) you can add whatever functionality you want from pressing the switch-slider only

  • the functionality from clicking the preference can be done in three ways:
    • Implement the onClick() further in the custom SwitchPreference class
    • Implement the method onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) in the fragment
    • Implement an onPreferenceClickListener like you did for the ChangeListener.

Sorry if this is a long post. It is my first and I have been through so many stackoverflow-pages about this and no one was accurate, just wanted to get it right ;)

Solution 3

i think you are asking about a feature that doesn't exist.

however , since the preference activity uses a listView , you can use some tricks to customize it and handle it however you wish .

here's a post i've made about customizing it , based on this website . what i've asked there is how to add a listView , but i didn't know that a preference activity actually uses a listview .

Share:
14,054
forevercrashed
Author by

forevercrashed

Updated on June 09, 2022

Comments

  • forevercrashed
    forevercrashed about 2 years

    I've been trying to get a switch preference working in Android whereby I can intercept and handle differently, in certain cases, when they switch it on/off vs when they click the whole preference.

    This is what I'm trying to accomplish: User goes into preferences tags are off and no tags are stored (ie: tag preference is empty) User turns on preference for tags, and since no tags are stored currently it launches a tag search activity for user to find the tag. - works fine.

    If tag already exists, and they change the state ONLY then update the value as normal. - works fine

    Here's my issue: If they click the preference though and they already have a tag saved, don't change the state (regardless if it's enabled or disabled), launch the tag search activity. - this DOESN'T work.

    What I've found so far is that in the final scenario above, I get a call to onPreferenceChanged, followed by a call to onPreferenceClicked, followed by a subsequent call to onPreferenceChanged. This seems to be my problem. The first call to onPreferenceChanged causes my listener on my SharedPreferences to be called telling it that it's now enabled.

    If I didn't receive the first call to onPreferenceChanged then I wouldn't have an issue.

    Here is the relevant parts where I'm setting the listeners

    SwitchPreference tagPref = (SwitchPreference) findPreference(PreferencesConstants.PREFERENCE_TAG_ENABLED);
        tagPref.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
    
            @Override
            public boolean onPreferenceChange(Preference preference, Object newValue) {
                Log.e("BLAH", "onPrefChanged....is it handled by OnClick?" + Boolean.toString(handledByClick));
    
    
                SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity().getApplicationContext());
    
                boolean enabled = prefs.getBoolean(PreferencesConstants.PREFERENCE_TAG_ENABLED, false);
                Log.e("BLAH", "value stored in prefs? " + Boolean.toString(enabled));
                if (newValue instanceof Boolean) {
                    enabled = (Boolean) newValue;
                }
    
                Log.e("BLAH", "New value? " + Boolean.toString(enabled));
    
                if (!handledByClick) {
                    if (enabled && (currentTag == null || currentTag.isEmpty())) {
                        Log.e("BLAH", "Enabled and CurrentTag empty!");
                        Intent intent = new Intent(getActivity(), TagSearchActivity.class);
                        startActivityForResult(intent, 0);
    
                        return false; // always return false, we'll handle
                                        // updating
                                        // this value manually.
                    } else {
                        return true;
                    }
                }
                Log.e("BLAH", "returning false (AS IN WE HANDLED IT).");
                return false;
            }
        });
    
        tagPref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
    
            @Override
            public boolean onPreferenceClick(Preference preference) {
    
                handledByClick = true;
                Log.e("BLAH", "onprefClick");
    
                Intent intent = new Intent(getActivity(), TagSearchActivity.class);
                startActivityForResult(intent, 0);
    
                return true;
            }
        });
    

    Here are the relevant log lines after running it with a saved tag, and clicking the preference.

    01-18 15:55:05.593: E/BLAH(13261): onPrefChanged....is it handled by OnClick?false
    01-18 15:55:05.593: E/BLAH(13261): value stored in prefs? true
    01-18 15:55:05.593: E/BLAH(13261): New value? false
    01-18 15:55:05.613: E/DifferentClass(13261): On Shared Preferences Changed - tagEnabled
    01-18 15:55:05.652: E/DifferentClass(13261): disabled TAG in cancelAlarmService
    01-18 15:55:05.662: E/AnotherClass(13261): Updating Feed List.  Old Size: 33, New Size: 14
    01-18 15:55:05.682: E/BLAH(13261): onprefClick
    01-18 15:55:05.812: E/BLAH(13261): onPrefChanged....is it handled by OnClick?true
    01-18 15:55:05.812: E/BLAH(13261): value stored in prefs? false
    01-18 15:55:05.822: E/BLAH(13261): New value? false
    01-18 15:55:05.822: E/BLAH(13261): returning false (AS IN WE HANDLED IT).
    
  • forevercrashed
    forevercrashed over 11 years
    Thanks for the links...after searching for hours more I managed to find a duplicate of this question posted to SO. I'm going to put that in as the answer so if anyone else comes across this post, they'll find the proper solution. (surprisingly easy too!)
  • android developer
    android developer over 11 years
    very nice post . thank you for showing it . you can still customize the preferences anyway you wish just like i did . do note that last time i've checked , custom preferences have some issues in case they exist within an android library . not sure what and why . maybe they are already fixed on the latest ADT&SDK .
  • forevercrashed
    forevercrashed over 11 years
    Also, I just realized that to get the "true" look and feel of the switch preference you'll need to combine a bit from the 2 links I posted above. While the first link is what I implemented, I realized after the fact the layout file only had a title, and no summary. I need both the title and summary. The second link I posted has a very nice layout file which does exactly that. I still had to change a couple paddings and the text size to get it to look the same as the other preferences in my settings screen...just in case someone needs this info.
  • lostintranslation
    lostintranslation over 10 years
    Your code for your preference activity would have been very helpful as well. Overall good solution to implement any kind of preference with checkable widget (switch, checkbox, etc).
  • Anonsage
    Anonsage about 9 years
    @Yokich, thank you. This works great on my Moto X (API 4.4.4), but unfortunately, doesn't work on Nexus 4 (API 5.0). According to the Android source code (the Listener class in SwitchPreference), it should be working, but the N4 still only registers the onPreferenceClick when the switch is pressed. Any ideas?
  • Yokich
    Yokich almost 9 years
    I don't really have any answers for you there, sorry. Hope you'll find what you're looking for