пользовательская строка в listPreference?
Я пытаюсь создать ListPreference
но как-то отключить один из элементов. Вроде как серый или что-то в этом роде и не имеет возможности выбрать его. Это будет предстоящая функция, и я хочу, чтобы она была в списке просто не выбирается.
Я создал пользовательский ListPreference
class и в этом классе пользовательский адаптер, надеясь использовать адаптер для создания того, что я хочу.
код работает, и он устанавливает адаптер, но ни одна из функций адаптера не вызывается. Я точки останова в методах, такие как 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
имеет как OK, так и кнопку отмены, где ListPreference
есть только кнопка отмены. Я не знаю, как удалить кнопку OK. Кроме того, я не могу получить строки, чтобы выделить, когда я нажимаю на них, как они делают в обычном 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, вынул все мои другие элементы предпочтений для простоты. Снова быть обязательно запомните имя пакета, custom 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 вот мой массив.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 и выше.
функции getcount()
возвращает неправильно.
public int getCount()
{
return entries.length;
}
public Object getItem(int position)
{
return null;
}
public long getItemId(int position)
{
return position;
}
спасибо Бобу за этот ответ, и Vamsi за попытку исправить ошибку повторяющихся записей, но исправление Vamsi не сработало для меня. Я должен был сохранить массив представлений и вернуть его на позицию, если он уже был создан раньше. Итак, вот мой полный класс 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);
}
}
}
Я думаю, вы можете достичь именно того, что хотите, установив флаг enabled ListPreference в false:
ListPreference lp = (ListPreference) findPreference("YOUR_KEY");
lp.setEnabled(false);
это серое описание и делает его не выбираемым.
изменил код, как показано ниже -
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 запросил измененный код, так как в комментариях это не подходит, обновляя измененный код здесь.
вы можете сделать это более легко.
действия:
-
Расширить 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 - >адаптер
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; } }
это сработало для меня, но это не сработало хорошо, если список не подходит на экране (и требует прокрутки). Мне потребовалось много времени, чтобы найти решение (но я, наконец, это сделал).
первая проблема: Как описано здесь: 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();
}
});
}
тратить waaaay слишком много времени на это, поэтому надеюсь, что это поможет кому-то:)
в целом, все еще очень доволен этим примером кода! (используйте его как выбор цвета).
P. S. Если вам понравилась эта должность, пожалуйста, проголосуйте полезно. Thx!