How to open or simulate a click on an android Preference, created with XML, programmatically?
Asked Answered
E

8

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.

Elate answered 26/1, 2011 at 14:50 Comment(4)
You want to show only one field from all the preference screen? Or simply open the whole preference dialog?Joey
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.Elate
CheckBoxPreference doesn't have a dialog pop up, you just click it on and off.Ward
you're right, was a error in reasoning. Still have the problem with a DialogPreference (more specifically ListPreference). Updated my question.Elate
R
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.

Recommit answered 16/11, 2012 at 9:53 Comment(8)
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.Elate
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.Bossuet
@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... ;-)Recommit
@Recommit I don't think that's possible with PreferenceScreens, as it seems PreferenceScreen has no method called showDialog()Bossuet
@user1121352 You don't need to extend PreferenceScreen, only the individual preference types listed in it (ListPreference, CheckBoxPreference, etc).Recommit
@Recommit 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.Bossuet
@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.Recommit
This doesn't work for me. After showDialog i get a NPE when it tries to access getPreferenceManger() which is null.Malicious
E
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" );
Elate answered 1/2, 2011 at 23:8 Comment(8)
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: #6933108Piazza
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.Newsstand
OK but please how do you open only the ListPreference dialog without opening the main preferences screen?Geddes
@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.Rig
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!Straitlaced
Any solution if I have a preference in a PreferenceCategory?Etna
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?Sickening
If you have preference category then use the below method. See my answer for it.Goaltender
R
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.

Recommit answered 16/11, 2012 at 9:53 Comment(8)
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.Elate
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.Bossuet
@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... ;-)Recommit
@Recommit I don't think that's possible with PreferenceScreens, as it seems PreferenceScreen has no method called showDialog()Bossuet
@user1121352 You don't need to extend PreferenceScreen, only the individual preference types listed in it (ListPreference, CheckBoxPreference, etc).Recommit
@Recommit 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.Bossuet
@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.Recommit
This doesn't work for me. After showDialog i get a NPE when it tries to access getPreferenceManger() which is null.Malicious
W
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.

Weanling answered 26/1, 2016 at 13:22 Comment(1)
Does not work for androidx.preference.CheckBoxPreference. Check androidx.preference.PreferenceFragmentCompat method onDisplayPreferenceDialog. There is IllegalArgumentException.Waistcoat
G
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;
        }
    }
     }
 }  
Goaltender answered 29/7, 2013 at 9:2 Comment(1)
Yes. If there are categories within ,this works fine.Annalisaannalise
C
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;
        }
    }
}
Cincture answered 29/9, 2015 at 20:16 Comment(0)
B
4

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);
    }
}
Brough answered 2/10, 2020 at 18:59 Comment(0)
P
-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;
    }
});
Precast answered 31/8, 2011 at 13:13 Comment(3)
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?Elate
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---------------<PreferenceCategory <PreferenceScreen /> </PreferenceScreen> </PreferenceCategory> so as u can see after clicking PreferenceCategory a new child PreferenceScreen launches. let me know if i got ur question rightPrecast
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).Elate
C
-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;
            }
        });
Caducity answered 19/11, 2012 at 12:34 Comment(1)
How will this simulate a click/touch event?Fra

© 2022 - 2024 — McMap. All rights reserved.