[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)

まとめ

いろいろ調べて書いてきましたが結論として
できるだけシンプルに間違った書き方ができない、しずらいコードを目指すと
このサンプルの実装が今の所シンプルで良かったですということです。
さすが公式サンプルでした。

https://github.com/android/architecture-components-samples/blob/master/ViewBindingSample/app/src/main/java/com/android/example/viewbindingsample/BindFragment.kt#L36-L41

/**
 * 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()
    }
}