I am trying to display a list with checkboxes using a preference activity. The list is supplied with 121 entries and by debugging I have confirmed that the complete list is passed into the ListPreference. However the scrollable list ends up with the first 4 entries repeated to the size of the entries i.e. 121. I found code on here which is as follows. Stepping through with the debugger it seems that CustomHolder(View row, int position) is supplied with position as first 0 then 2 and so on to 3 at which point it appears as 0 again. I cant see where and why this is happening so.
First a preference.xml
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:title="Your Title">
<com.Applitastic.CustomListPreference
android:key="multipref"
android:id="@+id/multiselectlist"
android:title="Apps to exclude" android:summary="Specify exclusion"
android:dialogTitle="Apps to exclude" android:defaultValue="1"/>
</PreferenceCategory>
</PreferenceScreen>
Now an xml for a tablelayout
<?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>
Now the ListPreference code.
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.AlertDialog.Builder;
import android.app.Dialog;
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.excludeapps, 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();
}
});
// }
} else {
holder = (CustomHolder) convertView.getTag();
}
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();
}
}
});
}
}
}
}
Well, look at
getView. Your last action for a recycled view is:You have to fill and updated that holder with the new data for that position. It your case it just stays the same (so the data which was set in the
ifsection).Edit: How to update the holder?
Suppose you would implement this method in
CustomHolder, then you could update it the way showed obove:Notice: the common pattern is not to implement the update itself in the holder. My holder is usually a class with public members like
holder.title(and no methods, just a container for references), so I can callholder.title.setTitle(...)fromgetViewdirectly.