Android

Android FragmentのTips


はじめに

AndroidでFragment作成時の僕なりのTipsをまとめました。

以下についてまとめています。


  • Fragmentのインスタンス作成と値渡し

  • FragmentごとのViewModel作成

  • Fragment内Fragment

言語はKotlinでversionは1.3.30です。


Fragmentのインスタンス作成と値渡し

Fragmentクラスを継承したクラスにcompanion objectを作成しインスタンスを返すようにしています。

また、Fragmentの仕様上コンストラクタで値渡しはできないようになっているので、setArgumentsgetArgumentsを使って値渡ししています。


HogeFragment.kt

class HogeFragment : Fragment() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val position = arguments!!.getInt("position")
}

companion object {
fun newInstance(position: Int): HogeFragment {
return HogeFragment().apply {
arguments = Bundle().apply {
putInt("position", position)
}
}
}
}
}



FragmentごとにViewModel作成

AACのViewModelにふれているのでgradleを記載しておきます。


app/build.gradle

def arch_version = '1.1.1'

implementation "android.arch.lifecycle:extensions:$arch_version"
implementation "android.arch.lifecycle:reactivestreams:$arch_version"
annotationProcessor "android.arch.lifecycle:compiler:$arch_version"


失敗パターン1

FragmentStatePagerAdapterを使ってFragmentのAdapterを作成する場合、不要になったページのFragmentのインスタンスは破棄されます。

よって各FragmentでViewModel取得時にViewModelProviders.of()の引数にFragmentのインスタンスを指定するとViewModelも新しく作り直されるので、残念なことになります。


HogeFragment.kt

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)
val hogeViewModel = ViewModelProviders.of(this).get(HogeViewModel::class.java)
}


失敗パターン2

そこで、ViewModelProviders.of()の引数にActivityを指定するとViewModelのインスタンスはActivityに紐づくのでViewModelは新しく作り直されず、以前のインスタンスが返ってきます。

しかし、全てのFragmentで同じViewModelのインスタンスが返ってきてしまい、またもや残念なことになります。


HogeFragment.kt

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)
val hogeViewModel = ViewModelProviders.of(activity).get(HogeViewModel::class.java)
}


成功パターン

各Fragmentで別のViewModelのインスタンスを返すようにするには.get()にpositionをキーとして指定します。

これでFragmentごとで新しく作り直されないViewModelが返されるようになります。


HogeFragment.kt

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)
val position = arguments!!.getInt("position")
val hogeViewModel = ViewModelProviders.of(this).get(position.toString(), HogeViewModel::class.java)
}


Fragment内Fragment

例えばBottomNavigationViewのFragment内にTabLayout作る場合Fragment内にFragmentを作成することになります。

Fragment内でFragmentStatePagerAdapterのインスタンスを作成し、ViewPagerにセットする実装にどハマリしたのでそれを書きます。


Activityの場合

ActivityでFragmentStatePagerAdapterのインスタンスを作成し、ViewPagerにセットする場合、下記のように書きます。


HogeActivity.kt

val adapter = HogeFragmentPagerAdapter(supportFragmentManager)

hogeViewPager.adapter = adapter


Fragmentの場合

FragmentでFragmentStatePagerAdapterのインスタンスを作成し、ViewPagerにセットする場合の失敗パターンがこちらです。


HogeFragment.kt

val adapter = HogeFragmentPagerAdapter(fragmentManager)

hogeViewPager.adapter = adapter

fragmentManagerを引数に指定すると挙動がおかしくなりました。

ここでかなりハマりました。

下記が成功パターンです。


HogeFragment.kt

val adapter = HogeFragmentPagerAdapter(childFragmentManager)

hogeViewPager.adapter = adapter

childFragmentManagerを引数に指定すると正しい挙動になりました。