[Android] ViewBindingを安全に使うためのベストプラクティス

スポンサーリンク

はじめに

最近の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では_bindingonDestroyView()でnull解除が必須
  • 拡張関数を使うとFragmentでも簡潔に書ける
  • Adapterでも積極的にViewBindingを使おう

ちょっとしたことですが、ViewBindingを正しく扱うだけで、クラッシュやリークのリスクがぐっと減ります。
毎回書くパターンを整えておくと、チーム開発でもコードが統一されて見やすくなりますね。

おまけ

ちなみに、ViewBindingを導入した後は、
古いfindViewById()を混在させないのがコツです。
Bindingが生成されていないケース(カスタムViewなど)以外では、すべてBindingに寄せるとコードの保守性が上がります。

コメント

タイトルとURLをコピーしました