2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

たった1行で ViewBinding を使う方法 👏

2
Last updated at Posted at 2021-05-20

ViewBinding 使っていますか? とても便利ですが、ActivityとFragmentでインスタンスの取り扱いに違いがあり少々クセがあります。特にFragmentの場合が面倒です。

ViewBinding をActivity で使う場合

Activity で使う場合は lazy { } を使うと簡単です。インスタンスを破棄することは考えなくても大丈夫です。1行で書くことができます。

private val binding by lazy { MyActivityBinding.inflate(layoutInflator, container, false)

ViewBinding を Fragment で使う場合

問題は ViewBinding を Fragment で使う場合で、インスタンスを破棄することを考える必要があるのでコードがやや冗長になります。ボイラープレートコードを減らしてもっと短く方法はないでしょうか。

private var _binding: MyFragmentBinding? = null
private val binding get() = _binding!!

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        _binding = MyFragmentBinding.inflate(inflater, container, false)
        return binding.root
}

override fun onDestroyView() {
		super.onDestroyView()
    _binding = null
}

Fragment でも 1行で ViewBinding を使う方法

Simple one-liner ViewBinding in Fragments and Activities with Kotlin で紹介されている方法を使います。 Delegated Property を通じて ViewBinding のインスタンスを取得するのがミソです。

導入手順

  1. jitpack でライブラリを入れる
implementation("com.github.Zhuinden:fragmentviewbindingdelegate-kt:1.0.0")
  1. by viewBinding { } を使って書き換える
class MyFragment : Fragment(R.layout.my_fragmet) {  // ← layoutファイルを指定する
   private val binding by viewBinding(MyFragmentBinding::bind) // ← ::bind を使う。(::inflateではない)
}

たったこれだけです!素直に書く場合と比べて 10行 くらい短くなるし、インスタンス破棄を忘れることもなくなって一石二鳥です ✨

補足: Delegated Property の実装の解説

/*
 * Copyright 2021 Gabor Varadi
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.zhuinden.fragmentviewbindingdelegatekt

import android.view.View
import androidx.fragment.app.Fragment
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.Observer
import androidx.viewbinding.ViewBinding
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty

class FragmentViewBindingDelegate<T : ViewBinding>(
    val fragment: Fragment,
    val viewBindingFactory: (View) -> T
) : ReadOnlyProperty<Fragment, T> {
    private var binding: T? = null

    init {
        fragment.lifecycle.addObserver(object : DefaultLifecycleObserver {
            val viewLifecycleOwnerLiveDataObserver =
                Observer<LifecycleOwner?> {
                    val viewLifecycleOwner = it ?: return@Observer

                    viewLifecycleOwner.lifecycle.addObserver(object : DefaultLifecycleObserver {
                        override fun onDestroy(owner: LifecycleOwner) {
                            binding = null // FragmentのonDestroyが呼ばれるタイミングで自動的に ViewBinding のインスタンスを破棄してくれる
                        }
                    })
                }

            override fun onCreate(owner: LifecycleOwner) {
                fragment.viewLifecycleOwnerLiveData.observeForever(viewLifecycleOwnerLiveDataObserver)
            }

            override fun onDestroy(owner: LifecycleOwner) {
                fragment.viewLifecycleOwnerLiveData.removeObserver(viewLifecycleOwnerLiveDataObserver)
            }
        })
    }

    override fun getValue(thisRef: Fragment, property: KProperty<*>): T {
        val binding = binding // シャドーイング
        if (binding != null) {
            return binding
        }

        val lifecycle = fragment.viewLifecycleOwner.lifecycle
        if (!lifecycle.currentState.isAtLeast(Lifecycle.State.INITIALIZED)) {
            throw IllegalStateException("Should not attempt to get bindings when Fragment views are destroyed.")
        }

				// View と bind することで ViewBinding のインスタンスを作る
        return viewBindingFactory(thisRef.requireView()).also { 
						this.binding = it // ViewBinding のインスタンスを変数に保持する
				}
    }
}

fun <T : ViewBinding> Fragment.viewBinding(viewBindingFactory: (View) -> T) =
    FragmentViewBindingDelegate(this, viewBindingFactory)

参考リンク

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?