118

In my preferences.xml I have a preference element like this:

<Preference android:title="About" />

I want to assign onClick event, so if user would click on it, I would be able to do open new Intent or browser. I tried to do it like I do with buttons, but this not seem to work.

Alex Lockwood
  • 83,063
  • 39
  • 206
  • 250
Badr Hari
  • 8,114
  • 18
  • 67
  • 100

5 Answers5

244

You need to set android:key for the item:

<Preference
    android:key="myKey"
    android:title="About" />

Then you can do the following in your code:

Preference myPref = (Preference) findPreference("myKey");
myPref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
    public boolean onPreferenceClick(Preference preference) {
        // open browser or intent here
        return true;
    }
});
Mahozad
  • 18,032
  • 13
  • 118
  • 133
Will Tate
  • 33,439
  • 9
  • 77
  • 71
  • 3
    Where shall i place this? in my SettingsActivity in `onPostCreate` wont work because it is too early(Nullpointer-Exception). Any help is appreciated – Wandang Feb 21 '13 at 18:28
  • 2
    `findPreference` is deprecated. – zackygaurav Sep 09 '15 at 14:13
  • 8
    `PreferenceActivity.findPreference` is deprecated. However the documentation says "[T]his functionality should now be found in the new PreferenceFragment class. If you are using PreferenceActivity in its old mode, the documentation there applies to the deprecated APIs here.", and `PreferenceFragment.findPreference` is not deprecated (as of API level 23, current at the time of writing). So, `findPreference` itself is not deprecated; it is the direct use of `PreferenceActivity` which is deprecated. Google want us to move to `PreferenceFragment` instead, not to abandon `findPreference`. – Mike Playle Mar 18 '16 at 22:11
  • In `onPreferenceClick`, return `true` if the click was handled else `false`. – Thupten Oct 10 '17 at 01:10
  • This works for me with PreferenceFragmentCompatDividers. – Joseph Lam Nov 23 '17 at 00:18
60

to launch a website:

<PreferenceScreen android:title="website">
    <intent
        android:action="android.intent.action.VIEW"
        android:data="http://www.example.com"
        />
</PreferenceScreen>

to launch a specific activity:

<PreferenceScreen android:title="something">
    <intent
        android:action="android.intent.action.MAIN"
        android:targetPackage="com.example.foo"
        android:targetClass="com.example.foo.SomeActivity"
        />
</PreferenceScreen>

you can also use "android:mimetype" to set the mimetype.

Andro Selva
  • 53,910
  • 52
  • 193
  • 240
jason gilbert
  • 609
  • 5
  • 5
  • 1
    I agree, this approach is better, especially because it doesn't use the now deprecated findPreference(String key) method and because it's just cleaner overall. – lyallcooper Jun 04 '13 at 00:00
  • Very nice, but is it also possible to create a IntentChooser this way? – Peterdk May 16 '14 at 18:28
  • @jasongilbert Is it possible to send a broadcast with ACTION rather than starting activity from preference click? – Sazzad Hissain Khan Nov 30 '15 at 18:20
  • 2
    @jasongilbert Should i specify any `intent-filter` in the manifest for the `SomeActivity`, cuz am getting `ActivityNotFoundException` :( – theapache64 Feb 08 '16 at 17:01
  • Can't run an Activity usign this way, always get the following error: `Unable to find explicit activity class {de.etc/datnet.app.activities.PinAendernActivity}; have you declared this activity in your AndroidManifest.xml?` – Konstantin Konopko Oct 08 '22 at 17:48
12

You need to use onPreferenceTreeClick event.

For example see http://www.javased.com/index.php?source_dir=platform_packages_apps_phone/src/com/android/phone/MobileNetworkSettings.java

 @Override 
    public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { 
        /** TODO: Refactor and get rid of the if's using subclasses */ 
        if (mGsmUmtsOptions != null && 
                mGsmUmtsOptions.preferenceTreeClick(preference) == true) { 
            return true; 
        } else if (mCdmaOptions != null && 
                   mCdmaOptions.preferenceTreeClick(preference) == true) { 
            if (Boolean.parseBoolean( 
                    SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE))) { 

                mClickedPreference = preference; 

                // In ECM mode launch ECM app dialog 
                startActivityForResult( 
                    new Intent(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null), 
                    REQUEST_CODE_EXIT_ECM); 
            } 
            return true; 
        } else if (preference == mButtonPreferredNetworkMode) { 
            //displays the value taken from the Settings.System 
            int settingsNetworkMode = android.provider.Settings.Secure.getInt(mPhone.getContext(). 
                    getContentResolver(), android.provider.Settings.Secure.PREFERRED_NETWORK_MODE, 
                    preferredNetworkMode); 
            mButtonPreferredNetworkMode.setValue(Integer.toString(settingsNetworkMode)); 
            return true; 
        } else if (preference == mButtonDataRoam) { 
            if (DBG) log("onPreferenceTreeClick: preference == mButtonDataRoam."); 

            //normally called on the toggle click 
            if (mButtonDataRoam.isChecked()) { 
                // First confirm with a warning dialog about charges 
                mOkClicked = false; 
                new AlertDialog.Builder(this).setMessage( 
                        getResources().getString(R.string.roaming_warning)) 
                        .setTitle(android.R.string.dialog_alert_title) 
                        .setIconAttribute(android.R.attr.alertDialogIcon) 
                        .setPositiveButton(android.R.string.yes, this) 
                        .setNegativeButton(android.R.string.no, this) 
                        .show() 
                        .setOnDismissListener(this); 
            } else { 
                mPhone.setDataRoamingEnabled(false); 
            } 
            return true; 
        } else if (preference == mButtonDataEnabled) { 
            if (DBG) log("onPreferenceTreeClick: preference == mButtonDataEnabled."); 
            ConnectivityManager cm = 
                    (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE); 

            cm.setMobileDataEnabled(mButtonDataEnabled.isChecked()); 
            return true; 
        } else if (preference == mLteDataServicePref) { 
            String tmpl = android.provider.Settings.Secure.getString(getContentResolver(), 
                        android.provider.Settings.Secure.SETUP_PREPAID_DATA_SERVICE_URL); 
            if (!TextUtils.isEmpty(tmpl)) { 
                TelephonyManager tm = (TelephonyManager) getSystemService( 
                        Context.TELEPHONY_SERVICE); 
                String imsi = tm.getSubscriberId(); 
                if (imsi == null) { 
                    imsi = ""; 
                } 
                final String url = TextUtils.isEmpty(tmpl) ? null 
                        : TextUtils.expandTemplate(tmpl, imsi).toString(); 
                Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); 
                startActivity(intent); 
            } else { 
                android.util.Log.e(LOG_TAG, "Missing SETUP_PREPAID_DATA_SERVICE_URL"); 
            } 
            return true; 
        } else { 
            // if the button is anything but the simple toggle preference, 
            // we'll need to disable all preferences to reject all click 
            // events until the sub-activity's UI comes up. 
            preferenceScreen.setEnabled(false); 
            // Let the intents be launched by the Preference manager 
            return false; 
        } 
    } 
Pentium10
  • 204,586
  • 122
  • 423
  • 502
9

2018+ UPDATE Today, the findPreference method is depricated. So, to achieve this, just override the onPreferenceTreeClick method in your Preference fragment. For example:

public class MySettingsFragment extends PreferenceFragment {

    @Override
    public boolean onPreferenceTreeClick (PreferenceScreen preferenceScreen,
                                          Preference preference)
    {
        String key = preference.getKey();
        if(key.equals("someKey")){
            // do your work
            return true;
        }
        return false;
    }
}

Furthermore, if you need to handle a click inside particular preference element (e.g. ListPreference), you should register the setOnPreferenceChangeListener inside the onCreate method of the MySettingsFragment:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // Load the preferences from an XML resource
    addPreferencesFromResource(R.xml.preferences);

    // register listener
    final Preference prefList = findPreference("key");
    prefList.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
        public boolean onPreferenceChange(Preference preference, Object value) {
            System.out.println("Selected: " + value);
            return true;
        }
    });
}
akelec
  • 3,797
  • 3
  • 41
  • 39
4

A followup on @jason gilbert's answer

I'm on targetSdkVersion 25, and his answer didn't work, I had to wrap the intent tag with a Preference tag. Example:

<PreferenceScreen android:title="something">
    <Preference title="Title">
       <intent
       android:action="android.intent.action.MAIN"
       android:targetPackage="com.example.foo"
       android:targetClass="com.example.foo.SomeActivity"
       />
    </Preference>
</PreferenceScreen>
Malek Hijazi
  • 4,112
  • 1
  • 26
  • 31