Пользовательская строка в listPreference?
Я пытаюсь создать ListPreference
но как-то отключить один из пунктов. Что-то вроде серого цвета или чего-то такого, и у него нет возможности выбрать его. Это будет новая функция, и я хочу, чтобы она была в списке, но ее нельзя было выбрать.
Я создал кастом ListPreference
класс и в этом классе пользовательский адаптер, надеясь использовать адаптер для создания того, что я хочу.
Код работает и устанавливает адаптер, но ни одна из функций адаптера не вызывается. Я устанавливаю точки останова на методы, такие как getCount()
но они никогда не звонят.
Вот мой код Пользовательские ListPreference взяты с http://blog.350nice.com/wp/archives/240
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Color;
import android.preference.ListPreference;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.app.AlertDialog.Builder;
public class CustomListPreference extends ListPreference {
private boolean[] mClickedDialogEntryIndices;
CustomListPreferenceAdapter customListPreferenceAdapter = null;
Context mContext;
public CustomListPreference(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
mClickedDialogEntryIndices = new boolean[getEntries().length];
}
@Override
protected void onPrepareDialogBuilder(Builder builder) {
CharSequence[] entries = getEntries();
CharSequence[] 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");
}
builder.setMultiChoiceItems(entries, mClickedDialogEntryIndices,
new DialogInterface.OnMultiChoiceClickListener() {
public void onClick(DialogInterface dialog, int which,
boolean val) {
mClickedDialogEntryIndices[which] = val;
}
});
// setting my custom list adapter
customListPreferenceAdapter = new CustomListPreferenceAdapter(mContext);
builder.setAdapter(customListPreferenceAdapter, null);
}
private class CustomListPreferenceAdapter extends BaseAdapter {
public CustomListPreferenceAdapter(Context context) {}
public int getCount() {
return 1;
}
public Object getItem(int position) {
return position;
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
convertView.setBackgroundColor(Color.BLUE);
return convertView;
}
}
}
9 ответов
Хорошо, я получил это на работу, в основном. Мне пришлось использовать пользовательский класс, который расширяет ListPreference
, Затем внутри этого я должен был создать собственный класс адаптера, как вы бы для ListView
и установить его для строителя, используя builder.setAdapter()
, Я также должен был определить слушателей для обоих RadioButtons
и ListView
строки, которые обрабатываются снятия флажка RadioButtons
и тому подобное. Единственные проблемы, которые у меня остались, это мой обычай ListPreference
имеет кнопки ОК и Отмена, где ListPreference
есть только кнопка отмены. Я не знаю, как удалить кнопку ОК. Кроме того, я не могу выделить строки, когда нажимаю на них, как в обычной ListPreference
,
Java-код для пользовательских ListPreference
учебный класс. Обязательно помните такие вещи, как имя вашего пакета, имя предпочтения (ключ), ваши записи и значения для ListPreference
и названия ваших элементов XML.
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();
}
}
});
}
}
}
}
XML для моего PreferenceActivity
, Это не мой полный xml, для простоты убрал все остальные мои предпочтения. Опять же, обязательно обратите внимание на название пакета, на заказ ListPreference
На класс должен ссылаться имя пакета. Также учитывайте имена предпочтений и имена массивов, которые содержат записи и значения.
<?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>
Мой XML для строки представления списка диалога. В методе getView обязательно используйте имя этого xml-файла в строке, которая его раздувает.
<?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>
Наконец, под res/values здесь мой array.xml, который содержит имена записей и значения для ListPreference
, Опять укороченный мой для простоты.
<?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>
Это хорошо сработало для меня. Я использовал подход "Адаптер", который вставляет в представление обернутый адаптер.
Вот базовый класс обернутого адаптера:
import android.database.DataSetObserver;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListAdapter;
import android.widget.WrapperListAdapter;
class ListPrefWrapperAdapter implements WrapperListAdapter {
private ListAdapter mOrigAdapter;
public ListPrefWrapperAdapter(ListAdapter origAdapter) {
mOrigAdapter = origAdapter;
}
@Override
public ListAdapter getWrappedAdapter() {
return mOrigAdapter;
}
@Override
public boolean areAllItemsEnabled() {
return getWrappedAdapter().areAllItemsEnabled();
}
@Override
public boolean isEnabled(int position) {
return getWrappedAdapter().isEnabled(position);
}
@Override
public void registerDataSetObserver(DataSetObserver observer) {
getWrappedAdapter().registerDataSetObserver(observer);
}
@Override
public void unregisterDataSetObserver(DataSetObserver observer) {
getWrappedAdapter().unregisterDataSetObserver(observer);
}
@Override
public int getCount() {
return getWrappedAdapter().getCount();
}
@Override
public Object getItem(int position) {
return getWrappedAdapter().getItem(position);
}
@Override
public long getItemId(int position) {
return getWrappedAdapter().getItemId(position);
}
@Override
public boolean hasStableIds() {
return getWrappedAdapter().hasStableIds();
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
return getWrappedAdapter().getView(position, convertView, parent);
}
@Override
public int getItemViewType(int position) {
return getWrappedAdapter().getItemViewType(position);
}
@Override
public int getViewTypeCount() {
return getWrappedAdapter().getViewTypeCount();
}
@Override
public boolean isEmpty() {
return getWrappedAdapter().isEmpty();
}
}
Вот базовый класс CustomListPreference, который использует ListPrefWrapperAdapter:
import android.app.AlertDialog;
import android.content.Context;
import android.os.Bundle;
import android.util.AttributeSet;
import android.widget.ListAdapter;
import android.widget.ListView;
public class CustomListPreference extends ListPreference {
public CustomListPreference(Context context) {
super(context);
}
public CustomListPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void showDialog(Bundle state) {
super.showDialog(state);
AlertDialog dialog = (AlertDialog) getDialog();
ListView listView = dialog.getListView();
ListAdapter adapter = listView.getAdapter();
final ListPrefWrapperAdapter fontTypeAdapter = createWrapperAdapter(adapter);
// Adjust the selection because resetting the adapter loses the selection.
int selectedPosition = findIndexOfValue(getValue());
listView.setAdapter(fontTypeAdapter);
if (selectedPosition != -1) {
listView.setItemChecked(selectedPosition, true);
listView.setSelection(selectedPosition);
}
}
protected ListPrefWrapperAdapter createWrapperAdapter(ListAdapter origAdapter) {
return new ListPrefWrapperAdapter(origAdapter);
}
}
Наконец, вот производные классы, которые делают отключение и включение определенных строк:
import android.content.Context;
import android.graphics.Color;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckedTextView;
import android.widget.ListAdapter;
public class FontTypePreference extends CustomListPreference {
public FontTypePreference(Context context) {
super(context);
}
public FontTypePreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected ListPrefWrapperAdapter createWrapperAdapter(ListAdapter origAdapter) {
return new Adapter(origAdapter);
}
private class Adapter extends ListPrefWrapperAdapter {
private static final float TEXT_SIZE = 25.0f;
private static final int STARTING_UPGRADE_REQUIRED_INDEX = 8;
public Adapter(ListAdapter origAdapter) {
super(origAdapter);
}
@Override
public boolean areAllItemsEnabled() {
return false;
}
@Override
public boolean isEnabled(int position) {
return position < STARTING_UPGRADE_REQUIRED_INDEX;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
CheckedTextView textView = (CheckedTextView) getWrappedAdapter()
.getView(position, convertView, parent);
textView.setTextColor(position < STARTING_UPGRADE_REQUIRED_INDEX ?
Color.BLACK : Color.RED);
return textView;
}
}
}
Я тестировал этот код только на SDK версии 15 и выше.
Наверное, придется добавить editor.commit()
; после каждого editor.putInt(...)
Спасибо Бобу за этот ответ и Вамси за попытку исправить ошибку с дублирующимися записями, но исправление Вамси мне не помогло. Мне пришлось сохранить массив представлений и вернуть его на позицию, если она уже была создана ранее. Итак, вот мой полный класс CustomListPreferenceAdapter. Он также содержит исправление для проверки выбранного значения предпочтения.
private class CustomListPreferenceAdapter extends BaseAdapter
{
View[] Views;
public CustomListPreferenceAdapter(Context context)
{
Views = new View[entries.length];
}
public int getCount()
{
return entries.length;
}
public Object getItem(int position)
{
return null;
}
public long getItemId(int position)
{
return position;
}
public View getView(final int position, View convertView, ViewGroup parent)
{
View row = Views[position];
CustomHolder holder = null;
if(row == null)
{
row = mInflater.inflate(R.layout.listrow, parent, false);
holder = new CustomHolder(row, position);
row.setTag(holder);
Views[position] = row;
}
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);
if(getPersistedString("").compareTo((String)entryValues[position])==0)
rButton.setChecked(true);
rButtonList.add(rButton);
}
}
}
Функция getcount()
возвращается неправильно.
public int getCount()
{
return entries.length;
}
public Object getItem(int position)
{
return null;
}
public long getItemId(int position)
{
return position;
}
Это сработало для меня, но не сработало, если список не помещается на экране (и требует прокрутки). Мне понадобилось много времени, чтобы найти решение (но я наконец-то нашел).
Первая проблема: как описано здесь: getView вызывается с неправильной позицией при быстрой прокрутке, вы получите непредсказуемое поведение при использовании прослушивателя onclick в:
public View getView(final int position, View convertView, ViewGroup parent)
В моем случае событие onClick будет сохранено в памяти и будет выполнено, когда пользователь попытается прокрутить (слегка).
А теперь решение: поместите слушатель onClick в основной класс (по крайней мере, это сработало для меня):
public class CustomListPreference extends ListPreference {
// Other code (see above)
@Override
protected void onPrepareDialogBuilder(Builder builder)
{
builder.setPositiveButton(null, null);
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 position)
{
// Code here, using position to indicate the row that was clicked...
dialog.dismiss();
}
});
}
Потратьте на это слишком много времени, так что надеюсь, что это кому-нибудь поможет:)
В общем, все еще очень доволен этим примером кода! (используйте его как палитру цветов).
PS Если вам понравился этот пост, пожалуйста, проголосуйте за полезное. Спасибо!
Я думаю, что вы можете достичь именно того, что вы хотите, установив флаг включения ListPreference в false:
ListPreference lp = (ListPreference) findPreference("YOUR_KEY");
lp.setEnabled(false);
Это затеняет описание и делает его недоступным для выбора.
Вы можете сделать это более легко.
шаги:
Расширить ListPreference
public class CustomListPreference extends ListPreference { Context mContext; public CustomListPreference(Context context, AttributeSet attrs) { super(context, attrs); mContext = context; } }
Переопределите onPrepareDialogBuilder и замените mBuilder в DialogPreference на ProxyBuilder:
@Override protected void onPrepareDialogBuilder(android.app.AlertDialog.Builder builder){ super.onPrepareDialogBuilder(builder); if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.FROYO) { return; } // Inject Builder Proxy for intercepting of getView. try { Field privateBuilderField = DialogPreference.class.getDeclaredField("mBuilder"); privateBuilderField.setAccessible(true); privateBuilderField.set(this, new ProxyBuilder(mContext, (android.app.AlertDialog.Builder)privateBuilderField.get(this))); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } }
Обработка getView в ProxyBuilder->AlertDialog->onShow->getListView->Adapter
private class ProxyBuilder extends android.app.AlertDialog.Builder{ android.app.AlertDialog.Builder mBuilder; private ProxyBuilder(Context context, AlertDialog.Builder builder) { super(context); mBuilder = builder; } @TargetApi(Build.VERSION_CODES.FROYO) @Override public AlertDialog create() { AlertDialog alertDialog = mBuilder.create(); alertDialog.setOnShowListener(new DialogInterface.OnShowListener() { @Override public void onShow(DialogInterface dialog) { ListView listView = ((AlertDialog)dialog).getListView(); final ListAdapter originalAdapter = listView.getAdapter(); listView.setAdapter(new ListAdapter(){ @Override public int getCount() { return originalAdapter.getCount(); } @Override public Object getItem(int id) { return originalAdapter.getItem(id); } @Override public long getItemId(int id) { return originalAdapter.getItemId(id); } @Override public int getItemViewType(int id) { return originalAdapter.getItemViewType(id); } @Override public View getView(int position, View convertView, ViewGroup parent) { View view = originalAdapter.getView(position, convertView, parent); TextView textView = (TextView)view; textView.setTextColor(Color.RED); return view; } @Override public int getViewTypeCount() { return originalAdapter.getViewTypeCount(); } @Override public boolean hasStableIds() { return originalAdapter.hasStableIds(); } @Override public boolean isEmpty() { return originalAdapter.isEmpty(); } @Override public void registerDataSetObserver(DataSetObserver observer) { originalAdapter.registerDataSetObserver(observer); } @Override public void unregisterDataSetObserver(DataSetObserver observer) { originalAdapter.unregisterDataSetObserver(observer); } @Override public boolean areAllItemsEnabled() { return originalAdapter.areAllItemsEnabled(); } @Override public boolean isEnabled(int position) { return originalAdapter.isEnabled(position); } }); } }); return alertDialog; } }
Изменил код, как показано ниже -
if(row == null) {
row = mInflater.inflate(R.layout.custom_list_preference_row, parent, false);
holder = new CustomHolder(row, position);
} else {
holder = row.getTag()
}
// update the holder with new Text/Drawables etc.,
row.setTag(holder);
return row;
PS - NidhiGondhia запросила модифицированный код, так как в комментариях этого не может быть, обновив модифицированный код здесь.