[Android] DataBinding-ktx 4.0.0 をリリースしました
DataBinding-ktx とは
DataBinding と ViewBinding に関する問題を解決し、安全・簡単に利用するためのライブラリです。
DataBinding/ViewBinding に関する問題
1. Activity と Fragment で変数宣言の方法が異なる
Activity では by lazy を使えますが、Fragment では View が再生成されるので by lazy が使えません(*)。
※: 再生成後の View に対して binding インスタンスが生成されないため
2. Fragment は onDestroyView で binding を null にしないとメモリを浪費する
Navigation component やバックスタック、デタッチを利用している場合、onDestroyView で Fragment の View は解放されるが Fragment が生きています。binding は View Tree を保持しているため解放しないとメモリを浪費してしまいます。
3. setLifecycleOwner を呼び出し忘れる
DataBinding のみの問題ですが、LiveData を利用している場合、setLifecycleOwner を呼び出さないと LiveData が DataBinding されません。
DataBinding-ktx による解決
1. Activity と Fragment で変数宣言の方法が異なる
Kotlin の Delegated Properties によって Activity と Fragment で同じ書き方でプロパティを宣言することができます。
2. Fragment は onDestroyView で binding を null にしないとメモリを浪費する
Delegated Properties 内部で View と同時に binding も解放されるためライブラリ利用者は意識する必要がありません。
3. setLifecycleOwner を呼び出し忘れる
binding 変数に初回アクセス時に自動的に setLifecycleOwner が呼び出されているため呼び出し忘れがなくなります。
DataBinding-ktx の使い方
DataBinding
private val binding: DataBindingActivityBinding by dataBinding()
ViewBinding
private val binding by viewBinding { ViewBindingActivityBinding.bind(it) }
Activity
binding 変数を使う前に setContentView を呼び出すか、レイアウト ID をコンストラクタに渡してください。
class DataBindingActivity : FragmentActivity() {
// Declare the `binding` variable using `by dataBinding()`.
private val binding: DataBindingActivityBinding by dataBinding()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.data_binding_activity)
// You can use binding
}
}
class DataBindingActivity : AppCompatActivity(R.layout.data_binding_activity) {
// Declare the `binding` variable using `by dataBinding()`.
private val binding: DataBindingActivityBinding by dataBinding()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// You can use binding
}
}
Fragment
onCreateView で inflater.inflate を呼び出すか、レイアウト ID をコンストラクタに渡してください。
class DataBindingFragment : Fragment() {
// Declare the `binding` variable using `by dataBinding()`.
private val binding: DataBindingFragmentBinding by dataBinding()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.data_binding_fragment, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// You can use binding
}
}
class DataBindingFragment : Fragment(R.layout.data_binding_fragment) {
// Declare the `binding` variable using `by dataBinding()`.
private val binding: DataBindingFragmentBinding by dataBinding()
// DO NOT override onCreateView
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// You can use binding
}
}
備考
レイアウト ID をコンストラクタに渡し忘れた場合、Activity ではクラッシュし、Fragment では表示されません。そのため、以下のようなクラスを定義すれば良いと思います。
open class DataBindingAppCompatActivity<T : ViewDataBinding>(@LayoutRes contentLayoutId : Int) : AppCompatActivity(contentLayoutId) {
protected val binding: T by dataBinding()
}
class YourActivity : DataBindingAppCompatActivity<YourActivityBinding>(R.layout.your_activity) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// You can use binding
}
}
open class DataBindingFragment<T : ViewDataBinding>(@LayoutRes contentLayoutId : Int) : Fragment(contentLayoutId) {
protected val binding: T by dataBinding()
}
class YourFragment : DataBindingFragment<YourFragmentBinding>(R.layout.your_fragment) {
// DO NOT override onCreateView
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// You can use binding
}
}
ViewBinding は以下の通り。
open class ViewBindingAppCompatActivity<T : ViewBinding>(@LayoutRes contentLayoutId: Int, bind: (View) -> T) : AppCompatActivity(contentLayoutId) {
protected val binding by viewBinding(bind)
}
class YourActivity : ViewBindingAppCompatActivity<YourActivityBinding>(R.layout.your_activity, YourActivityBinding::bind) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// You can use binding
}
}
open class ViewBindingFragment<T : ViewBinding>(@LayoutRes contentLayoutId : Int, bind: (View) -> T) : Fragment(contentLayoutId) {
protected val binding: T by viewBinding(bind)
}
class YourFragment : ViewBindingFragment<YourFragmentBinding>(R.layout.your_fragment) {
// DO NOT override onCreateView
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// You can use binding
}
}