2021年2月12日金曜日

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

ViewBinding のドキュメントでは Fragment で使う時の実装はこのようになっています。
  1. class LoginFragment : Fragment() {  
  2.   
  3.     private var _binding: FragmentLoginBinding? = null  
  4.     private val binding: FragmentLoginBinding  
  5.         get() = _binding!!  
  6.   
  7.     override fun onCreateView(  
  8.         inflater: LayoutInflater,  
  9.         container: ViewGroup?,  
  10.         savedInstanceState: Bundle?  
  11.     ): View {  
  12.         _binding = FragmentLoginBinding.inflate(inflater, container, false)  
  13.         return binding.root  
  14.     }  
  15.   
  16.     override fun onDestroyView() {  
  17.         super.onDestroyView()  
  18.         _binding = null  
  19.     }  
  20. }  
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 を参考に変更を加えています。
  1. class FragmentViewBindingDelegate<T : ViewBinding>(  
  2.     val fragment: Fragment,  
  3.     val viewBindingFactory: (View) -> T  
  4. ) : ReadOnlyProperty<Fragment, T> {  
  5.   
  6.     private var binding: T? = null  
  7.   
  8.     private val viewLifecycleOwnerObserver = Observer<LifecycleOwner?> {  
  9.         if (it == null) {  
  10.             binding = null  
  11.         }  
  12.     }  
  13.   
  14.     private val observer = object : DefaultLifecycleObserver {  
  15.   
  16.         override fun onCreate(owner: LifecycleOwner) {  
  17.             fragment.viewLifecycleOwnerLiveData.observeForever(viewLifecycleOwnerObserver)  
  18.         }  
  19.   
  20.         override fun onDestroy(owner: LifecycleOwner) {  
  21.             fragment.viewLifecycleOwnerLiveData.removeObserver(viewLifecycleOwnerObserver)  
  22.             fragment.lifecycle.removeObserver(this)  
  23.         }  
  24.     }  
  25.   
  26.     init {  
  27.         if (fragment.lifecycle.currentState != Lifecycle.State.DESTROYED) {  
  28.             fragment.lifecycle.addObserver(observer)  
  29.         }  
  30.     }  
  31.   
  32.     override fun getValue(thisRef: Fragment, property: KProperty<*>): T {  
  33.         val binding = binding  
  34.         if (binding != null) {  
  35.             return binding  
  36.         }  
  37.   
  38.         val view = thisRef.view  
  39.         checkNotNull(view) {  
  40.             "Should get bindings when the view is not null."  
  41.         }  
  42.   
  43.         return viewBindingFactory(view).also { this.binding = it }  
  44.     }  
  45. }  
  46.   
  47. fun <T : ViewBinding> Fragment.viewBinding(viewBindingFactory: (View) -> T) =  
  48.     FragmentViewBindingDelegate(this, viewBindingFactory)  
これを使うと最初のコードはこうなります。
  1. class LoginFragment : Fragment() {  
  2.   
  3.     private val binding by viewBinding(FragmentLoginBinding::bind)  
  4.   
  5.     override fun onCreateView(  
  6.         inflater: LayoutInflater,  
  7.         container: ViewGroup?,  
  8.         savedInstanceState: Bundle?  
  9.     ): View {  
  10.         return inflater.inflate(R.layout.fragment_login, container, false)  
  11.     }  
  12. }  

0 件のコメント:

コメントを投稿