【问题标题】:custom row in a listPreference?listPreference 中的自定义行?
【发布时间】:2011-05-31 18:55:12
【问题描述】:

我正在尝试创建一个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;
        }
    }
}

【问题讨论】:

  • 你是如何使用这个 CustomListPreference 的?您是否从 xml 为您的 PreferenceActivity 膨胀 ui?用代码构建它?
  • 我正在通过 xml 定义我的 PreferenceActivity 的首选项,并且在那个 xml 中我有一个项目是这个自定义 listPreference,使用完整的包名称来引用这个类。
  • 更新:我通过使用 builder.setSingleChoiceItems() 并传入我的适配器作为第一个参数来使适配器工作。这个方法被重载了几次,所以如果你正在阅读这个,只需在文档中查找它。现在正在努力将它们捆绑在一起。

标签: android listadapter listpreference


【解决方案1】:

好的,我主要是让这个工作。我必须使用扩展ListPreference 的自定义类。然后在其中我必须创建一个自定义适配器类,就像您为ListView 所做的一样,并使用builder.setAdapter() 将其设置为构建器。我还必须为 RadioButtonsListView 行定义侦听器,这些行处理取消选中 RadioButtons 等。我仍然遇到的唯一问题是,我的自定义 ListPreference 有一个确定和一个取消按钮,而 ListPreference 只有一个取消按钮。我不知道如何删除确定按钮。此外,当我像在常规 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 方法中,请务必在扩展 this 的行中使用此 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>

【讨论】:

  • 删除确定按钮放“builder.setPositiveButton(null, null);”就在“protected void onPrepareDialogBu​​ilder(Builder builder) {”之后
  • 这是完美的工作,直到项目的数量(在你的情况下为 4)超过屏幕大小;你能看一次吗?在元素超过屏幕大小的情况下,旧元素会重复而不是显示下一个元素
  • 条目数超过屏幕可容纳条目时重复条目已解决,过段时间会贴出代码
  • 选中的单选按钮不起作用。并且它未选中!如何解决这个问题?!
  • 这是有问题的模拟项目。问题是选中的单选按钮不起作用s1.picofile.com/file/7491144301/…
【解决方案2】:

这对我来说效果很好。我使用了一种将包装好的适配器注入视图的适配器方法。

这是基础包装的适配器类:

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 及更高版本上测试过此代码。

【讨论】:

    【解决方案3】:

    这对我有用,但如果列表不适合屏幕(并且需要滚动),则效果不佳。我花了很长时间才找到解决方案(但我终于找到了)。

    首先是问题: 如此处所述:getView called with wrong position when scrolling fast 当您在以下位置使用 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();
            }
        });
    
    }
    

    花太多时间在这上面,所以希望它能帮助别人:)

    总而言之,仍然对这个代码示例感到非常满意! (将其用作颜色选择器)。

    附:如果你喜欢这篇文章,请投票有用。谢谢!

    【讨论】:

      【解决方案4】:

      您可以更轻松地做到这一点。

      步骤:

      1. 扩展列表偏好

        public class CustomListPreference extends ListPreference
        {
            Context mContext;
        
            public CustomListPreference(Context context, AttributeSet attrs)
            {
                super(context, attrs);
                mContext = context;
            }
        }
        
      2. 重写onPrepareDialogBu​​ilder并将DialogPreference中的mBuilder替换为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();
            }
        }
        
      3. 在ProxyBuilder中处理getView->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;
            }
        }
        

      【讨论】:

        【解决方案5】:

        感谢 Bob 的回答,感谢 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);
                }
            }
        }
        

        【讨论】:

          【解决方案6】:

          修改代码如下 -

          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 请求修改代码,因为在 cmets 中这不适合,在此处更新修改后的代码。

          【讨论】:

          【解决方案7】:

          我认为你可以通过将 ListPreference 的启用标志设置为 false 来实现你想要的:

          ListPreference lp = (ListPreference) findPreference("YOUR_KEY");
          lp.setEnabled(false);
          

          这会使描述变灰并使其无法选择。

          【讨论】:

          • 这将禁用整个 ListPreference,并且 OP 只想禁用 ListPreference 内的某些项目
          【解决方案8】:

          函数getcount()返回错误。

          public int getCount()
              {
                  return entries.length;
              }
          
              public Object getItem(int position)
              {
                  return null;
              }
          
              public long getItemId(int position)
              {
                  return position;
              }
          

          【讨论】:

          【解决方案9】:

          可能要加editor.commit();在每个editor.putInt(...)之后

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-05-26
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多