レイアウト ビューをアーキテクチャ コンポーネントにバインドする

AndroidX ライブラリに含まれているアーキテクチャ コンポーネントを使用することで、堅牢でテストとメンテナンスが簡単なアプリを設計できます。データ バインディング ライブラリはアーキテクチャ コンポーネントとシームレスに連携し、UI の開発をさらに簡素化します。アプリのレイアウトはアーキテクチャ コンポーネントのデータにバインドできます。この機能は、UI コントローラのライフサイクルを管理し、データの変更について通知するのに役立っています。

このページでは、アーキテクチャ コンポーネントをアプリに組み込んで、データ バインディング ライブラリを使用するメリットをさらに拡大する方法を説明します。

LiveData を使用してデータの変更について UI に通知する

LiveData オブジェクトをデータ バインディング ソースとして使用することで、データの変更について UI に自動的に通知できます。このアーキテクチャ コンポーネントについて詳しくは、LiveData の概要をご覧ください。

LiveData オブジェクトは、Observable を実装するオブジェクト(監視可能なフィールドなど)とは異なり、データの変更に関する情報を受信するオブザーバーのライフサイクルを認識しています。この情報にはさまざまな利点があります。詳しくは、LiveData を使用するメリットをご覧ください。Android Studio バージョン 3.1 以降では、データ バインディング コードにおいて監視可能なフィールドLiveData オブジェクトに置き換えることができます。

LiveData オブジェクトをバインディング クラスとともに使用するには、ライフサイクル所有者を指定して LiveData オブジェクトのスコープを定義する必要があります。次の例では、バインディング クラスをインスタンス化した後にアクティビティをライフサイクル所有者として指定しています。

Kotlin

    class ViewModelActivity : AppCompatActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            // Inflate view and obtain an instance of the binding class.
            val binding: UserBinding = DataBindingUtil.setContentView(this, R.layout.user)

            // Specify the current activity as the lifecycle owner.
            binding.setLifecycleOwner(this)
        }
    }
    

Java

    class ViewModelActivity extends AppCompatActivity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            // Inflate view and obtain an instance of the binding class.
            UserBinding binding = DataBindingUtil.setContentView(this, R.layout.user);

            // Specify the current activity as the lifecycle owner.
            binding.setLifecycleOwner(this);
        }
    }
    

ViewModel を使用して UI 関連のデータを管理するで説明するように、ViewModel コンポーネントを使用してデータをレイアウトにバインドできます。ViewModel コンポーネントで LiveData オブジェクトを使用すると、データを変換したり、複数のデータソースを統合したりできます。次の例は、ViewModel のデータを変換する方法を示しています。

Kotlin

    class ScheduleViewModel : ViewModel() {
        val userName: LiveData

        init {
            val result = Repository.userName
            userName = Transformations.map(result) { result -> result.value }
        }
    }
    

Java

    class ScheduleViewModel extends ViewModel {
        LiveData username;

        public ScheduleViewModel() {
            String result = Repository.userName;
            userName = Transformations.map(result, result -> result.value);
        }
    }
    

ViewModel を使用して UI 関連のデータを管理する

データ バインディング ライブラリは ViewModel コンポーネントとシームレスに連携します。レイアウトは、このコンポーネントが公開するデータを監視し、その変更に反応します。ViewModel コンポーネントをデータ バインディング ライブラリとともに使用すると、UI ロジックをレイアウトからテストが容易なコンポーネントに移動できます。また、データ バインディング ライブラリを使用すると、必要に応じてビューをデータソースにバインドしたり、そのバインドを解除したりできます。残りの作業のほとんどは、正しいデータが公開されていることを確認することです。このアーキテクチャ コンポーネントについて詳しくは、ViewModel の概要をご覧ください。

ViewModel コンポーネントをデータ バインディング ライブラリとともに使用するには、ViewModel クラスから継承したコンポーネントをインスタンス化し、バインディング クラスのインスタンスを取得して、ViewModel コンポーネントをバインディング クラスのプロパティに割り当てる必要があります。次の例は、このコンポーネントとライブラリを併用する方法を示しています。

Kotlin

    class ViewModelActivity : AppCompatActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            // Obtain the ViewModel component.
            val userModel: UserModel by viewModels()

            // Inflate view and obtain an instance of the binding class.
            val binding: UserBinding = DataBindingUtil.setContentView(this, R.layout.user)

            // Assign the component to a property in the binding class.
            binding.viewmodel = userModel
        }
    }
    

Java

    class ViewModelActivity extends AppCompatActivity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            // Obtain the ViewModel component.
            UserModel userModel = new ViewModelProvider(this).get(UserModel.class);

            // Inflate view and obtain an instance of the binding class.
            UserBinding binding = DataBindingUtil.setContentView(this, R.layout.user);

            // Assign the component to a property in the binding class.
            binding.viewmodel = userModel;
        }
    }
    

次の例に示すように、レイアウトでバインディング式を使用して、ViewModel コンポーネントのプロパティとメソッドを対応するビューに割り当てます。

<CheckBox
        android:id="@+id/rememberMeCheckBox"
        android:checked="@{viewmodel.rememberMe}"
        android:onCheckedChanged="@{() -> viewmodel.rememberMeChanged()}" />
    

監視可能な ViewModel を使用して、バインディング アダプターを詳細に管理する

Observable を実装する ViewModel コンポーネントを使用すると、LiveData オブジェクトの使用方法と同様に、データの変更について他のアプリ コンポーネントに通知できます。

LiveData のライフサイクル管理機能を失ったとしても、LiveData オブジェクトを使用するよりも、Observable インターフェースを実装する ViewModel コンポーネントを使用する方が望ましい場合があります。Observable を実装する ViewModel コンポーネントを使用すると、バインディング アダプターをアプリで詳細に管理できるようになります。たとえばこのパターンでは、データが変更されたときの通知を詳細に管理できるほか、カスタムのメソッドを指定して、双方向データ バインディングの属性の値を設定することもできます。

監視可能な ViewModel コンポーネントを実装するには、ViewModel クラスから継承し、Observable インターフェースを実装するクラスを作成する必要があります。オブザーバーが addOnPropertyChangedCallback() メソッドと removeOnPropertyChangedCallback() メソッドを使用して通知を登録または登録解除する場合は、カスタム ロジックを指定できます。また、notifyPropertyChanged() メソッドのプロパティが変更されたときに実行するカスタム ロジックを指定することもできます。次のコード例は、監視可能な ViewModel を実装する方法を示しています。

Kotlin

    /**
     * A ViewModel that is also an Observable,
     * to be used with the Data Binding Library.
     */
    open class ObservableViewModel : ViewModel(), Observable {
        private val callbacks: PropertyChangeRegistry = PropertyChangeRegistry()

        override fun addOnPropertyChangedCallback(
                callback: Observable.OnPropertyChangedCallback) {
            callbacks.add(callback)
        }

        override fun removeOnPropertyChangedCallback(
                callback: Observable.OnPropertyChangedCallback) {
            callbacks.remove(callback)
        }

        /**
         * Notifies observers that all properties of this instance have changed.
         */
        fun notifyChange() {
            callbacks.notifyCallbacks(this, 0, null)
        }

        /**
         * Notifies observers that a specific property has changed. The getter for the
         * property that changes should be marked with the @Bindable annotation to
         * generate a field in the BR class to be used as the fieldId parameter.
         *
         * @param fieldId The generated BR id for the Bindable field.
         */
        fun notifyPropertyChanged(fieldId: Int) {
            callbacks.notifyCallbacks(this, fieldId, null)
        }
    }
    

Java

    /**
     * A ViewModel that is also an Observable,
     * to be used with the Data Binding Library.
     */
    class ObservableViewModel extends ViewModel implements Observable {
        private PropertyChangeRegistry callbacks = new PropertyChangeRegistry();

        @Override
        protected void addOnPropertyChangedCallback(
                Observable.OnPropertyChangedCallback callback) {
            callbacks.add(callback);
        }

        @Override
        protected void removeOnPropertyChangedCallback(
                Observable.OnPropertyChangedCallback callback) {
            callbacks.remove(callback);
        }

        /**
         * Notifies observers that all properties of this instance have changed.
         */
        void notifyChange() {
            callbacks.notifyCallbacks(this, 0, null);
        }

        /**
         * Notifies observers that a specific property has changed. The getter for the
         * property that changes should be marked with the @Bindable annotation to
         * generate a field in the BR class to be used as the fieldId parameter.
         *
         * @param fieldId The generated BR id for the Bindable field.
         */
        void notifyPropertyChanged(int fieldId) {
            callbacks.notifyCallbacks(this, fieldId, null);
        }
    }
    

参考情報

データ バインディングの詳細については、以下の参考情報をご確認ください。

サンプル

Codelab

ブログ投稿