タブメニューを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