[Android]Fragmentの実装の仕方について
[Android]Fragmentの実装の仕方について
Fragment正しく実装できてますか?
結構みんな間違ってFragment実装しているコードを見るので、
一度Fragmentの実装について自分なりに整理してみました。
FragmentでDatabindingを使う場合の実装について
DatabindingをFragmentのフィールドに持つ場合は必ずonDestroyViewで開放する必要があります。
https://developer.android.com/topic/libraries/view-binding#fragments
private var _binding: ResultProfileBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = ResultProfileBinding.inflate(inflater, container, false)
val view = binding.root
return view
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
でそのためにはbindingフィールドをNullableにしないといけないんですが、
Nullableにするといちいちオプショナルの解除が必要でNonNullで宣言したいんですよね。
そこで、こういう風にAutoCleardValueを使ったり
https://github.com/DroidKaigi/conference-app-2020/pull/294/files#diff-ece280c4922c43e26a5266b1a334647bR32
DataBinding-ktxを使ったり
https://github.com/wada811/DataBinding-ktx
といろいろあるわけなんですが。
調べてみてAutoCleardValueはonDestroyView以降でbindingを触るとクラッシュする可能性があるし。
DataBinding-ktxは結局使い勝手があまりNullableで宣言するのと変わらない感じがあって。
わざわざライブラリに依存してまで対応するほどなのか?という気がするし。
で、結局は普通にNullableで宣言すればいいんでは?と思ってしまいました。
ただ、絶対にonDestroyViewで開放忘れを無くしたいぜって場合はDataBinding-ktxがいいとは思います。
Fragmentでのviewのinfrateについて
FragmentのviewをinfrateするのにonCreateViewをoverrideしますが。
実はAndroidXのFragmentは下記のようにコンストラクタに直接LayoutXMLを渡せて、onCreateViewをoverrideする必要がない書き方ができます。
https://developer.android.com/reference/androidx/fragment/app/Fragment#Fragment(int)
class MainFragment : Fragment(R.layout.main_fragment){
}
この書き方だとonCreateViewでinfrate以外のViewの初期化をしたりといった非推奨な書き方をしないように、onCreateViewをそもそも書かないので強制できるわけです。
https://developer.android.com/reference/androidx/fragment/app/Fragment#onCreateView(android.view.LayoutInflater,android.view.ViewGroup,android.os.Bundle)
ただこれだとDatabindingのインスタンスはどうやって取得するのか?となるんですが。
onViewCreatedで下記のようにviewRootからDatabindingのインスタンスが取得できるようになっています。
https://developer.android.com/topic/libraries/data-binding/generated-binding#create
val binding: MyLayoutBinding = MyLayoutBinding.bind(viewRoot)
まとめ
いろいろ調べて書いてきましたが結論として
できるだけシンプルに間違った書き方ができない、しずらいコードを目指すと
このサンプルの実装が今の所シンプルで良かったですということです。
さすが公式サンプルでした。
/**
* View Binding example with a fragment that uses the alternate constructor for inflation and
* [onViewCreated] for binding.
*/
class BindFragment : Fragment(R.layout.fragment_blank) {
// Scoped to the lifecycle of the fragment's view (between onCreateView and onDestroyView)
private var fragmentBlankBinding: FragmentBlankBinding? = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val binding = FragmentBlankBinding.bind(view)
fragmentBlankBinding = binding
binding.textViewFragment.text = getString(string.hello_from_vb_bindfragment)
}
override fun onDestroyView() {
// Consider not storing the binding instance in a field, if not needed.
fragmentBlankBinding = null
super.onDestroyView()
}
}