пользовательская строка в listPreference? - PullRequest
21 голосов
/ 29 декабря 2010

Я пытаюсь создать 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 ]

33 голосов
/ 30 декабря 2010

ОК, я получил это на работу, в основном.Мне пришлось использовать пользовательский класс, который расширяет 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>

My 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>
5 голосов
/ 01 февраля 2015

Это хорошо сработало для меня.Я использовал подход «Адаптер», который внедряет в представление упакованный адаптер.

Вот базовый класс упакованного адаптера:

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 и выше.

2 голосов
/ 23 апреля 2012

Вероятно, придется добавить editor.commit(); после каждого editor.putInt(...)

1 голос
/ 02 октября 2013

Спасибо Бобу за этот ответ и Вамси за попытку исправить ошибку с дублирующимися записями, но исправление Вамси мне не помогло. Мне пришлось сохранить массив представлений и вернуть его на позицию, если она уже была создана ранее. Итак, вот мой полный класс 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);
        }
    }
}
1 голос
/ 03 ноября 2011

функция getcount() возвращает неправильно.

public int getCount()
    {
        return entries.length;
    }

    public Object getItem(int position)
    {
        return null;
    }

    public long getItemId(int position)
    {
        return position;
    }
0 голосов
/ 10 октября 2014

Это сработало для меня, но не сработало, если список не помещается на экране (и требует прокрутки).Мне понадобилось много времени, чтобы найти решение (но я наконец-то нашел).

Первая проблема: Как описано здесь: 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 Если вам понравился этот пост, пожалуйста, проголосуйте за полезное.Thx!

0 голосов
/ 24 ноября 2013

Вы можете сделать это проще.

Шаги:

  1. Расширить список предпочтений

    public class CustomListPreference extends ListPreference
    {
        Context mContext;
    
        public CustomListPreference(Context context, AttributeSet attrs)
        {
            super(context, attrs);
            mContext = context;
        }
    }
    
  2. Переопределить 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();
        }
    }
    
  3. Обработка 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;
        }
    }
    
0 голосов
/ 26 сентября 2013

изменил код, как показано ниже -

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 запросила модифицированный код, так как в комментариях это может не поместиться, обновив здесь модифицированный код.

0 голосов
/ 07 января 2013

Я думаю, что вы можете достичь именно того, что вы хотите, установив флаг включения ListPreference в false:

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

Это скрывает описание и делает его недоступным для выбора.

...