Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Switch Preference - Handling both onPreferenceChange and onPreferenceClick

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).
like image 827
forevercrashed Avatar asked Jan 18 '13 20:01

forevercrashed


3 Answers

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

like image 166
Yokich Avatar answered Nov 20 '22 04:11

Yokich


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.

like image 37
forevercrashed Avatar answered Nov 20 '22 03:11

forevercrashed


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 .

like image 1
android developer Avatar answered Nov 20 '22 04:11

android developer