Lavorare con oggetti dati osservabili

Per osservabilità si intende la capacità di un oggetto di informare altre persone delle modifiche apportate ai suoi dati. La libreria di associazione dei dati consente di rendere osservabili oggetti, campi o raccolte.

Puoi utilizzare qualsiasi oggetto per l'associazione di dati, ma la modifica dell'oggetto non determina automaticamente l'aggiornamento dell'interfaccia utente. Puoi utilizzare l'associazione di dati per consentire agli oggetti di dati di inviare notifiche ad altri oggetti, noti come listener, quando i dati cambiano. Esistono tre tipi di classi osservabili: campi, raccolte e oggetti.

Quando uno di questi oggetti di dati osservabili è associato all'interfaccia utente e una proprietà dell'oggetto dati cambia, l'interfaccia utente si aggiorna automaticamente.

Campi osservabili

Se le tue classi hanno solo poche proprietà, potrebbe non valere la pena creare classi che implementino l'interfaccia di Observable. In questo caso, puoi utilizzare la classe generica Observable e le seguenti classi primitive specifiche per rendere osservabili i campi:

I campi osservabili sono oggetti osservabili autonomi che hanno un singolo campo. Le versioni primitive evitano di "boxing" e "unboxing" durante le operazioni di accesso. Per utilizzare questo meccanismo, crea una proprietà public final nel linguaggio di programmazione Java o una proprietà di sola lettura in Kotlin, come mostrato nell'esempio seguente:

Kotlin

class User {
    val firstName = ObservableField<String>()
    val lastName = ObservableField<String>()
    val age = ObservableInt()
}

Java

private static class User {
    public final ObservableField<String> firstName = new ObservableField<>();
    public final ObservableField<String> lastName = new ObservableField<>();
    public final ObservableInt age = new ObservableInt();
}

Per accedere al valore del campo, utilizza i metodi della funzione di accesso set() e get() oppure la sintassi della proprietà Kotlin:

Kotlin

user.firstName = "Google"
val age = user.age

Java

user.firstName.set("Google");
int age = user.age.get();

Raccolte osservabili

Alcune app utilizzano strutture dinamiche per conservare i dati. Le raccolte osservabili consentono di accedere a queste strutture tramite una chiave. La classe ObservableArrayMap è utile quando la chiave è un tipo di riferimento, come String, come mostrato nell'esempio seguente:

Kotlin

ObservableArrayMap<String, Any>().apply {
    put("firstName", "Google")
    put("lastName", "Inc.")
    put("age", 17)
}

Java

ObservableArrayMap<String, Object> user = new ObservableArrayMap<>();
user.put("firstName", "Google");
user.put("lastName", "Inc.");
user.put("age", 17);

Nel layout puoi trovare la mappa utilizzando le chiavi stringa, come mostrato nell'esempio seguente:

<data>
    <import type="android.databinding.ObservableMap"/>
    <variable name="user" type="ObservableMap&lt;String, Object&gt;"/>
</data>
…
<TextView
    android:text="@{user.lastName}"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>
<TextView
    android:text="@{String.valueOf(1 + (Integer)user.age)}"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>

La classe ObservableArrayList è utile quando la chiave è un numero intero, come segue:

Kotlin

ObservableArrayList<Any>().apply {
    add("Google")
    add("Inc.")
    add(17)
}

Java

ObservableArrayList<Object> user = new ObservableArrayList<>();
user.add("Google");
user.add("Inc.");
user.add(17);

Nel layout puoi accedere all'elenco tramite gli indici, come mostrato nell'esempio seguente:

<data>
    <import type="android.databinding.ObservableList"/>
    <import type="com.example.my.app.Fields"/>
    <variable name="user" type="ObservableList&lt;Object&gt;"/>
</data>
…
<TextView
    android:text='@{user[Fields.LAST_NAME]}'
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>
<TextView
    android:text='@{String.valueOf(1 + (Integer)user[Fields.AGE])}'
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>

Oggetti osservabili

Una classe che implementa l'interfaccia Observable consente la registrazione degli ascoltatori che vogliono ricevere una notifica in caso di modifiche alle proprietà dell'oggetto osservabile.

L'interfaccia Observable dispone di un meccanismo per aggiungere e rimuovere i listener, ma sei tu a decidere quando inviare le notifiche. Per semplificare lo sviluppo, la libreria DataBinding fornisce la classe BaseObservable, che implementa il meccanismo di registrazione listener. La classe di dati che implementa BaseObservable è responsabile dell'invio di una notifica quando le proprietà cambiano. A questo scopo, assegna un'annotazione Bindable al getter e chiama il metodo notifyPropertyChanged() nel setter, come mostrato nell'esempio seguente:

Kotlin

class User : BaseObservable() {

    @get:Bindable
    var firstName: String = ""
        set(value) {
            field = value
            notifyPropertyChanged(BR.firstName)
        }

    @get:Bindable
    var lastName: String = ""
        set(value) {
            field = value
            notifyPropertyChanged(BR.lastName)
        }
}

Java

private static class User extends BaseObservable {
    private String firstName;
    private String lastName;

    @Bindable
    public String getFirstName() {
        return this.firstName;
    }

    @Bindable
    public String getLastName() {
        return this.lastName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
        notifyPropertyChanged(BR.firstName);
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
        notifyPropertyChanged(BR.lastName);
    }
}

L'associazione di dati genera una classe denominata BR nel pacchetto del modulo, che contiene gli ID delle risorse utilizzate per l'associazione di dati. L'annotazione Bindable genera una voce nel file della classe BR durante la compilazione. Se la classe base per le classi di dati non può essere modificata, puoi implementare l'interfaccia Observable utilizzando un oggetto PropertyChangeRegistry per registrare e inviare notifiche ai listener in modo efficiente.

Oggetti sensibili al ciclo di vita

I layout nella tua app possono anche essere associati a origini di associazione di dati che segnalano automaticamente all'interfaccia utente le modifiche ai dati. In questo modo, le associazioni sono sensibili al ciclo di vita e vengono attivate solo quando l'interfaccia utente è visibile sullo schermo.

L'associazione di dati supporta StateFlow e LiveData. Per ulteriori informazioni sull'utilizzo di LiveData nell'associazione di dati, consulta Utilizzare LiveData per notificare l'interfaccia utente in caso di modifiche ai dati.

Utilizza StateFlow

Se la tua app utilizza Kotlin con le coroutine, puoi utilizzare gli oggetti StateFlow come origine di associazione di dati. Per utilizzare un oggetto StateFlow con la classe di associazione, specifica un proprietario del ciclo di vita per definire l'ambito dell'oggetto StateFlow. L'esempio seguente specifica l'attività come proprietario del ciclo di vita dopo la creazione dell'istanza della classe di associazione:

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.lifecycleOwner = this
    }
}

Come descritto in Associa le viste del layout ai componenti dell'architettura, l'associazione di dati funziona perfettamente con gli oggetti ViewModel. Puoi utilizzare StateFlow e ViewModel insieme nel seguente modo:

class ScheduleViewModel : ViewModel() {

    private val _username = MutableStateFlow<String>("")
    val username: StateFlow<String> = _username

    init {
        viewModelScope.launch {
            _username.value = Repository.loadUserName()
        }
    }
}

Nel layout, assegna le proprietà e i metodi dell'oggetto ViewModel alle viste corrispondenti utilizzando espressioni di associazione, come mostrato nell'esempio seguente:

<TextView
    android:id="@+id/name"
    android:text="@{viewmodel.username}" />

L'interfaccia utente si aggiorna automaticamente ogni volta che il valore del nome dell'utente cambia.

Disabilita il supporto di StateFlow

Per le app che utilizzano Kotlin e AndroidX, il supporto di StateFlow è incluso automaticamente nell'associazione di dati. Ciò significa che la dipendenza delle coroutine viene inclusa automaticamente nell'app se la dipendenza non è già disponibile.

Puoi disattivare questa funzionalità aggiungendo quanto segue al tuo file build.gradle:

Trendy

android {
    ...
    dataBinding {
        addKtx = false
    }
}

Kotlin

android {
    ...
    dataBinding {
        addKtx = false
    }
}

In alternativa, puoi disabilitare StateFlow a livello globale nel progetto aggiungendo la seguente riga al file gradle.properties:

Trendy

android.defaults.databinding.addKtx = false

Kotlin

android.defaults.databinding.addKtx = false

Risorse aggiuntive

Per scoprire di più sull'associazione di dati, consulta le risorse aggiuntive riportate di seguito:

Samples

Codelab

Post del blog