PreferenceFragment で RadioButton を実現する

PreferenceFragment で RadioButton を実現する

Android の設定画面に表示した項目の一覧から項目を一つ選択したい。
例: アカウント一覧からアカウントを選択したい場合など。
![PreferenceFragmentRadioButton.png](https://qiita-image-store.s3.amazonaws.com/0/19840/e8f972a7-3281-506d-83f4-96d550448e36.png)

Android Framework の Preference 関連のクラスには RadioButton が用意されていないので下記の実装が必要になる。

# 雛形作成

Eclipse で Android 4.0.3(API Level: 15)以上を対象にした Android プロジェクトを新規作成する。
Android プロジェクトには、1つの PreferenceMainActivity の継承クラスと1つの PreferenceFragment の継承クラスが含まれる。

雛形 Eclipse プロジェクトは、下記の GitHub URL を参照のこと。

<https://github.com/miyagi389/org-mayaa-android-examples/commit/54817924908e0945bc43143ecfda2c72333cabee>

# Account 一覧を表示

DB(にあると仮定する)からアカウント情報を読み込んで PreferenceFragment に追加する。
アカウント情報1件ごとに CheckBoxPreference のインスタンスを生成して親の PreferenceScreen に追加する。

```java:
    public class PreferenceAccountFragment extends PreferenceFragment {

        private PreferenceScreen mRootPreferenceScreen;

        private String mSelectAccount;

        @Override
        public void onCreate(final Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            mRootPreferenceScreen = getPreferenceManager().createPreferenceScreen(getActivity());
            setPreferenceScreen(mRootPreferenceScreen);
        }

        @Override
        public void onResume() {
            super.onResume();
            loadSelectAccount();
            removeAllPreferenceScreen();
            buildAccountsPreferences();
        }

        private void removeAllPreferenceScreen() {
            mRootPreferenceScreen.removeAll();
        }

        private void buildAccountsPreferences() {
            for (final String account : Constants.ACCOUNTS) {
                final Preference preference = buildAccountPreferences(account);
                mRootPreferenceScreen.addPreference(preference);
            }
        }

        private Preference buildAccountPreferences(final String account) {
            final CheckBoxPreference p = new CheckBoxPreference(getActivity());

            p.setTitle(account);

            final boolean isChecked = TextUtils.equals(mSelectAccount, account);
            p.setChecked(isChecked);

            return p;
        }

        private void loadSelectAccount() {
            mSelectAccount = getSelectAccount(getActivity());
        }

        private String getSelectAccount(final Context context) {
            final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
            return prefs.getString(context.getString(R.string.pref_account_key), null);
        }

        private void setSelectAccount(final Context context, final String value) {
            final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
            final Editor editor = prefs.edit();
            editor.putString(context.getString(R.string.pref_account_key), value);
            editor.commit();
        }
    }
```

# RadioButton の機能を実装する

CheckBoxPreference そのままの機能では、複数の項目を選択できてしまう。
そこで、 RadioButton のように 1つの項目だけを選択するようにする。

```java:
    private Preference buildAccountPreferences(final String account) {
        final CheckBoxPreference p = new CheckBoxPreference(getActivity());

        ....

        // 下記の1行を追加
        p.setOnPreferenceChangeListener(mOnPreferenceChangeListener);
    }

    private final Preference.OnPreferenceChangeListener mOnPreferenceChangeListener =
        new Preference.OnPreferenceChangeListener() {
            @Override
            public boolean onPreferenceChange(final Preference preference, final Object newValue) {
                // true -> false ignore
                if (Boolean.FALSE.equals(newValue)) {
                    return false;
                }

                // save account
                final String account = String.valueOf(preference.getTitle());
                setSelectAccount(getActivity(), account);

                // refresh accounts preference screen
                loadSelectAccount();
                removeAllPreferenceScreen();
                buildAccountsPreferences();

                return true;
            }
        };
```

# UI を CheckBox から RadioButton にする

CheckBox に RadioButton のリソースを適用する定義を作成する。

res/layout/preference_widget_checkbox.xml

```xml:
    <?xml version="1.0" encoding="utf-8"?>
    <CheckBox xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+android:id/checkbox"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:button="?android:attr/listChoiceIndicatorSingle"
        android:clickable="false"
        android:focusable="false" />
```

res/values/styles.xml

```xml:
    <?xml version="1.0" encoding="utf-8"?>
    <resources xmlns:android="http://schemas.android.com/apk/res/android">
        <style name="Preference" />
        <style name="Preference.CheckBoxPreference">
            <item name="android:widgetLayout">@layout/preference_widget_checkbox</item>
        </style>
    </resources>
```

res/values/themes.xml

```xml:
    <?xml version="1.0" encoding="utf-8"?>
    <resources xmlns:android="http://schemas.android.com/apk/res/android">
        <style name="Theme" parent="android:Theme.Holo">
            <item name="accountCheckBoxPreferenceStyle">@style/Preference.CheckBoxPreference</item>
        </style>
    </resources>
```

res/values/attrs.xml

```xml:
    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <!-- PreferenceAccountFragment accountCheckBoxPreferenceStyle -->
        <attr name="accountCheckBoxPreferenceStyle" format="reference" />
    </resources>
```

AndroidManifest.xml

```xml:
    <activity
        android:name=".PreferenceMainActivity"
        android:label="@string/app_name"
        android:theme="@style/Theme" >
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
```

CheckBoxPreference のコンストラクタで、作成した RadioButton のリソースを指定する。

```java:
    final CheckBoxPreference p = new CheckBoxPreference(getActivity(),
        null, R.attr.accountCheckBoxPreferenceStyle);
```

# 完成

完成したソースファイル一式は下記のGitHubにアップした。

<https://github.com/miyagi389/org-mayaa-android-examples/tree/master/org-mayaa-android-examples-preference-radio>