Mygod Mygod - 4 months ago 9
Java Question

Selected item not changed in the custom ListPreference

It seems

setChecked
is invoked every time I click the preference and the selected item changes but in the dialog the selected item remains unchanged for some reason. I have no idea what's going on. Relaunching the application can make the selected item change but that's not the expected behavior. :~

Here's the class:

public class IconListPreference extends ListPreference {
private Context mContext;
private LayoutInflater mInflater;
private Drawable[] mEntryIcons = null;
private String mKey;
private int selectedEntry = -1;

public IconListPreference(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}

public IconListPreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs);
mContext = context;
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.IconPreference, defStyle, 0);
int entryIconsResId = a.getResourceId(R.styleable.IconPreference_entryIcons, -1);
if (entryIconsResId != -1) setEntryIcons(entryIconsResId);
mInflater = LayoutInflater.from(context);
mKey = getKey();
a.recycle();
}

public void setEntryIcons(Drawable[] entryIcons) {
mEntryIcons = entryIcons;
}

public void setEntryIcons(int entryIconsResId) {
TypedArray icons_array = mContext.getResources().obtainTypedArray(entryIconsResId);
Drawable[] icon_ids_array = new Drawable[icons_array.length()];
for (int i = 0; i < icons_array.length(); i++) icon_ids_array[i] = icons_array.getDrawable(i);
setEntryIcons(icon_ids_array);
icons_array.recycle();
}

@Override
protected void onPrepareDialogBuilder(Builder builder) {
CharSequence[] entries = getEntries(), entryValues = getEntryValues();
if (entries.length != entryValues.length) throw new IllegalStateException
("ListPreference requires an entries array and an entryValues array which are both the same length");
if (mEntryIcons != null && entries.length != mEntryIcons.length) throw new IllegalStateException
("IconListPreference requires the icons entries array be the same length than entries or null");
IconListPreferenceScreenAdapter iconListPreferenceAdapter = new IconListPreferenceScreenAdapter();
if (mEntryIcons != null) {
String selectedValue = getPreferenceManager().getSharedPreferences().getString(mKey, "");
for (int i = 0; i < entryValues.length; i++) {
if (selectedValue.compareTo((String) entryValues[i]) == 0) {
selectedEntry = i;
break;
}
}
builder.setAdapter(iconListPreferenceAdapter, null);
}
super.onPrepareDialogBuilder(builder);
}

private class IconListPreferenceScreenAdapter extends BaseAdapter {
public int getCount() {
return mEntryIcons.length;
}

class CustomHolder {
private CheckedTextView text = null;

CustomHolder(View row, int position) {
text = (CheckedTextView) row.findViewById(android.R.id.text1);
text.setText(getEntries()[position]);
text.setChecked(selectedEntry == position);
if (mEntryIcons != null)
text.setCompoundDrawablesWithIntrinsicBounds(mEntryIcons[position], null, null, null);
}
}

public Object getItem(int position) {
return getEntries()[position];
}

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

public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) convertView = mInflater.inflate(Resources.getSystem()
.getIdentifier("select_dialog_singlechoice_holo", "layout", "android"), parent, false);
CustomHolder holder;
final int p = position;
holder = new CustomHolder(convertView, position);
convertView.setTag(holder);
convertView.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
v.requestFocus();
getDialog().dismiss();
IconListPreference.this.callChangeListener(getEntryValues()[p]);
SharedPreferences.Editor editor = getPreferenceManager().getSharedPreferences().edit();
editor.putString(mKey, getEntryValues()[p].toString());
selectedEntry = p;
editor.apply();
}
});
return convertView;
}
}
}


And
res/values/attrs.xml
:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="IconPreference">
<attr name="entryIcons" format="reference" />
</declare-styleable>
</resources>


EDIT: Updated my code with a bugfix, but still don't work sthough. A temporary solution found: invoke
setValue((String) newValue)
in
OnPreferenceChangeListener.onPreferenceChange
.

Answer

OK I finally get it. You need to do the following to make your customized preference work:

  • callChangeListener before setting user-supplied values;
  • notifyChanged if you need to update user interface, i.e. whenever value changes.