AndroidXで使えるPreferenceActivityを作った話

# PreferenceActivityって便利でしたよね

XMLで設定項目を定義することで簡単に設定画面を作ることができます。API Level 15では直接設定画面を作ることがdeprecatedになった後もPreferenceFragmentと組み合わせて使われていました。しかし、ついに**API Level 29**にて**deprecated**になってしまいました。:cry:
FragmentのほうはPreferenceFragmentCompatがAndroidXで提供されていますが、PreferenceActivityの代替手段は提供されていません。

無いのなら作ってしまえばいいということで（少し前に）作りました。`PreferenceActivityCompat`です。

https://github.com/ohmae/preference-activity-compat

jCenterに登録していますので以下だけでつかえます。

```gradle
implementation 'net.mm2d:preference:0.1.2'
```

Android 4.x上でもマテリアルデザインが適用されます。

|PreferenceActivity|PreferenceActivityCompat|
|:--|:--|
|![4N4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155171/b5141c66-e684-7086-4481-c2ef37c151ae.png)|![7C4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155171/b26da1a3-fc6e-c3ae-8b87-81d8ccd3f5b4.png)|

元々のPreferenceActivtyの全機能をサポートしているわけではなく、`preference-headers`を定義して`PreferenceFragment`を呼び出していたところを、`PreferenceFragmentCompat`を呼び出せるようにしたものです。
また、実際の設定項目などの部分は`PreferenceFragmentCompat`にお任せで、`PreferenceFragmentCompat`自体の改善を行うものではありません。

# 使い方

少し前まで、Android StudioでSettings Activityを作成すると勝手に作られていた`AppCompatPreferenceActivity`を使った設定画面を拙作の`PreferenceActivityCompat`に置き換えるという形で説明していきます。（ほぼReadmeの翻訳）

### 1. `preferenceTheme`を`PreferenceActivity`のテーマに追加
```xml
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>

+        <item name="preferenceTheme">@style/PreferenceCompatTheme</item>
    </style>
```

### 2. `SettingsActivity`の親クラスを書き換え

```java
- public class SettingsActivity extends AppCompatPreferenceActivity {
+ public class SettingsActivity extends PreferenceActivityCompat {
```

`isValidFragment`が`protected`で定義されていると思いますので、`public`に書き換えます。

```java
    @Override
-     protected boolean isValidFragment(final String fragmentName) {
+     public boolean isValidFragment(final String fragmentName) {
```

`onMenuItemSelected`をOverrideしている場合は、`onOptionsItemSelected`に書き換えます。

```java
    @Override
-    public boolean onMenuItemSelected(int featureId, MenuItem item) {
+    public boolean onOptionsItemSelected(final MenuItem item) {
        final int id = item.getItemId();
        if (id == android.R.id.home) {
-            if (!super.onMenuItemSelected(featureId, item)) {
+            if (!super.onOptionsItemSelected(item)) {
                NavUtils.navigateUpFromSameTask(this);
            }
            return true;
        }
-        return super.onMenuItemSelected(featureId, item);
+        return super.onOptionsItemSelected(item);
    }
```

### 3. `PreferenceFragment`を`PreferenceFragmentCompat`に書き換え

`onCreate`で書いていた処理は`onCreatePreferences`に書き換えます。

```java
-     public static class GeneralPreferenceFragment extends PreferenceFragment {
+     public static class GeneralPreferenceFragment extends PreferenceFragmentCompat {
        @Override
-         public void onCreate(final Bundle savedInstanceState) {
-             super.onCreate(savedInstanceState);
+         public void onCreatePreferences(final Bundle savedInstanceState,final String rootKey) {
            addPreferencesFromResource(R.xml.native_pref_general);
        }
```

### 4. インポートの書き換え

`android.preference.PreferenceActivity.Header`を`net.mm2d.preference.Header`に置き換えます。

```java
- import android.preference.PreferenceActivity.Header;
+ import net.mm2d.preference.Header;
```

`android.preference`を`androidx.preference`に書き換えます。

```java
- import android.preference.ListPreference;
- import android.preference.Preference;
- import android.preference.Preference.OnPreferenceChangeListener;
- import android.preference.PreferenceFragment;
- import android.preference.PreferenceManager;
+ import androidx.preference.ListPreference;
+ import androidx.preference.Preference;
+ import androidx.preference.Preference.OnPreferenceChangeListener;
+ import androidx.preference.PreferenceFragmentCompat;
+ import androidx.preference.PreferenceManager;
```

### 5. xmlのnamespaceの書き換え

`preference-headers`を定義しているxmlのnamespaceを`android`から`app`に書き換えます。

```xml
<preference-headers
-     xmlns:android="http://schemas.android.com/apk/res/android">
+     xmlns:app="http://schemas.android.com/apk/res-auto">
    <header
-         android:fragment="net.mm2d.preference.sample.SettingsActivity$GeneralPreferenceFragment"
-         android:icon="@drawable/ic_info_black_24dp"
-         android:title="@string/pref_header_general"
+         app:fragment="net.mm2d.preference.sample.SettingsActivity$GeneralPreferenceFragment"
+         app:icon="@drawable/ic_info_black_24dp"
+         app:title="@string/pref_header_general"
        />
```

### 6. `SwtichPreference`を`SwitchPreferenceCompat`に書き換え（必要なら）

`PreferenceFragmentCompat`では置き換え可能というだけなので、`PreferenceActivityCompat`は関係無いです。

```xml
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
-     <SwitchPreference
+     <!--suppress AndroidElementNotAllowed -->
+     <SwitchPreferenceCompat
        android:defaultValue="true"
        android:key="example_switch"
        android:summary="@string/pref_description_social_recommendations"
        android:title="@string/pref_title_social_recommendations"
        />
```

なぜ置き換えるか、というと、Android 4.4 以前ではSwitchPreferenceのままではマテリアルデザインにならないからです。

|SwitchPreference|SwitchPreferenceCompat|
|:-:|:-:|
|![switch-preference.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155171/57123b3f-cdd5-0d80-bcce-9aab25388962.png)|![switch-preference-compat.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155171/6c66c44e-9314-9e43-c6db-038ec1c3a70c.png)|

ただ、警告が出ちゃうのが美しくないです:sweat:

### 7. アイコン用のmarginが不要なら`app:iconSpaceReserved="false"`を追加

こちらも`PreferenceFragmentCompat`を使う上での話で`PreferenceActivityCompat`は関係無いです。

```xml
    <Preference
        android:title="@string/pref_title_system_sync_settings"
+         app:iconSpaceReserved="false"
        >
```

デフォルトでアイコン分の余白が出るようになっちゃったんですよね。`app:iconSpaceReserved="false"`で詰めることができます。

|default|`app:iconSpaceReserved="false"`|
|:-:|:-:|
|![icon-space-reserved-true.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155171/4a0020a9-caaf-369e-c337-9c703d0d97c0.png)|![icon-space-reserved-false.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155171/79d84225-1b94-4da2-1483-10155d1fc5d5.png)|

# まとめ

かつてのPreferenceActivityを延命させられるかもしれないライブラリを作ってみたというご紹介でした。