はじめに
最近のAndroid開発では、ViewBindingがすっかり標準になりましたね。
findViewById地獄から解放されて、コードも読みやすくなりました。
でも、FragmentやAdapterなどでうっかりリークしてしまうケース、まだまだ多いです。
この記事では、そんなViewBindingを「安全に」「スッキリ」使うための実践的なパターンをまとめてみました。
ViewBindingの基本設定
まずはbuild.gradleでViewBindingを有効化します。
android {
...
buildFeatures {
viewBinding true
}
}
これだけで、各レイアウトXMLに対応するBindingクラス(例:ActivityMainBinding
)が自動生成されます。
Activityでの基本的な使い方
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
with(binding) {
button.setOnClickListener {
textView.text = "ボタンが押されました!"
}
}
}
}
Activityではこの形でOK。
Lifecycle的にもActivityが破棄されるタイミングでbindingも破棄されるので特に問題なしです。
Fragmentでの安全な使い方
Fragmentでありがちなのが、bindingを破棄し忘れてメモリリークするパターン。
公式でも推奨されているパターンを紹介します。
class SampleFragment : Fragment() {
private var _binding: FragmentSampleBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View {
_binding = FragmentSampleBinding.inflate(inflater, container, false)
return binding.root
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
ポイントは、onDestroyView()
でbindingをnullに戻すこと。
FragmentのViewは再生成される可能性があるため、この1行を忘れるとリークします。
ちょっと便利な拡張関数
毎回同じパターンを書くのが面倒な人向けに、拡張関数で簡略化する方法もあります。
inline fun Fragment.viewBinding(
crossinline bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> T
) = lazy(LazyThreadSafetyMode.NONE) {
bindingInflater(layoutInflater, null, false)
}
これを使うと、Fragment内でこんなふうに書けます。
class ExampleFragment : Fragment(R.layout.fragment_example) {
private val binding by viewBinding(FragmentExampleBinding::inflate)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
with(binding) {
textView.text = "こんにちは ViewBinding"
}
}
}
シンプルでリークの心配もなく、FragmentのViewBindingをより直感的に扱えます。
RecyclerView.Adapterでの使い方
Adapter内でもViewBindingは使えます。
こちらもfindViewById不要でスッキリ。
class SampleAdapter(
private val items: List
) : RecyclerView.Adapter<SampleAdapter.ViewHolder>() {
inner class ViewHolder(val binding: ItemSampleBinding)
: RecyclerView.ViewHolder(binding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding = ItemSampleBinding.inflate(
LayoutInflater.from(parent.context), parent, false
)
return ViewHolder(binding)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.binding.textView.text = items[position]
}
override fun getItemCount() = items.size
}
ViewHolderにbindingをそのまま渡すのがポイントです。
Bindingの責務を明確に分けることで、Adapterの可読性が一気に上がります。
まとめ
- Activityでは
lateinit
でOK - Fragmentでは
_binding
+onDestroyView()
でnull解除が必須 - 拡張関数を使うとFragmentでも簡潔に書ける
- Adapterでも積極的にViewBindingを使おう
ちょっとしたことですが、ViewBindingを正しく扱うだけで、クラッシュやリークのリスクがぐっと減ります。
毎回書くパターンを整えておくと、チーム開発でもコードが統一されて見やすくなりますね。
おまけ
ちなみに、ViewBindingを導入した後は、
古いfindViewById()
を混在させないのがコツです。
Bindingが生成されていないケース(カスタムViewなど)以外では、すべてBindingに寄せるとコードの保守性が上がります。
コメント