2021年2月12日金曜日

viewLifecycleOwnerLiveData を使って Fragment の onDestroyView() で自動で null がセットされる ViewBinding 用の property delegates を作る

ViewBinding のドキュメントでは Fragment で使う時の実装はこのようになっています。 class LoginFragment : Fragment() { private var _binding: FragmentLoginBinding? = null private val binding: FragmentLoginBinding get() = _binding!! override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { _binding = FragmentLoginBinding.inflate(inflater, container, false) return binding.root } override fun onDestroyView() { super.onDestroyView() _binding = null } } Fragment はそのライフサイクル中にViewが破棄されることがあるので onDestroyView() で View への参照を外しておく必要があります。

Fragment.viewLifecycleOwnerLiveData で LiveData<LifecycleOwner?> が取れます」で紹介した viewLifecycleOwnerLiveData を使うと、onDestroyView() で null を代入する処理を自動でやってくれる ViewBinding 用の property delegates を作ることができます。

https://medium.com/@Zhuinden/simple-one-liner-viewbinding-in-fragments-and-activities-with-kotlin-961430c6c07c を参考に変更を加えています。 class FragmentViewBindingDelegate<T : ViewBinding>( val fragment: Fragment, val viewBindingFactory: (View) -> T ) : ReadOnlyProperty<Fragment, T> { private var binding: T? = null private val viewLifecycleOwnerObserver = Observer<LifecycleOwner?> { if (it == null) { binding = null } } private val observer = object : DefaultLifecycleObserver { override fun onCreate(owner: LifecycleOwner) { fragment.viewLifecycleOwnerLiveData.observeForever(viewLifecycleOwnerObserver) } override fun onDestroy(owner: LifecycleOwner) { fragment.viewLifecycleOwnerLiveData.removeObserver(viewLifecycleOwnerObserver) fragment.lifecycle.removeObserver(this) } } init { if (fragment.lifecycle.currentState != Lifecycle.State.DESTROYED) { fragment.lifecycle.addObserver(observer) } } override fun getValue(thisRef: Fragment, property: KProperty<*>): T { val binding = binding if (binding != null) { return binding } val view = thisRef.view checkNotNull(view) { "Should get bindings when the view is not null." } return viewBindingFactory(view).also { this.binding = it } } } fun <T : ViewBinding> Fragment.viewBinding(viewBindingFactory: (View) -> T) = FragmentViewBindingDelegate(this, viewBindingFactory) これを使うと最初のコードはこうなります。 class LoginFragment : Fragment() { private val binding by viewBinding(FragmentLoginBinding::bind) override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { return inflater.inflate(R.layout.fragment_login, container, false) } }

0 件のコメント:

コメントを投稿