34

I have a preference activity that has a language as ListPreference which displays the available language list. I can fill the list when onCreate is called, but I want to fill the list when the user clicks on it.

this is the java code :

public class SettingsActivity extends PreferenceActivity implements OnPreferenceClickListener {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        try {
            addPreferencesFromResource(R.xml.settings);
        } catch (Exception e) {

        }

    }

    @Override
    public boolean onPreferenceClick(Preference preference) {

        if((preference instanceof ListPreference) && (preference.getKey().equals("language"))){
            ListPreference lp = (ListPreference)preference;
            CharSequence[] entries = { "English", "French" };
            CharSequence[] entryValues = {"1" , "2"};
            lp.setEntries(entries);
            lp.setDefaultValue("1");
            lp.setEntryValues(entryValues);
            return true;
        }
        return false;

    }
}

and this is the settings.xml (preference) :

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
  xmlns:android="http://schemas.android.com/apk/res/android">
    <PreferenceCategory android:title="General Settings">
        <CheckBoxPreference android:key="enabled"  android:title="Application Status" android:summary="Enable or disable the application" />
         <ListPreference 
         android:key="language" 
         android:title="Language"
         android:dialogTitle="Application language" 
         android:summary="Select the Application language"
         />
    </PreferenceCategory>
</PreferenceScreen>

I searched but found no result! An exception occurs every time I click on that list.

Abdullah
  • 5,445
  • 10
  • 41
  • 48

4 Answers4

60

You are getting the exception because your ListPreference object is not initialized - you either need to set entries and entryValues attributes in your XML or do it programatically in onCreate() or in onCreatePreferences() (e.g. when using androidx.preference.PreferenceFragmentCompat).

If if you need to change the items in the list dynamically after the initial ListPreference object has been initialized then a OnPreferenceClickListener needs to be set on the ListPreference object. Use the key you have specified in the XML to get a handle to the preference.

Since the code to populate the entries and entryValues arrays will have to be run both in onCreate() and in onPreferenceClick, it makes sense to extract it to a separate method - setListPreferenceData() in order to avoid duplication.

public class SettingsActivity extends PreferenceActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        try {
            addPreferencesFromResource(R.xml.settings);
        } catch (Exception e) {

        }

        final ListPreference listPreference = (ListPreference) findPreference("language");

        // THIS IS REQUIRED IF YOU DON'T HAVE 'entries' and 'entryValues' in your XML
        setListPreferenceData(listPreference);

        listPreference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
            @Override
            public boolean onPreferenceClick(Preference preference) {
                    
                setListPreferenceData(listPreference);
                return false;
            }
        });
    }

    protected static void setListPreferenceData(ListPreference lp) {
            CharSequence[] entries = { "English", "French" };
            CharSequence[] entryValues = {"1" , "2"};
            lp.setEntries(entries);
            lp.setDefaultValue("1");
            lp.setEntryValues(entryValues);
    }
}

Here is another example from google's DeskClock app:

ccpizza
  • 28,968
  • 18
  • 162
  • 169
  • 3
    This answer works but it's a little buggy if you let the setListPreferenceData run only onClick (and not onCReate). That's because if the setListPreferenceData method takes more than a fraction of a second then the default list is shown before the data is set, and the user is forced to cancel then open the preference again. Is there a way to update the listPreference that's already being displayed after you setListPreferenceData is finished? – Malabarba Apr 22 '13 at 10:58
  • 3
    If you need custom behaviour your best bet would probably be extending `ListPreference` or `Preference`. A purely 'social' workaround would be to set the default list item value to something like – ccpizza Apr 23 '13 at 15:11
  • 1
    Yeah, I had to extend listpreference myself. But it wasn't so bad. onCreate was the only method that needed overloading. – Malabarba Apr 23 '13 at 15:50
  • @ccpizza why not simply use a dialog? see http://developer.android.com/reference/android/app/AlertDialog.Builder.html#setAdapter(android.widget.ListAdapter, android.content.DialogInterface.OnClickListener) – likejudo Apr 28 '14 at 21:31
  • @ccpizza Great Job, I am Using PreferenceFragment & JAVA set key rather than PreferenceActivity & XML in my answer http://stackoverflow.com/a/36904777/1815624 which was derived from this answer. – CrandellWS Apr 28 '16 at 03:34
  • This does not seem to work. It seems that the click event is called after the list is shown. So when you first click it, you do not see the changed list, you see the old one. You only see the changed list if you click it again. – Damn Vegetables Sep 01 '17 at 11:16
  • Google's DeskClock source was really helpful in solving this issue. – kalan nawarathne Aug 09 '21 at 20:42
8

Using PreferenceFragment & JAVA set key rather than PreferenceActivity & XML as shown in https://stackoverflow.com/a/13828912/1815624, which this answer is based on:

If what you want is to be able to change the items in the list dynamically after the initial ListPreference object has been initialized then you will need to attach the OnPreferenceClickListener directly to the ListPreference object. Use the key you have specified in the JAVA source (as CUSTOM_LIST) to get a handle to the preference.

Since the code to populate the entries and entryValues arrays will have to run both in onCreate() and in onPreferenceClick, it makes sense to extract it to a separate method - setListPreferenceData() in order to avoid duplication.

/**
 * This fragment shows data and sync preferences only. It is used when the
 * activity is showing a two-pane settings UI.
 */
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static class myCustomPreferenceFragment extends PreferenceFragment {

    final private String CUSTOM_LIST= "custom_list";

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.pref_custom_frag);

        PreferenceCategory targetCategory = (PreferenceCategory) findPreference("CUSTOM_FRAG");

        final ListPreference lp = setListPreferenceData((ListPreference) findPreference(CUSTOM_LIST), getActivity());

        lp.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
            @Override
            public boolean onPreferenceClick(Preference preference) {

                setListPreferenceData(lp, getActivity());
                return false;
            }
        });
        setHasOptionsMenu(true);
        targetCategory.addPreference(lp);

        bindPreferenceSummaryToValue(targetCategory);
        bindPreferenceSummaryToValue(lp);
    }

    protected ListPreference setListPreferenceData(ListPreference lp, Activity mActivity) {
        CharSequence[] entries = { "One", "Two", "Three" };
        CharSequence[] entryValues = { "1", "2", "3" };
        if(lp == null)
            lp = new ListPreference(mActivity);
        lp.setEntries(entries);
        lp.setDefaultValue("1");
        lp.setEntryValues(entryValues);
        lp.setTitle("Number Of blahs");
        lp.setSummary(lp.getEntry());
        lp.setDialogTitle("Number of Blah objects");
        lp.setKey(CUSTOM_LIST);
        return lp;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (id == android.R.id.home) {
            startActivity(new Intent(getActivity(), SettingsActivity.class));
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
}

XML layout:

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
    <PreferenceCategory
        android:key="CUSTOM_FRAG"
        android:title="Some Options">

    </PreferenceCategory>
</PreferenceScreen>
Community
  • 1
  • 1
CrandellWS
  • 2,708
  • 5
  • 49
  • 111
1

I solved the problem my extending the ListPreference. It was very simple.

public class DListPref extends ListPreference
{
    public interface LoadingListener
    {
        void setData(ListPreference lp);
    }

    LoadingListener TheLL;

    public void setLoadingListener(LoadingListener l)
    {
        TheLL = l;
    }

    @Override
    protected void onPrepareDialogBuilder(AlertDialog.Builder builder)
    {
        if(TheLL!=null)
        {
            TheLL.setData(this);
        }
        super.onPrepareDialogBuilder(builder);
    }

    //Do not mind the rest of this class, as they are auto-generated boilerplate code.
    public DListPref(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)
    {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    public DListPref(Context context, AttributeSet attrs, int defStyleAttr)
    {
        super(context, attrs, defStyleAttr);
    }

    public DListPref(Context context, AttributeSet attrs)
    {
        super(context, attrs);
    }

    public DListPref(Context context)
    {
        super(context);
    }
}

And then I just changed the name to my class in the XML.

<my.company.DListPref
    android:defaultValue="-1"
    android:key="damn"
    android:title="vegetables"/>

And then I simply did this at the onCreate.

        DListPref lp = (DListPref) findPreference("damn");

        lp.setLoadingListener(new DListPref.LoadingListener()
        {
            @Override
            public void setData(ListPreference lp)
            {
                lp.setEntries(new String[]{"doge", "wow"});
                lp.setEntryValues(new String[] {"1", "2"});
                lp.setDefaultValue("1");
            }
        });

Worked right away. Actually, I never expected it would be done so easily.

Damn Vegetables
  • 11,484
  • 13
  • 80
  • 135
0

This is what I had to do to make mine work

    public static class MyPreferenceFragment extends PreferenceFragment
{
    @Override
    public void onCreate(final Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.pref_general);
        PreferenceCategory targetCategory = (PreferenceCategory) findPreference("CUSTOM_FRAG");

        //final Preference pref;
        //pref = findPreference("example_list1");
        final ListPreference lp = setListPreferenceDataSocieties((ListPreference) findPreference("custom1"), getActivity());
        final ListPreference lp2 = setListPreferenceDataRoutes((ListPreference) findPreference("custom2"), getActivity());
        final ListPreference lp3 = setListPreferenceDataCoolers((ListPreference) findPreference("custom3"), getActivity());



        setHasOptionsMenu(true);
        targetCategory.addPreference(lp);
        targetCategory.addPreference(lp2);
        targetCategory.addPreference(lp3);

        bindPreferenceSummaryToValue(targetCategory);
        bindPreferenceSummaryToValue(lp);
        bindPreferenceSummaryToValue(lp2);
        bindPreferenceSummaryToValue(lp3);
    }

    protected ListPreference setListPreferenceDataSocieties(ListPreference lp, Activity mActivity) {
        CharSequence[] entries = { "One", "Two", "Three" };
        CharSequence[] entryValues = { "1", "2", "3" };
        if(lp == null)
            lp = new ListPreference(mActivity);
        lp.setEntries(entries);
        lp.setDefaultValue("1");
        lp.setEntryValues(entryValues);
        lp.setTitle("Origin Society");
        lp.setSummary(lp.getEntry());
        lp.setDialogTitle("Registered Societies");
        lp.setKey("society_list");
        return lp;
    }


    protected ListPreference setListPreferenceDataRoutes(ListPreference lp, Activity mActivity) {
        CharSequence[] entries = { "One", "Two", "Three" };
        CharSequence[] entryValues = { "1", "2", "3" };
        if(lp == null)
            lp = new ListPreference(mActivity);
        lp.setEntries(entries);
        lp.setDefaultValue("1");
        lp.setEntryValues(entryValues);
        lp.setTitle("Designated Route");
        lp.setSummary(lp.getEntry());
        lp.setDialogTitle("Available Routes");
        lp.setKey("route_list");
        return lp;
    }
    protected ListPreference setListPreferenceDataCoolers(ListPreference lp, Activity mActivity) {
        CharSequence[] entries = { "One", "Two", "Three" };
        CharSequence[] entryValues = { "1", "2", "3" };
        if(lp == null)
            lp = new ListPreference(mActivity);
        lp.setEntries(entries);
        lp.setDefaultValue("1");
        lp.setEntryValues(entryValues);
        lp.setTitle("Destination Cooler");
        lp.setSummary(lp.getEntry());
        lp.setDialogTitle("Available Coolers");
        lp.setKey("cooler_list");
        return lp;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (id == android.R.id.home) {
            startActivity(new Intent(getActivity(), SettingsActivity.class));
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

}


boolean first_run = true;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    MyPreferenceFragment frag =  new MyPreferenceFragment();
    getFragmentManager().beginTransaction().replace(android.R.id.content, frag).commit();

}
Howard J
  • 421
  • 3
  • 7