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

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

このページでは、アーキテクチャ コンポーネントをアプリに組み込み、データ バインディング ライブラリを最大限に活用する方法について説明します。

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

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

監視可能フィールドなどの Observable を実装するオブジェクトとは異なり、LiveData オブジェクトはデータ変更に登録されたオブザーバーのライフサイクルを認識しています。この知識には多くの利点があります。詳しくは 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 コンポーネントを使用してデータをレイアウトにバインドできます。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 は、レイアウトが監視してその変化に対応するデータを公開します。データ バインディング ライブラリで 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 must 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 must 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);
    }
}

参考情報

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