Data BindingでViewの表示と状態(データ)を結合!

投稿日:  更新日:

Data BindingはViewシステムを用いている場合に、Viewの表示と状態(データ)を結合する仕組みです。

結合とは「状態の変化に連動して、表示を更新する」ことです。

Googleは「利点がある」と述べています。しかし、私は利点に感じないので、積極的な利用をしていません。

Googleが提供するAndroidアプリのサンプルで頻繁に登場し、よく見かけます。

ですので、ここに備忘録として、まとめます。

※環境:Android Studio Ladybug Feature Drop | 2024.2.2
    Kotlin 2.0.0
     (Viewシステムのプロジェクトは1.9.24が選ばれる)
     (Composeのプロジェクトは2.0.0が選ばれる)

スポンサーリンク

Data Vindingとは

Data BindingはViewシステム(レイアウトファイルで画面構成を定義する環境)を用いている場合に、Viewの表示と状態(データ)を結合する仕組みです。

結合とは「状態の変化に連動して、表示を更新する」ことです。

AAC(Androidアーキテクチャコンポーネント)の「UIの分離」を意図したプログラミング手法です。

Data Bindingあり・なしを比較すれば、違いと利点がわかると思います。

Data Bindingなし(UIとロジックの関係が密)

表示を更新する場合、ロジック側は対象のViewを把握して、そのView固有のセッター関数で、新たな状態を設定する必要があります。

ロジック側でfindViewByIdを用いて、Viewのインスタンスを取得するのは、そのためです。

UIとロジックの関係はとても「密(みつ)」になります。

しかも、Viewの操作はメインスレッドで行う必要があります。

 Data Bindingなし 

Data Bindingなし

Data Bindingあり(UIとロジックの関係が疎)

状態を保持する変数はUI側に持ちます。状態はViewと結合されていて、状態の変化が直ちにViewの表示に反映されます。

表示を更新する場合、ロジック側は状態を保持する変数へ、新たな状態を書き込むだけです。対象のViewを把握する必要はありません。

UIとロジックの関係は「疎(そ)」になります。

しかも、Viewの操作を行うわけでは無いので、状態の書き込みはワーカースレッドで行うことができます。

 Data Bindingあり 

Data Bindingあり

スポンサーリンク

環境設定

Data Bindingを利用するための環境設定は、以下の3つが必要です。

  • (1)Data Bindingの有効化
  • (2)Viewシステムの採用
  • (3)レイアウトファイルの変換

特に「(3)レイアウトファイルの変換」を行わないと、(1)と(2)が出来ていても、機能(Bindingの動作)は無反応です。注意して下さい。

Data Bindingの有効化

Data Bindingはモジュール毎に有効・無効を指定します。

有効化の設定

有効にするために、「app/build.gradle」へ次の設定を行います。

plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.kotlin.android)
    id("org.jetbrains.kotlin.kapt")
}

android {
    ...
    buildFeatures {
        dataBinding = true
    }
	...
}

Data BindingはKapt(Kotlin Anotation processing tools)を使います。Ksp(Kotlin Symbole Processor)はサポートしていないので、使えません。

Kotlin 2の対応

現在(2025.01)、Kotlinは1.9.xから2.0.xへ移行を果たしています。それに伴い、KaptもKspへ移行することが推奨されています。KaptはKotlin 1.9.x世代のプラグインなのです。

ですが、下記の設定をgradele.propertiesへ行うと、KaptはKotlin 2をサポートします。

   :
   :
# KapeのKotlin 2対応を有効化
kapt.use.k2=true

ただし、「サポートは実験段階です。評価目的のみに使用してください。」とドキュメントに書かれています。

上記の事項が承諾できないならば、Kotlinを1.9.xに戻してData Bindingをも用いる必要があります。

ちなみに、最新版のKaptはメンテナンス モードという位置づけでリリースされています。今後、新機能を実装する予定はなく、KotlinやJavaのバージョンアップに対応するのみになるようです。「いつまで対応が続くか?!」は不明です。

スポンサーリンク

Viewシステムの採用

Data BindingはViewシステムを用いている場合の機能です。

現状はJetpack Composeによる開発が主流になっているので、Viewシステムを意図的に選択しなければなりません。

新規プロジェクトの作成時に、「Views Activity」を選んでください。

Viewシステムの選択

レイアウトファイルの変換

レイアウトファイル(XXX.xml)をData Binding対応へ変換します。

<layout>タグで全体を囲み、レイアウトファイル中のルートViewと並列に<data>タグを設けてください。

変換後変換前
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>
        <!-- ここにBinding変数を定義 -->
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <TextView
            android:id="@+id/txtMesg"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello World!"
            android:textAllCaps="false"
            android:textSize="48sp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/txtMesg"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        android:textAllCaps="false"
        android:textSize="48sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

この変換作業ですが、Android Studioに自動変換する機能が付いています。こちらを使うと簡単です。

DataBindingのレイアウトファイルへ変換

スポンサーリンク

Binding動作

Data Bindingの有効化が行われると、Android Studioはレイアウトファイルを認識する際に、Bindingクラス(例:ActivityMainBinding)を自動生成します。

DataBindingのフロー

Bindingクラスの作成先フォルダ

Bindingクラスのクラス名は、レイアウトファイル名(例:activity_main.xml)を図のように変換したものになります。

Bindingクラスのネーミングルール

Bindingクラス#infulate()を実行すれば、BindingクラスのプロパティにBinding変数のインスタンスが登録されます。※方法2のやり方もあります。

そして、そのプロパティを使って、Binding変数へアクセスできるようになります。

Bindingインスタンスの取得:方法1方法2
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)        // トップViewをコンテンツとして指定

        binding.message = "Hello Android!"  // Binding変数(message)の参照
        binding.color = Color.WHITE         // Binding変数(color)の参照
		// DataBindingUtilを用いた方法
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)

        binding.message = "Hello Android!"  // Binding変数(message)の参照
        binding.color = Color.WHITE         // Binding変数(color)の参照

DataBindingの変数参照

スポンサーリンク

状態(データ)の結合

Binding変数は<data>タグ内の<variable>タグで定義します。「name」が変数名、「type」が変数の型です。

Binding変数はロジック側からのアクセスが監視され、書き込みが行われたらViewの関数を実行するように、Viewシステムが制御します。

Binding変数の監視

Binding変数と実行するViewの関数は、図のように関連付けられます。

右辺がBinding式(”@{“と”}”で囲まれた式)の場合は、左辺をViewの関数として扱います。その他の場合はViewの属性として扱います。

Binding式と実行するView関数の指定

Viewの関数は図のようにBinnding式の評価結果を引数にして実行されます。

Binding式は構文があるので、詳細は「レイアウトとバインディング式/式言語」を参照してください。

DataBindingにおけるView関数の実行

この時、レイアウト中に記載する関数名は省略形になっています。省略形のルールは図の通りです。

DataBindingで指定する関数名の省略形

スポンサーリンク

プリミティブ型

Binding変数がプリミティブ型である場合の例です。

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>
        <variable name="message" type="String" />
        <variable name="color" type="Integer" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <TextView
            android:id="@+id/txtMesg"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{message}"
            android:textAllCaps="false"
            android:textColor="@{color}"
            android:textSize="34sp"
            ... />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
    private  lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
		...
		
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.message = "Hello Android!"
        binding.color = Color.WHITE
        // 別スレッドで5秒後にWHITE->GREENへ変更
        lifecycleScope.launch(Dispatchers.Default) {
            delay(5000)                         // 5秒待つ
            binding.color = Color.GREEN         // WHITE->RED
        }
    }

非同期(5秒後)にWHITE⇒GREENへ変更しています。非同期であってもData Bindingは問題なく動作します。

DataBinding、プリミティブ型の例

スポンサーリンク

一般的なクラス

Binding変数が一般的なクラスである場合の例です。

型はフルパス(パッケージ名全体)を書かなければなりません。

data class Message (
    var text: String = "Hello Android !",
    @ColorInt var color: Int = Color.WHITE
)
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>
        <variable name="message" type="com.example.sample.Message" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <TextView
            android:id="@+id/txtMesg"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{message.text}"
            android:textAllCaps="false"
            android:textColor="@{message.color}"
            android:textSize="34sp"
            ... />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
    private  lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        ...

        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
	
        binding.message = Message(color = Color.WHITE)
        // 別スレッドで5秒後にWHITE->GREENへ変更
        lifecycleScope.launch(Dispatchers.Default) {
            delay(5000)                                     // 5秒待つ
            binding.message = Message(color = Color.GREEN)  // WHITE->GREEN(OK)
        }
    }

非同期(5秒後)にWHITE⇒GREENに変更しています。非同期であってもData Bindingは問題なく動作します。

実行結果は「プリミティブ型」と同じです。省略します。

Binding変数の書き込みの有無により、表示が変更される場合とされない場合があるので注意が必要です。

表示が変更されない表示が変更される
        lifecycleScope.launch(Dispatchers.Default) {
            delay(5000)                                     // 5秒待つ
            binding.message?.let {                          // WHITE->GREEN(NG)
                it.color = Color.GREEN
            }
        }
        lifecycleScope.launch(Dispatchers.Default) {
            delay(5000)                                     // 5秒待つ
            val _message = binding.message?.apply {         // WHITE->GREEN(OK)
                color = Color.GREEN
            }
            binding.message = _message
        }

ちなみに、Binding変数はNullalbe(Null許容型)で、初期値はNullです。
※Binding式でNullの場合の代替値が指定できます(”??”を使う、Binding式の構文のドキュメント参照)。

スポンサーリンク

イベントの結合

イベントハンドラー(実行される関数)を関数オブジェクトの形でリスナーへ登録します(setOnClockListener関数などを用いる)。

ただし、Binding変数の型に関数オブジェクトの型は指定できません。ですので、イベントハンドラーが属するクラスを型にします。

class Handlers {

    fun clickedMesg(v: View) {  // ⇐ イベントハンドラー
        //
        // イベントが発生した時に実行する処理
        //
    }
}

XXX

Viewのリスナーへ登録する関数(setOnClockListener関数など)は、図のようにBinnding式の評価結果を引数にして実行されます。

Binding式が「クラスに属する関数オブジェクト」になっている点がポイントです。

XXX

この時、レイアウト中に記載する関数名は省略形になっています。省略形のルールは図の通りです。

DataBindingで指定する関数名の省略形

イベントハンドラー(関数オブジェクト)の表現方法は、次の2つがあります。

  • (1)メソッド参照
  • (2)リスナーバインディング
  • ※(1)は上記の図に出てきた表現方法
スポンサーリンク

メソッド参照

「メソッド参照」は関数名で関数オブジェクトを表現する方法です。

このサンプルはハンドラーをMainActivityに実装しています。ですので、Binding変数の型はMainActivityです。

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>
        <variable name="message" type="com.example.sample.Message" />
        <variable name="activity" type="com.example.sample.MainActivity" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".BasicActivity">

        <LinearLayout ...>

            <TextView
                android:id="@+id/txtMesg"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@{message.text}"
                android:textColor="@{message.color}"
                android:onClick="@{activity::clickedMesg}"
                android:textAllCaps="false"
                android:textSize="34sp" />

        </LinearLayout>

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
    private  lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        ...

        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.message  = Message(color = Color.WHITE)
        binding.activity = this
    }

    fun clickedMesg() {
        binding.message = binding.message?.apply { color = Color.YELLOW }
    }
}

クリックでWHITE⇒YELLOWへ変更しています。

DataBinding、メソッド参照で表現したイベント結合

クリックイベントは引数にViewを持ったハンドラーを起動しようとします。

ですので、ハンドラーの引数にViewが必要です。また、ハンドラーはView以外の引数を持てません。

起動する側とされる側で、引数の整合性が必要だからです。

スポンサーリンク

リスナーバインディング

「リスナーバインディング」はラムダ式で関数オブジェクトを表現する方法です。そのラムダ式から、ハンドラーを名指しで起動します。

このサンプルはハンドラーをMainActivityに実装しています。ですので、Binding変数の型はMainActivityです。

引数なし引数あり1引数あり2引数あり3
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>
        <variable name="message" type="com.example.sample.Message" />
        <variable name="activity" type="com.example.sample.MainActivity" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".BasicActivity">

        <LinearLayout ...>

            <TextView
                android:id="@+id/txtMesg"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@{message.text}"
                android:textColor="@{message.color}"
                android:onClick="@{() -> activity.clickedMesg()}"
                android:textAllCaps="false"
                android:textSize="34sp" />

        </LinearLayout>

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
    private  lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        ...

        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.message  = Message(color = Color.WHITE)
        binding.activity = this
    }

    fun clickedMesg() {
        binding.message = binding.message?.apply { color = Color.YELLOW }
    }
}
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>
        <variable name="message" type="com.example.sample.Message" />
        <variable name="activity" type="com.example.sample.MainActivity" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".BasicActivity">

        <LinearLayout ...>

            <TextView
                android:id="@+id/txtMesg"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@{message.text}"
                android:textColor="@{message.color}"
                android:onClick="@{() -> activity.clickedMesg(message)}"
                android:textAllCaps="false"
                android:textSize="34sp" />

        </LinearLayout>

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
    private  lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        ...

        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.message  = Message(color = Color.WHITE)
        binding.activity = this
    }

    fun clickedMesg(m: Message) {
        binding.message = binding.message?.apply { color = Color.YELLOW }
        // mを使った処理
    }
}
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>
        <variable name="message" type="com.example.sample.Message" />
        <variable name="activity" type="com.example.sample.MainActivity" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".BasicActivity">

        <LinearLayout ...>

            <TextView
                android:id="@+id/txtMesg"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@{message.text}"
                android:textColor="@{message.color}"
                android:onClick="@{(view) -> activity.clickedMesg(view)}"
                android:textAllCaps="false"
                android:textSize="34sp" />

        </LinearLayout>

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
    private  lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        ...

        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.message  = Message(color = Color.WHITE)
        binding.activity = this
    }

    fun clickedMesg(v: View) {
        binding.message = binding.message?.apply { color = Color.YELLOW }
        // vを使った処理
    }
}
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>
        <variable name="message" type="com.example.sample.Message" />
        <variable name="activity" type="com.example.sample.MainActivity" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".BasicActivity">

        <LinearLayout ...>

            <TextView
                android:id="@+id/txtMesg"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@{message.text}"
                android:textColor="@{message.color}"
                android:onClick="@{(view) -> activity.clickedMesg(view, message)}"
                android:textAllCaps="false"
                android:textSize="34sp" />

        </LinearLayout>

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
    private  lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        ...

        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.message  = Message(color = Color.WHITE)
        binding.activity = this
    }

    fun clickedMesg(v: View, m: Message) {
        binding.message = binding.message?.apply { color = Color.YELLOW }
        // mとvを使った処理
    }
}

クリックでWHITE⇒YELLOWへ変更しています。

実行結果は「プリミティブ型」と同じです。省略します。

ラムダ式からハンドラーを名指しで起動する際に、ハンドラーへView以外の引数を設けることが可能です。ただし、引数にできるのはBinding変数です。

これが、この表現方法の利点になります。

スポンサーリンク

関連記事:

Androidアーキテクチャコンポーネント(AAC)は「堅牢でテストとメンテナンスが簡単なアプリの設計を支援する」とドキュメントで説明されています。 有効そうだけど、実態がよくわからないので、いろいろ調べて理解した内容をまとめました。 ...
Androidアーキテクチャコンポーネント(AAC)は「堅牢でテストとメンテナンスが簡単なアプリの設計を支援する」とドキュメントで説明されています。 その中で紹介されているコンポーネントの1つが「Lifecycle」です。 Lifecycleについて、まとめました。 ...
Androidアーキテクチャコンポーネント(AAC)は「堅牢でテストとメンテナンスが簡単なアプリの設計を支援する」とドキュメントで説明されています。 その中で紹介されているコンポーネントの1つが「LiveData」です。 LiveDataについて、まとめました。 ...
Androidアーキテクチャコンポーネント(AAC)は「堅牢でテストとメンテナンスが簡単なアプリの設計を支援する」とドキュメントで説明されています。 その中で紹介されているコンポーネントの1つが「ViewModel」です。 ViewModelについて、まとめました。 ...
Androidの開発スピードが速過ぎます。個人で習得を進めている私には、追い付いて行けません。 これが会社などであれば、グループ内のメンバーで分担して習得し、後に共有、などといった対応が可能でしょう。組織の強みですね! 先日も、久しぶりにAndroid Studioで新規プロジェクトを作成したら、Jetpack Composeのプロジェクトになってました。「はて?、これはどうすれば良いのだ?」と、頭の中は疑問符だらけで、プログラミングが先へ進めませんでした。 Jetpackの存在は知りながら、“使わなくてもアプリは作れる”と、学習は後回していたからです。 プロジェクトのひな型にJetpack Composeが採用されたならば、今後はJetpackの利用が開発の主軸になっていくのでしょう。 遅れ馳せならが、Jetpackの重要性を知った次第です。 ここで本腰をいれて習得しないと、さらに後方へ置いて行かれそうです。 という訳で、Jetpackの学習を始めました。 今回はJetpackそのものについてまとめます。 ※環境:Android Studio Flamingo | 2022.2.1 ...
View BindingはViewシステムを用いている場合に、プログラムから子Viewの参照を容易にする仕組みです。 Googleは「利点がある」と述べています。しかし、私は利点に感じないので、積極的な利用をしていません。 今まで、単なるオプションと考えていたのですが、最近は一般的になりつつあり、世に出回っているサンプルで頻繁に見かけるようになりました。 ですので、ここに備忘録として、まとめます。 ※環境:Android Studio Ladybug | 2024.2.1     Kotlin 1.9.24      (Viewシステムのプロジェクトは1.9.24が選ばれる)      (Composeのプロジェクトは2.0.0が選ばれる) ...
スポンサーリンク