Custom Row in a Listpreference

custom row in a listPreference?

OK I got this to work, mostly. I had to use a custom defined class that extends ListPreference. Then inside of that I had to create a custom adapter class just like you would for a ListView and set it to the builder using builder.setAdapter(). I also had to define listeners for both the RadioButtons and the ListView rows that handled unchecking of the RadioButtons and such. The only issues I still have are, my custom ListPreference has both an OK and a Cancel button where a ListPreference only has the cancel button. I don't know how to remove the OK button. Also, I can't get the rows to highlight when I click on them like they do in a regular ListPreference.

The java code for the custom ListPreference class. Be sure to mind things like your package name, the preference name (key), your entries and values for the ListPreference, and the names of your xml items.

package your.package.here;

import java.util.ArrayList;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.preference.ListPreference;
import android.preference.PreferenceManager;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.CompoundButton;
import android.widget.RadioButton;
import android.widget.TextView;
import android.app.Dialog;
import android.app.AlertDialog.Builder;

public class CustomListPreference extends ListPreference
{
CustomListPreferenceAdapter customListPreferenceAdapter = null;
Context mContext;
private LayoutInflater mInflater;
CharSequence[] entries;
CharSequence[] entryValues;
ArrayList<RadioButton> rButtonList;
SharedPreferences prefs;
SharedPreferences.Editor editor;

public CustomListPreference(Context context, AttributeSet attrs)
{
super(context, attrs);
mContext = context;
mInflater = LayoutInflater.from(context);
rButtonList = new ArrayList<RadioButton>();
prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
editor = prefs.edit();
}

@Override
protected void onPrepareDialogBuilder(Builder builder)
{
entries = getEntries();
entryValues = getEntryValues();

if (entries == null || entryValues == null || entries.length != entryValues.length )
{
throw new IllegalStateException(
"ListPreference requires an entries array and an entryValues array which are both the same length");
}

customListPreferenceAdapter = new CustomListPreferenceAdapter(mContext);

builder.setAdapter(customListPreferenceAdapter, new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int which)
{

}
});
}

private class CustomListPreferenceAdapter extends BaseAdapter
{
public CustomListPreferenceAdapter(Context context)
{

}

public int getCount()
{
return entries.length;
}

public Object getItem(int position)
{
return position;
}

public long getItemId(int position)
{
return position;
}

public View getView(final int position, View convertView, ViewGroup parent)
{
View row = convertView;
CustomHolder holder = null;

if(row == null)
{
row = mInflater.inflate(R.layout.custom_list_preference_row, parent, false);
holder = new CustomHolder(row, position);
row.setTag(holder);

// do whatever you need here, for me I wanted the last item to be greyed out and unclickable
if(position != 3)
{
row.setClickable(true);
row.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
for(RadioButton rb : rButtonList)
{
if(rb.getId() != position)
rb.setChecked(false);
}

int index = position;
int value = Integer.valueOf((String) entryValues[index]);
editor.putInt("yourPref", value);

Dialog mDialog = getDialog();
mDialog.dismiss();
}
});
}
}

return row;
}

class CustomHolder
{
private TextView text = null;
private RadioButton rButton = null;

CustomHolder(View row, int position)
{
text = (TextView)row.findViewById(R.id.custom_list_view_row_text_view);
text.setText(entries[position]);
rButton = (RadioButton)row.findViewById(R.id.custom_list_view_row_radio_button);
rButton.setId(position);

// again do whatever you need to, for me I wanted this item to be greyed out and unclickable
if(position == 3)
{
text.setTextColor(Color.LTGRAY);
rButton.setClickable(false);
}

// also need to do something to check your preference and set the right button as checked

rButtonList.add(rButton);
rButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener()
{
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
{
if(isChecked)
{
for(RadioButton rb : rButtonList)
{
if(rb != buttonView)
rb.setChecked(false);
}

int index = buttonView.getId();
int value = Integer.valueOf((String) entryValues[index]);
editor.putInt("yourPref", value);

Dialog mDialog = getDialog();
mDialog.dismiss();
}
}
});
}
}
}
}

The xml for my PreferenceActivity. This is not my full xml, took out all my other preference items for simplicity. Again, be sure to mind the package name, the custom ListPreference class must be referenced by the package name. Also mind the names of the preference and the array names that hold the entries and values.

<?xml version="1.0" encoding="utf-8"?>

<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android">

<PreferenceCategory
android:title="Your Title">

<your.package.here.CustomListPreference
android:key="yourPref"
android:title="Your Title"
android:dialogTitle="Your Title"
android:summary="Your Summary"
android:defaultValue="1"
android:entries="@array/yourArray"
android:entryValues="@array/yourValues"/>

</PreferenceCategory>
</PreferenceScreen>

My xml for the dialog's list view row. In the getView method be sure to use the name of this xml file in the line that inflates this.

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:paddingBottom="8dip"
android:paddingTop="8dip"
android:paddingLeft="10dip"
android:paddingRight="10dip">

<TableLayout
android:id="@+id/custom_list_view_row_table_layout"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:stretchColumns="0">

<TableRow
android:id="@+id/custom_list_view_row_table_row"
android:gravity="center_vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content">

<TextView
android:id="@+id/custom_list_view_row_text_view"
android:textSize="22sp"
android:textColor="#000000"
android:gravity="center_vertical"
android:layout_width="160dip"
android:layout_height="40dip" />

<RadioButton
android:checked="false"
android:id="@+id/custom_list_view_row_radio_button"/>
</TableRow>
</TableLayout>

</LinearLayout>

Finally, under res/values here is my array.xml that contains the entry names and values for the ListPreference. Again, shortened mine for simplicity.

<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="yourArray">
<item>Item 1</item>
<item>Item 2</item>
<item>Item 3</item>
<item>Item 4</item>
</string-array>

<string-array name="yourValues">
<item>0</item>
<item>1</item>
<item>2</item>
<item>3</item>
</string-array>
</resources>

Custom Row Adapter in androix.preference.ListPreference

One should override PreferenceFragmentCompat#onDisplayPreferenceDialog(preference: Preference) to display a custom preference dialog :

override fun onDisplayPreferenceDialog(preference: Preference) {
if (preference.key == MY_CUSTOM_DIALOG_KEY) {
if (parentFragmentManager.findFragmentByTag(DIALOG_FRAGMENT_TAG) != null) {
// already displayed to user
return
}
MyCustomPreferenceDialogFragment.newInstance(preference.key).let {
it.setTargetFragment(this, 0)
it.show(parentFragmentManager, DIALOG_FRAGMENT_TAG)
}
} else {
super.onDisplayPreferenceDialog(preference)
}
}

To implement your custom preference dialog fragment, you can have a look at implementations from support library :

  • PreferenceDialogFragmentCompat

Android Customize Row in ListPreference

I found that the List Preference does not have the ability to do this.

Doubled rows in a custom listPreference

[edited based on update]

It's possible that the id being set is conflicting with some other id. See if this is helpful - Android: View.setID(int id) programmatically - how to avoid ID conflicts?.

EditText inside a custom ListPreference : no keyboard

Ok, I managed to solve this out.

As said here: https://stackoverflow.com/a/9118027/1376834

The problem seems to be (at least in my case), that since the place where you enter text is hidden initially (or nested or something), AlertDialog is automatically setting the flag WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM (or some combination of that and WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) so that things don't trigger a soft input to show up.

So I just added
getDialog().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
in my CustomHolder constructor, so that the Dialog is already built when I do so.

Custom ListPreference with dynamic entries

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


Related Topics



Leave a reply



Submit