47

I've an android application with preferences declared in XML, loaded with addPreferencesFromResource. The user can open preferences, click on each item and edit them, all works.

One preference I have is:

        <ListPreference android:key="abc"
            android:title="@string/abc"
            android:summary="@string/cde"
            android:persistent="true"/>

How can I show the preference dialog to a user automatically (without the need for the user to go to the preference screen and click on it?).

I tried ( (android.preference.DialogPreference) prefMgr.findPreference( "abc" )).showDialog(null), but is says it is a protected method...? Called it from my main activity (which is a PreferenceActivity), that's why it obviously cannot work. But how else?

EDIT

I just found two threads (1, and 2) with the idea to use findViewById to access the preference, but with no success. It always returns null (does for me, too).

It looks like there is really no possibility to do this from code.

Cœur
  • 37,241
  • 25
  • 195
  • 267
Markus
  • 5,667
  • 4
  • 48
  • 64
  • You want to show only one field from all the preference screen? Or simply open the whole preference dialog? – Vincent Mimoun-Prat Jan 26 '11 at 14:59
  • I want to show the dialog of my `ListPreference` (or any `DialogPreference`), just as if I would click directly on it in the preference screen and it pops up - edited it accordingly. My first question was misleading, sorry. – Markus Jan 26 '11 at 21:58
  • `CheckBoxPreference` doesn't have a dialog pop up, you just click it on and off. – Nathan Schwermann Jan 31 '11 at 01:33
  • you're right, was a error in reasoning. Still have the problem with a `DialogPreference` (more specifically `ListPreference`). Updated my question. – Markus Jan 31 '11 at 01:33

8 Answers8

38

See the new accepted answer for a much cleaner approach! This was working, but not really the clean way of doing it.


Damn it, it got me several hours, but it finally works.

The solution is the undocumented call public void onItemClick (...). It takes several arguments, and as pointed out by this question it can be used to simulate a click according to the index of the element you want to call.

My problem was the item I want to call is deeply nested in an XML-structure. But the solution is very easy: add a key to the PreferenceScreen the item you want to open is in:

<PreferenceScreen
    android:key="pref_key"
    ....
    />
    <ListPreference android:key="abc"
        android:title="@string/abc"
        android:summary="@string/cde"
        android:persistent="true"/>

</PreferenceScreen>

And the you can just to the following:

// the preference screen your item is in must be known
PreferenceScreen screen = (PreferenceScreen) findPreference("pref_key");

// the position of your item inside the preference screen above
int pos = findPreference("abc").getOrder();

// simulate a click / call it!!
screen.onItemClick( null, null, pos, 0 ); 

And the Dialog pops up!

It would be nice to get the PreferenceScreen a Preference is in (so you would not have to know where your Preference is in), because moving the preference/changing the XML could break the automatic dialog silently and might not get noticed (if not tested).

For this I wrote a function which will search through all preferences and return the PreferenceScreen your preference is on, so you don't need to have your PreferenceScreen a key!

private PreferenceScreen findPreferenceScreenForPreference( String key, PreferenceScreen screen ) {
    if( screen == null ) {
        screen = getPreferenceScreen();
    }

    PreferenceScreen result = null;

    android.widget.Adapter ada = screen.getRootAdapter();
    for( int i = 0; i < ada.getCount(); i++ ) {
        String prefKey = ((Preference)ada.getItem(i)).getKey();
        if( prefKey != null && prefKey.equals( key ) ) {
            return screen;
        }
        if( ada.getItem(i).getClass().equals(android.preference.PreferenceScreen.class) ) {
            result = findPreferenceScreenForPreference( key, (PreferenceScreen) ada.getItem(i) );
            if( result != null ) {
                return result;
            }
        }
    }

    return null;
}

private void openPreference( String key ) {
    PreferenceScreen screen = findPreferenceScreenForPreference( key, null );
    if( screen != null ) {
        screen.onItemClick(null, null, findPreference(key).getOrder(), 0);
    }
}

// With this, you can call your `Preference` like this from code, you do
// not even have to give your PreferenceScreen a key!
openPreference( "abc" );
Community
  • 1
  • 1
Markus
  • 5,667
  • 4
  • 48
  • 64
  • THANK YOU for this code! I was working on a similar problem for most of the day: trying to use the Android JUnit testing to verify changes to ListPreference summary text when a new selection happens. I posted my solution (with credits to your solution!) here: http://stackoverflow.com/questions/6932608/android-junit-testing-of-preferences/6934281#6934281 – Chris Aug 03 '11 at 23:15
  • 1
    Thanks, this is really useful, but in the case where you have multiple PreferenceGroups nested under one PreferenceScreen, you can't use getOrder, since it only counts from the start of the group. Instead, I called getPreferenceScreen().getRootAdapter() and looped through each Preference therein until I found the one with the desired key. The position of that preference within the Adapter is then what I pass to onItemClick. – mhsmith Dec 29 '11 at 22:31
  • 2
    OK but please how do you open only the ListPreference dialog without opening the main preferences screen? – Patrick Mar 02 '12 at 15:15
  • 1
    @markus, Thanks for the solution! How can I call that `findPreference` from my dialog's `onClick` function? I.e. I should click on the Preference item, if user clicks OK in my main activity dialog. – LA_ May 01 '12 at 14:17
  • 1
    Using getOrder does only work when you don't have PreferenceCategories in your PreferenceScreen. Otherwise ordering equals not to the needed position in the PreferenceScreen! – denis May 03 '12 at 12:35
  • Any solution if I have a preference in a PreferenceCategory? – Ashwini Shahapurkar May 30 '12 at 05:29
  • 1
    How do you call getPreferenceScreen() from outside a PreferenceActivity? Will this solution work if I want to open a preference dialog from a regular Activity? – Alexander Dunaev Dec 02 '12 at 09:47
  • If you have preference category then use the below method. See my answer for it. – Deepak Goel Jul 29 '13 at 09:01
23

You could have extended ListPreference to create your dialog, then included your own public method that calls the protected showDialog method of ListPreference. Something like:

public void show()
{
    showDialog(null);
}

This way you won't run into the issue of getOrder() not working when there are PreferenceGroups as several people have pointed out in the comments your answer.

This can be done with any preference types that has a protected showDialog method.

Jabari
  • 5,359
  • 3
  • 26
  • 32
  • 2
    This is the clean way doing it - and apparently the easiest way. Tested it, working. Would be glad if you'd provide a more detailed example (with the extended preference class, changes in xml, etc.). My old answer was more like "the blind leading the blind" seeing this answer. – Markus Dec 09 '12 at 16:20
  • Yes but unluckily it's not possible to use this way to simulate a click on a PreferenceScreen since it's a final class and cannot be extended. Markus's answer in this case is the only way to go and it works if you're careful with PreferenceGroups. – gaborous Nov 20 '13 at 03:51
  • @user1121352 You can, you just have to extend whichever preference types that you want to simulate a button press on. If you want the ability to simulate them all, them you have to extend them all (the types that you use). This example used ListPreference, but you can do the same with all the other types... ;-) – Jabari Nov 21 '13 at 05:46
  • @Jabari I don't think that's possible with PreferenceScreens, as it seems PreferenceScreen has no method called showDialog() – gaborous Nov 23 '13 at 17:32
  • @user1121352 You don't need to extend PreferenceScreen, only the individual preference types listed in it (ListPreference, CheckBoxPreference, etc). – Jabari Nov 26 '13 at 09:16
  • @Jabari that would work if I wanted to simulate a click on one of these preferences, but actually I wanted to open a specific PreferenceScreen when I clicked on a Preference item. Anyway markus's function works for the time being, until I find a better way to do it. – gaborous Dec 31 '13 at 07:14
  • @user1121352 You have two options: 1) You have to extend the specific preference in question (just as my answer states). Within that class, you launch the new Preference Screen. Do a Google search for "Extending Preferences" if you've never done it before. 2) Handle preference clicks in your Preference Activity or Fragment (whichever you are using). If you still don't understand, ask a separate question on Stack Overflow and reply with a link...I'll post an answer with code. – Jabari Jan 03 '14 at 08:55
  • This doesn't work for me. After showDialog i get a NPE when it tries to access getPreferenceManger() which is null. – spatialist Nov 04 '15 at 10:25
10

If you use the support library you can open a dialog easily with PreferenceManager.showDialog(Preference).

In your PreferenceFragmentCompat:

getPreferenceManager().showDialog(findPreference("pref_name"));

Note that support preference package has many issues: non-material styling and it crashes when rotated with an open dialog.

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
headsvk
  • 2,726
  • 1
  • 19
  • 23
  • Does not work for `androidx.preference.CheckBoxPreference`. Check `androidx.preference.PreferenceFragmentCompat` method `onDisplayPreferenceDialog`. There is `IllegalArgumentException`. – t0m Jun 20 '21 at 14:05
8
 PreferenceScreen preferenceScreen  = (PreferenceScreen) findPreference("pref_key");
    final ListAdapter listAdapter = preferenceScreen.getRootAdapter();
         EditTextPreference editPreference = (EditTextPreference)   findPreference("set_password_preference");

    final int itemsCount = listAdapter.getCount();
    int itemNumber;
    for (itemNumber = 0; itemNumber < itemsCount; ++itemNumber) {
        if (listAdapter.getItem(itemNumber).equals(editPreference)) {
            preferenceScreen.onItemClick(null, null, itemNumber, 0);
            break;
        }
    }
     }
 }  
Deepak Goel
  • 5,624
  • 6
  • 39
  • 53
4

Improving deepak goel's answer:

private void openPreference(String key) {
    PreferenceScreen preferenceScreen = getPreferenceScreen();
    final ListAdapter listAdapter = preferenceScreen.getRootAdapter();

    final int itemsCount = listAdapter.getCount();
    int itemNumber;
    for (itemNumber = 0; itemNumber < itemsCount; ++itemNumber) {
        if (listAdapter.getItem(itemNumber).equals(findPreference(key))) {
            preferenceScreen.onItemClick(null, null, itemNumber, 0);
            break;
        }
    }
}
azendh
  • 921
  • 8
  • 23
2

If you're using AndroidX Preference library, it is quite simple.

public class CustomPreferenceFragment extends PreferenceFragmentCompat {

    @Override
    public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
        addPreferencesFromResource(R.xml.your_preference);
        DialogPreference dialogPreference = (DialogPreference) findPreference("your_preference_key");
        onDisplayPreferenceDialog(dialogPreference);
    }
}
brassmookie
  • 104
  • 6
-1

wait, u can do something like this as well

Preference p=findPreference("settings_background_color");
p.setOnPreferenceClickListener(new OnPreferenceClickListener() {

    @Override
    public boolean onPreferenceClick(Preference preference) {

        int color=PreferenceManager.getDefaultSharedPreferences(ALifePatternsWallpaperSettings.this).getInt("settings_background_color", Color.BLACK);
        new ColorPickerDialog(ALifePatternsWallpaperSettings.this, ALifePatternsWallpaperSettings.this, "settings_background_color", color, Color.BLACK).show();
        return true;
    }
});
Markus
  • 5,667
  • 4
  • 48
  • 64
neelabh
  • 479
  • 6
  • 19
  • edit: okey, I see. You create a new dialog on a click with existing settings? Could you modify your example to open a preference defined in XML? – Markus Sep 01 '11 at 10:55
  • not sure what u mean by a preference defined in xml. I think u mean like i click on preference and gives me a child preference. for that u don't need anything u can simply do something like this--------------- so as u can see after clicking PreferenceCategory a new child PreferenceScreen launches. let me know if i got ur question right – neelabh Sep 01 '11 at 22:15
  • 1
    generally it's about having a preference somewhere, and to open it from code without user interaction! Would be great if you provided an example how to open the preference of my question with key `abc` without requiring the user to click on it (or any other preference). – Markus Sep 03 '11 at 11:05
-2

hi friends try this code in works fine

getPreferenceManager().findPreference("YOUR PREF_KEY").setOnPreferenceClickListener(new OnPreferenceClickListener()
        {
            public boolean onPreferenceClick(Preference preference)
            {

                //your code here
                return true;
            }
        });
appukrb
  • 1,507
  • 4
  • 24
  • 53