web-dev-qa-db-ja.com

ListPreference:エントリとして文字列配列を使用し、エントリ値として整数配列を使用できない

Settings.xmlファイルでListPreferenceを使用しています。ユーザーに選択する3つのオプションのリストを表示したい。ユーザーが[設定]のオプションのいずれかを選択すると、次のエラーが表示されます。

Java.lang.NullPointerException
    at Android.preference.ListPreference.onDialogClosed(ListPreference.Java:264)
    at Android.preference.DialogPreference.onDismiss(DialogPreference.Java:381)
    at Android.app.Dialog$ListenersHandler.handleMessage(Dialog.Java:1228)
    at Android.os.Handler.dispatchMessage(Handler.Java:99)
    at Android.os.Looper.loop(Looper.Java:137)
    at Android.app.ActivityThread.main(ActivityThread.Java:4424)
    at Java.lang.reflect.Method.invokeNative(Native Method)
    at Java.lang.reflect.Method.invoke(Method.Java:511)
    at com.Android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.Java:784)
    at com.Android.internal.os.ZygoteInit.main(ZygoteInit.Java:551)
    at dalvik.system.NativeStart.main(Native Method)

これはListPreferenceのコードです:

<ListPreference
    Android:entries="@array/date_alignement"
    Android:entryValues="@array/date_alignement_values"
    Android:key="settings_date_alignement"
    Android:summary="@string/settings_date_alignement_summary"
    Android:title="@string/settings_date_alignement_title" />

そして、ここに私がエントリを埋めるために使用する配列があります:

<string-array name="date_alignement">
    <item>"Top"</item>
    <item>"Center"</item>
    <item>"Bottom"</item>
</string-array>
<integer-array name="date_alignement_values">
    <item >0</item>
    <item >1</item>
    <item >2</item>
</integer-array>

OnSharedPreferenceChangedで、これらの値を次のように使用します。

@Override
public void onSharedPreferenceChanged(
            SharedPreferences sharedPreferences, String key) {          

        //Text
        mAlignment =  mPrefs.getInt(PREF_ALIGNMENT, 1);
        switch (mAlignment) {
        case 0:
            offsetY = mHeight/3.0f;
            break;

        case 2:
            offsetY = mHeight*2.0f/3.0f;
            break;

        default:
            offsetY = mHeight/2.0f;
            break;
        }
}

entryValuesに別の文字列配列を使用すると、機能します。たとえば、値とエントリとして同じ文字列配列を使用する場合:

    Android:entries="@array/date_alignement"
    Android:entryValues="@array/date_alignement"

その後、コードを少し変更する必要がありますが、動作します:

        if(mAlignment.equalsIgnoreCase("center")) {
            offsetY = mHeight/2.0f;
        } else if(mAlignment.equalsIgnoreCase("top")) {
            offsetY = mHeight/3.0f;
        } else if(mAlignment.equalsIgnoreCase("bottom")) {
            offsetY = mHeight*2.0f/3.0f;
        }

ListPreferenceのエントリと値に文字列配列と整数配列を使用できないのはなぜですか?

69
Santacrab

答えは簡単です。なぜなら、Androidはこのように設計されているからです。エントリとエントリ値の両方にString配列を使用するだけです。それですべてです。これは、Integer.parseInt()メソッドを使用してStringintに簡単に変換できるためです。

82
Egor

答えは 環境設定ドキュメントのリスト にリストされています。

int findIndexOfValue (String value)

は指定された値のインデックスを返し、引数は文字列として取得されます。したがって、entryValues配列はこの作業を行うために文字列配列である必要があります。

3
kasper

エントリ値を文字列に変換してListPreferenceを維持し、永続データストアと通信するときにそれらをintsに変換できます。

  1. エントリ値を設定するときは、intの代わりに文字列を使用します。"1", "2", "3"など
  2. 値をintとして保持するカスタムIntListPreferenceを作成します
  3. あなたのpreferences.xmlファイル、<ListPreference><your.app.package.IntListPreference>

IntListPreference.Java

以下に実装例を示します。 AndroidX Preference 1.0.0でテストおよび動作しています。

public class IntListPreference extends ListPreference {

    public IntListPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    public IntListPreference(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public IntListPreference(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public IntListPreference(Context context) {
        super(context);
    }

    @Override
    protected boolean persistString(String value) {
        int intValue = Integer.parseInt(value);
        return persistInt(intValue);
    }

    @Override
    protected String getPersistedString(String defaultReturnValue) {
        int intValue;

        if (defaultReturnValue != null) {
            int intDefaultReturnValue = Integer.parseInt(defaultReturnValue);
            intValue = getPersistedInt(intDefaultReturnValue);
        } else {
            // We haven't been given a default return value, but we need to specify one when retrieving the value

            if (getPersistedInt(0) == getPersistedInt(1)) {
                // The default value is being ignored, so we're good to go
                intValue = getPersistedInt(0);
            } else {
                throw new IllegalArgumentException("Cannot get an int without a default return value");
            }
        }

        return Integer.toString(intValue);
    }

}
1
Sam