[Android]SupportLibrary v23から追加されたPreferenceFragmentCompatを使ってみる

# SupportLibrary v23でPreferenceFragmentが追加された

最近のGoogleのSupportLibraryへの対応はとても親切で、今まで、API 11未満では使えなかったPreferenceFragmentがとうとう！SupportLibraryで対応されました！

今まではこんなもので代用していましたよね。お世話になりました。
https://github.com/Machinarius/PreferenceFragment-Compat

と言っても今更API 11未満に対応する必要はほとんどないわけで、どちらかというと、バージョン依存の激しいSDKではなくアプリに組み込めるSupportLibraryでいろいろな挙動を吸収しようという流れに、とうとうPreferenceFragmentも乗ることができた、というのが大事でしょう。

今まではAppCompatActivityなど、MaterialなテーマをAPI 19以前でも使えるようにするためにSupportLibraryなFragment環境のアプリだと標準のPreferenceFragmentは使えませんでしたし。。

リリースされてもう時間が経ちましたがやっと触れたのでメモしておきます。
使ったのはv23.0.1です。

# PreferenceFragmentとPreferenceFragmentCompat！？

v23からSupportLibraryはいろいろなモジュールごとに分割されましたが、その中でもPreference関連のものがv7(API 7以上向け)とv14(API 14以上向け)にわかれています。

そして、supportなPreferenceFragmentがそれぞれに存在します！

* [android.support.v7.preference.PreferenceFragmentCompat](https://developer.android.com/reference/android/support/v7/preference/PreferenceFragmentCompat.html)
* [android.support.v14.preference.PreferenceFragment](https://developer.android.com/reference/android/support/v14/preference/PreferenceFragment.html)

少しわかりにくいのですが単純な話で、PreferenceFragmentCompat(v7)はandroid.support.v4.app.Fragmentを、PreferenceFragment(v14)の方は通常のandroid.app.Fragmentを継承しています。
なので採用するアプリがどちらのFragmentを使ってるかで選ぶのが良さそうです。


# 実装の注意

## onCreatePreferences()を実装

公式リファレンスには今までのPreferenceFragment(無印)と同じように、onCreateでaddPreferencesFromResourceして実装するみたいに書いてありますが、実装してみるとすぐわかるようにabstractなonCreatePreferencesを実装する必要があります。
試しにonCreateでなくてonCreatePreferencesの中でaddPreferencesFromResourceをしたら今までどおりに動きました。

```java:PrefsFragment.java
public static class PrefsFragment extends PreferenceFragment {

// リファレンスにはこう書いてありますが、
/*
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Load the preferences from an XML resource
        addPreferencesFromResource(R.xml.preferences);
    }
*/
// こちらだけでも良さそう。
    @Override
    public void onCreatePreferences(Bundle bundle, String s) {
        addPreferencesFromResource(R.xml.preferences);
    }
}
```

メソッドの説明でonCreateで呼ばれる、って書いてあるので、リファレンスがPreferenceFragment(無印)のコピペなだけだと思います。
ちなみにPreferenceFragment(v14)の方も、この形式になっていました。

## preferenceThemeを設定

上記実装をしてこのまま実行すると、落ちますw

> java.lang.IllegalStateException: Must specify preferenceTheme in theme
       at android.support.v7.preference.PreferenceFragmentCompat.onCreate(PreferenceFragmentCompat.java:202)

ということで、preferenceThemeを自分のActivityのThemeに定義する必要があります。


```xml:style.xml
<style name="SettingsTheme" parent="Theme.AppCompat.NoActionBar">
    <item name="preferenceTheme">@style/PreferenceThemeOverlay</item>
</style>
```

これでとりあえず起動するようになりました。

## カスタマイズ。。

Preferenceのwidgetをそのまま使っているケースは少ないかと思います。
そこでカスタマイズが必要ですが、今までのxmlにandroid:layoutなどを指定していたと思いますが、Themeが追加されたため、themeでの指定が必要になりました。

```xml

    <style name="Theme.WtfPreference" parent="@style/PreferenceThemeOverlay">
        <item name="preferenceScreenStyle">@style/Preference.PreferenceScreen</item>
        <item name="preferenceFragmentStyle">@style/PreferenceFragment</item>
        <item name="preferenceCategoryStyle">@style/Preference.Category</item>
        <item name="preferenceStyle">@style/Preference</item>
        <item name="preferenceInformationStyle">@style/Preference.Information</item>
        <item name="checkBoxPreferenceStyle">@style/Preference.CheckBoxPreference</item>
        <item name="switchPreferenceCompatStyle">@style/Preference.SwitchPreferenceCompat</item>
        <item name="dialogPreferenceStyle">@style/Preference.DialogPreference</item>
        <item name="editTextPreferenceStyle">@style/Preference.DialogPreference.EditTextPreference</item>
        <item name="preferenceFragmentListStyle">@style/PreferenceFragmentList</item>
    </style>
```

サポートライブラリ内のResourcesを参照してもらえば分かりますが、以下のようにそれぞれのstyleを定義します。

```xml

    <style name="MyPreference">
        <item name="layout">@layout/custom_checkbox_layout</item>
    </style>
    <style name="MyPreference.CheckBoxPreference">
        <item name="widgetLayout">@layout/custom_checkbox_widget_layout</item>
    </style>
```

また、Supportライブラリのせいか、resourceIDがandroid.Rだったりプロジェクト内のローカルIDだったりが混在しているので注意が必要です。

上の例だと、custom_checkbox_widget_layout.xmlのCheckboxのidを、android:id/checkboxからid/checkboxに変更する必要がありました。
他の部分のidは変更不要でしたが、他のwidgetの場合も確認した方がいいと思います。

# 参考

まだあまりスレッドも立っていませんが、Themeやレイアウトの設定など、こちらが参考になりました。

[PreferenceFragmentCompat requires preferenceTheme to be set](http://stackoverflow.com/questions/32070670/preferencefragmentcompat-requires-preferencetheme-to-be-set)

デフォルトでマテリアルデザインが適応されないissueが残っている！というような回答もありました。。
実際まだOpendなissueもありそうです。(2015/9/4)

公式のGoogle+の投稿ではリファレンスの間違いが、正しい実装方法で書かれていました。

[Preferences Support Library: Preference Fragments for API 7+, no matter the Activity](https://plus.google.com/+AndroidDevelopers/posts/9kZ3SsXdT2T)