当触发onPreferenceClick时如何动态填充ListPreference?

34

我有一个偏好设置活动,其中有一个语言作为ListPreference,它显示可用的语言列表。我可以在onCreate被调用时填充列表,但我希望在用户单击它时填充列表。

这是Java代码:

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;

    }
}

这是 settings.xml(首选项)文件:

<?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>

我搜索了,但没有找到结果!每次我点击那个列表时都会发生异常。

4个回答

60
你之所以出现异常是因为你的ListPreference对象没有初始化 - 你需要在XML中设置entriesentryValues属性,或者在onCreate()onCreatePreferences()中以编程方式进行设置(例如使用androidx.preference.PreferenceFragmentCompat)。
如果在初始化ListPreference对象之后需要动态更改列表中的项目,则需要在ListPreference对象上设置OnPreferenceClickListener。使用XML中指定的键获取对首选项的引用。
由于填充entriesentryValues数组的代码必须在onCreate()onPreferenceClick中运行,因此将其提取到一个单独的方法setListPreferenceData()中以避免重复是有意义的。
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);
    }
}

这是谷歌的DeskClock应用程序的另一个例子:在这里。

3
这个答案的效果还可以,但是如果你只在onClick方法中运行setListPreferenceData(而不是onCreate方法),可能会有一些bug。因为如果setListPreferenceData方法花费的时间超过了一小部分时间,那么在数据设置之前将显示默认列表,并且用户被迫取消后再次打开偏好设置。是否有办法在setListPreferenceData完成后更新已经显示的listPreference? - Malabarba
3
如果您需要自定义行为,最好的选择可能是扩展ListPreferencePreference。一个纯粹的“社交”解决方法是将默认列表项值设置为类似于<loading...>的内容。 - ccpizza
1
是的,我不得不自己扩展ListPreference。但这并不难。只需要重载onCreate方法即可。 - Malabarba
@ccpizza 做得好,我在我的答案https://dev59.com/nWw15IYBdhLWcg3wpNcB#36904777中使用PreferenceFragment和JAVA set key而不是PreferenceActivity和XML,这个答案是从这个答案中派生出来的。 - CrandellWS
这似乎不起作用。看起来点击事件是在列表显示后调用的。因此,当您第一次单击它时,您看不到更改后的列表,而是看到旧列表。只有再次单击它才能看到更改后的列表。 - Damn Vegetables
显示剩余2条评论

8
使用PreferenceFragment和JAVA设置键,而不是像https://dev59.com/nWw15IYBdhLWcg3wpNcB#13828912中所示的使用PreferenceActivity和XML。如果您想要能够在初始化ListPreference对象后动态更改列表中的项目,则需要直接将OnPreferenceClickListener附加到ListPreference对象上。使用您在JAVA源代码中指定的键(如CUSTOM_LIST)获取对该首选项的处理。由于填充entriesentryValues数组的代码必须在onCreate()onPreferenceClick中运行,因此最好将其提取到一个单独的方法-setListPreferenceData()以避免重复。
/**
 * 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 布局:

<?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>

1
我通过扩展ListPreference来解决了这个问题。 这非常简单。
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);
    }
}

然后我只是在XML中将名称更改为我的类名。
<my.company.DListPref
    android:defaultValue="-1"
    android:key="damn"
    android:title="vegetables"/>

然后我只是在 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");
            }
        });

立即生效。实际上,我从未想过会如此轻松地完成。


0

这是我必须做的才能让我的工作正常运行

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

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接