Android

Android - FragmentPagerAdapterを使っててandroid.support.v4.app.Fragmentが必要だぞと怒られた時に必要な対処法(PreferenceFragmentは使えないぽよ?)

タブメニューをFragmentPagerAdapterを使って実装し、設定画面部分をFragmentPreferenceを継承したフラグメントで作ろうとしたら「Required: android.support.v4.app.Fragment」と怒られてしまいました。解決するために試行錯誤したメモを残します!

突然の「Required: android.support.v4.app.Fragment」エラー

悪戦苦闘した経緯を簡単にご説明します。(普段はWeb開発がメインなゆえアプリ開発情弱な筆者です。)

タブメニューを作りたくアクティビティの新規作成画面で、おおこれやんとTabbed Activityを自動生成しました。

生成されたコードを見てみるとFragmentPagerAdapterが書かれていてSwitch文があったので、ふむふむとりあえずFragmentでタブを管理するのだなと。

fragment_main.xmlというファイルがあったので、ここらへんを同じようにフラグメントを作っていけばよさそう。

2画面ほどフラグメント作ってみて、おお動いたーとなって、次は設定画面を作ろうと。

Androidで設定画面をつくるには、Preferenceだ!と思いPreferenceFragmentみたいなのあるんじゃ?と調べてみました。

見事に存在したので、PreferenceFragmentを継承したSettingFragmentを作成し、いざFragmentPagerAdapterで設定しようとしたそのときまさかの「Required: android.support.v4.app.Fragment」エラーが

なんだこれは?他のフラグメントだと上手くいってるのになぜSettingFragmentだけ?

こんな感じで、PreferenceFragmentとの戦いは始まりました

実はあまり対応されていなかったPreferenceFragment事情

まあ正直なところよく理解できていないのが本音なのですが、少し調べてみた結果を書き連ねます。(正しいかどうかはわからないので、詳しい方の優しい解説お願いします)

AndroidのAPIは日に日に進化しており、新しいAPIのバージョンで開発されたライブラリは基本的に下位APIでは使えません。

そこで、下位APIをサポートするためのライブラリが提供されており、Fragmentを使うにはAndroid 1.6 (API level 4) 以降を対象としたv4 Support Libraryを使う必要があります。

いざ使おうとしたPreferenceFragment(v14)は、android.app.Fragmentを継承しています。

ここで問題となるのが、自動生成したFragmentPagerAdapterで使用されるFragmentはandroid.support.v4.app.FragmentのFragmentということです。

つまり、PreferenceFragmentはandroid.app.Fragmentを継承しているため、FragmentPagerAdapterとは違う型になってしまうのです。

Support Library v23から追加されたPreferenceFragmentCompat

以前は、Machinarius/PreferenceFragment-Compat: Unofficial PreferenceFragment compatibility layer for Android 1.6 and up.のような非公式なもので代用するしかありませんでした。

しかし、Support Library v23でPreferenceFragmentCompatが追加され、android.support.v4.app.Fragmentを継承したFragmentを作成できるようになりました。

ここで注意が必要なのが、bundle.gradleに使うサポートライブラリを明記しなければならないことです。

app/bundle.gradleに以下のコードを追加して更新しましょう。

dependencies {
    ...
    compile 'com.android.support:preference-v7:25.1.1'
    compile 'com.android.support:preference-v14:25.1.1'
    compile 'com.android.support:recyclerview-v7:25.1.1'
}

PreferenceFragmentCompatを継承したクラスの作成方法

PreferenceFragmentCompatを継承したSettingFragmentを作成していく例です。

FragmentPagerAdapterで使用できるように作っています。

public class SettingFragment extends PreferenceFragmentCompat {
    /**
     * The fragment argument representing the section number for this
     * fragment.
     */
    private static final String ARG_SECTION_NUMBER = "section_number";

    public SettingFragment() {
    }

    /**
     * Returns a new instance of this fragment for the given section
     * number.
     */
    public static SettingFragment newInstance(int sectionNumber) {
        SettingFragment fragment = new SettingFragment();
        Bundle args = new Bundle();
        args.putInt(ARG_SECTION_NUMBER, sectionNumber);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onCreatePreferences(Bundle bundle, String s) {
        addPreferencesFromResource(R.xml.preferences);
    }
}

通常のFragmentで必要なonCreateメソッドではなく、onCreatePreferencesメソッドを実装する必要があることに注意。

さらに、上記実装だけだと、下記のようなランタイムエラーが発生します。

E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.pompom, PID: 3328
java.lang.IllegalStateException: Must specify preferenceTheme in theme
at android.support.v7.preference.PreferenceFragmentCompat.onCreate(PreferenceFragmentCompat.java:210)

解決するには、preferenceThemeをActivityのThemeに定義する必要があるとのこと。

/res/values/styles.xmlに以下のように追記してください。

<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    ...
    <!-- 以下を追記 -->        
    <item name="preferenceTheme">@style/PreferenceThemeOverlay</item>
</style>

これでやっとエラーにならず実行することができました。めでたし。

まとめ

本記事では、タブメニューをFragmentPagerAdapterを使って実装しPreferenceをフラグメントで作成しようと試行錯誤して解決した一連の流れをご紹介しました。

参考にしたサイト

【Android】PreferenceActivity/PreferenceFragmentを使った設定画面 - phicdy devlog

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

Androidアプリ開発 Preference PreferenceActivity, PreferenceFragment, PreferenceFragmentCompatの違いを理解する プログラミングJava

Preference Support Library の試行錯誤メモ - exception think

-Android