web-dev-qa-db-ja.com

listPreferenceのカスタム行?

ListPreferenceを作成しようとしていますが、どういうわけかアイテムの1つを無効にします。灰色か何かのようなもので、それを選択する能力がありません。これは今後の機能であり、選択できないようにリストに含めたいと思います。

カスタムListPreferenceクラスを作成し、そのクラスにカスタムアダプターを作成しました。アダプターを使用して、必要なものを作成したいと考えています。

コードは機能し、アダプターを設定しますが、アダプター関数は呼び出されません。 getCount()などのメソッドにブレークポイントを設定しましたが、呼び出されません。

これが私のコードです。 http://blog.350Nice.com/wp/archives/24 から取得したカスタムListPreference

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;
        }
    }
}
21
Bob

OK私はこれをほとんど機能させました。 ListPreferenceを拡張するカスタム定義のクラスを使用する必要がありました。次に、その中でListViewの場合と同じようにカスタムアダプタクラスを作成し、builder.setAdapter()を使用してビルダーに設定する必要がありました。また、RadioButtonsなどのチェック解除を処理するListView行とRadioButtons行の両方のリスナーを定義する必要がありました。私がまだ抱えている唯一の問題は、カスタムListPreferenceには[OK]ボタンと[キャンセル]ボタンの両方があり、ListPreferenceにはキャンセルボタンしかないということです。 OKボタンを外す方法がわかりません。また、通常の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の下に、ListPreferenceのエントリ名と値を含むarray.xmlがあります。繰り返しますが、簡単にするために鉱山を短縮しました。

<?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>
33
Bob

これは私にとってうまくいきました。ラップされたアダプターをビューに挿入するアダプターアプローチを使用しました。

基本ラップされたアダプタクラスは次のとおりです。

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以降でのみテストしました。

5
Roger Jack

おそらくeditor.commit()を追加する必要があります。各editor.putInt(...)の後

2
Amitav Paul

function getcount()が返すのは間違っています。

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

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

    public long getItemId(int position)
    {
        return position;
    }
1
hyongbai

その答えをボブに感謝し、重複エントリのバグを修正しようとして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);
        }
    }
}
1
Xav

以下のようにコードを変更しました-

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
Vamsi

あなたはそれをより簡単に行うことができます。

手順:

  1. ListPreferenceを拡張する

    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-> 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;
        }
    }
    
0
galex

ListPreferenceのenabledフラグをfalseに設定することで、まさにあなたが望むことを達成できると思います。

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

これにより、説明がグレー表示され、選択できなくなります。

0
Malthan

これは私にとってはうまくいきましたが、リストが画面に収まらない場合(そしてスクロールが必要な場合)はうまくいきませんでした。解決策を見つけるのにかなりの時間がかかりました(しかし、私はついにそうしました)。

最初の問題:ここで説明されているように: 高速スクロール時に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();
        }
    });

}

これにあまりにも多くの時間を費やすので、誰かを助けることを願っています:)

全体として、このコード例にはまだ本当に満足しています! (カラーピッカーとして使用してください)。

P.S.この投稿が気に入ったら、投票してください。どうも!

0
EddyBee