Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom ListPreference with dynamic entries

Tags:

android

I need dynamic entries in ListPreference so I can not use conventional way of XML setup of which there are tons of materials online. So far I have following setup as you can see bellow. Problem is that when I run this I see dialog with title and message but no entries are showed even though I know that entries and values are not empty (I know that my entries and values are same but I would get error if I didn't supplied entries)


my.preference.DynamicPreference
            android:title="@string/date_format"
            android:dialogMessage="@string/profile_info_date_format"
            android:entryValues="@array/date_format_values"
            android:entries="@array/date_format_values"

public class DynamicPreference extends ListPreference {
    private int index;

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

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

    @Override
    protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
        builder.setTitle(getTitle());
        builder.setMessage(getDialogMessage());
        builder.setSingleChoiceItems(entries(), -1, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {

            }
        });
        super.onPrepareDialogBuilder(builder);
    }

    @Override
    public void setEntries(CharSequence[] sequence) {
        CharSequence[] entries = listObjects().toArray(new CharSequence[listObjects().size()]);
        super.setEntries(entries);
    }

    @Override
    public void setEntryValues(CharSequence[] sequence) {
        CharSequence[] values = getContext().getResources().getStringArray(R.string.date_format);
        super.setEntryValues(values);
    }
}
like image 903
peter_budo Avatar asked Apr 05 '12 19:04

peter_budo


2 Answers

The android:dialogMessage was good starting point here, thank you @MH. for spotting it. Bellow is simple setup I ended with, I hope someone may find it helpful

<my.preference.DynamicPreference
        android:title="@string/local_time"
        android:key="profile_info_local_time"
        />

public class DynamicPreference extends ListPreference {


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

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

    @Override
    protected View onCreateDialogView() {
        ListView view = new ListView(getContext());
        view.setAdapter(adapter());
        setEntries(entries());
        setEntryValues(entryValues());
        setValueIndex(initializeIndex());
        return view;
    }

    private ListAdapter adapter() {
        return new ArrayAdapter(getContext(), android.R.layout.select_dialog_singlechoice);
    }

    private CharSequence[] entries() {
        //action to provide entry data in char sequence array for list
    }

    private CharSequence[] entryValues() {
        //action to provide value data for list
    }
}
like image 67
peter_budo Avatar answered Sep 27 '22 19:09

peter_budo


I can see some potential possibilities for why it isn't working as expected:

  1. Where does listObjects come from? Its presence suggests it's a member variable, but I don't see it getting set or initialized anywhere. Check that your not using an 'empty' list to populate the dialog, as that might be causing the absence of any items in it.

  2. Alternatively, are you ever explicitly calling setEntries(...) and setEntryValues(...)? I'm asking, because ListPreference doesn't use those two setters internally - it directly uses the private member variables (mEntries and mEntryValues) for that. In other words, if you don't ever call both functions yourself, no one will, which means that the arrays will never get set on the super class either; hence the empty dialog.

  3. Similar to my first observation: what is entries()? It sitting in onPrepareDialogBuilder, but it does not seem to be declared anywhere.

Perhaps it's best to start adding some more code snippets, because based on everything that's missing, there's no way of telling where the exact origin of your problem lies...

Edit: Alright, I set up a quick test project to follow your steps. The solution to your problem is actually fairly straightforward: get rid of the android:dialogMessage attribute, and your list items should show up - I have to add that I only tested this with a plain ListPreference.

The problem is easily found if you go look at the source for ListPreference, with extends from DialogPreference. In ShowDialog() the following can be found:

285        View contentView = onCreateDialogView();
286        if (contentView != null) {
287            onBindDialogView(contentView);
288            mBuilder.setView(contentView);
289        } else {
290            mBuilder.setMessage(mDialogMessage);
291        }
292        
293        onPrepareDialogBuilder(mBuilder);

Now, as you can see it all depends on what onCreateDialog returns, because at the time onPrepareDialogBuilder is called, the decision what to display is already made. So let's have a look at that:

336    protected View onCreateDialogView() {
337        if (mDialogLayoutResId == 0) {
338            return null;
339        }
340        
341        LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(
342                Context.LAYOUT_INFLATER_SERVICE);
343        return inflater.inflate(mDialogLayoutResId, null);
344    }

It appears it tries to inflate a resource referenced by mDialogLayoutResId. If that isn't set or available, it'll return null and only then the dialog 'message' will show. The content (list with items) will only show if there is a valid layout referenced. If you want both the content and the message to display, you will need to add a custom dialog layout using the android:dialogLayout attribute, and add a TextView with android:id="@android:id/message" as id. In the case of a ListPreference, the message will then be shown below the list, by default.

Note that you do not need to extend the built-in ListPreference in order to add entries and values programmatically. Simple call setEntries(...) and setEntryValues(...) on the ListPreference instance with the data you want it to show.

Also, in the case of your DynamicPreference subclass, any behaviour you changed may get overridden as you call super.onPrepareDialogBuilder(builder) all the way at the end of your method. As mentioned earlier though: I've just tested this with the default ListPreference and that seems to work fine and do everything you request.

like image 25
MH. Avatar answered Sep 27 '22 17:09

MH.