自定义行在listPreference?
我想创build一个ListPreference,但不知何故禁用其中的一个项目。 有点像灰色的东西,而不是有能力select它。 这将是一个即将到来的function,我希望它在列表中不能select。
我创build了一个自定义的ListPreference类,并在该类中定制了一个适配器,希望能够使用适配器来创build我想要的东西。
代码工作,它设置适配器,但没有适配器function被调用。 我在方法上设置了断点,比如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; } } }
好的,我主要是这样做的。 我不得不使用扩展ListPreference的自定义类。 然后在里面,我不得不创build一个自定义的适配器类,就像你为一个ListView,并使用builder.setAdapter()将其设置为生成器。 我还必须定义单选button和处理取消选中单选button等的ListView行的侦听器。 我唯一的问题仍然是,我的自定义ListPreference有一个确定和取消button,其中一个ListPreference只有取消button。 我不知道如何删除确定button。 此外,我不能让行突出显示,当我点击他们像他们在一个正常的ListPreference。
自定义ListPreference类的java代码。 请务必注意包装名称,首选项名称(键),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(); } } }); } } } }
我的PreferenceActivity的xml。 这不是我的完整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(); } }
这是使用ListPrefWrapperAdapter的CustomListPreference基类:
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及以上版本上testing过这个代码。
可能要添加editor.commit()
; 之后每个editor.putInt(...)
函数getcount()
返回是错误的。
public int getCount() { return entries.length; } public Object getItem(int position) { return null; } public long getItemId(int position) { return position; }
感谢鲍勃的答案,Vamsi试图纠正重复的条目错误,但Vamsi的修复不适合我。 如果之前已经创build过,我必须保留一系列视图并将其返回到位置。 所以这里是我的完整的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); } } }
我想你可以通过将ListPreference的enabled标志设置为false来实现你想要的:
ListPreference lp = (ListPreference) findPreference("YOUR_KEY"); lp.setEnabled(false);
这灰色描述,使其不可select。
修改代码如下 –
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并用ProxyBuilderreplaceDialogPreference中的mBuilder:
@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(); } }
-
在ProxyBuilder-> AlertDialog-> onShow-> getListView-> Adapter中处理getView
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; } }
这对我工作,但如果列表不适合在屏幕上(并需要滚动),它不工作。 我花了很长时间find解决scheme(但我终于做到了)。
首先是问题:如下所述: 当快速滚动时,getView调用错误的位置,当您在以下位置使用onclick监听器时,您将获得不可预知的行为:
public View getView(final int position, View convertView, ViewGroup parent)
在我的情况下,onClick事件将被存储在内存中,并将在用户尝试滚动(略)时执行。
现在解决scheme:把onClick监听器放在主类(至less这对我有效):
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(); } }); }
花太多的时间在这个,所以希望它会帮助别人:)
总而言之,还是真的很满意这个代码示例! (使用它作为颜色select器)。
PS如果你喜欢这篇文章,请投票有用。 谢谢!