Praca z obserwowalnymi obiektami danych

Dostrzegalność to zdolność obiektu do powiadamiania innych osób o zmianach w jego danych. Biblioteka powiązań danych umożliwia obserwację obiektów, pól i kolekcji.

Do powiązania danych możesz użyć dowolnego obiektu, ale modyfikacja obiektu nie spowoduje automatycznej aktualizacji interfejsu użytkownika. Dzięki wiązaniom danych obiekty danych mogą powiadamiać inne obiekty (tzw. detektory) o zmianie danych. Istnieją 3 typy klas możliwych do obserwacji: pola, kolekcje i obiekty.

Gdy jeden z tych możliwych do obserwacji obiektów danych jest powiązany z interfejsem użytkownika, a jego właściwość zmieni się, interfejs aktualizuje się automatycznie.

Obserwowalne pola

Jeśli klasy mają tylko kilka właściwości, utworzenie klas, które implementują interfejs Observable, może być nieopłacalne. W takim przypadku możesz użyć ogólnej klasy Observable i poniższych klas specyficznych dla obiektów podstawowych, aby umożliwić obserwację pól:

Pola obserwowane to niezależne obserwowalne obiekty, które mają jedno pole. Wersje podstawowe zapobiegają pakowaniu i rozpakowywaniu podczas operacji dostępu. Aby użyć tego mechanizmu, utwórz właściwość public final w języku programowania Java lub właściwość tylko do odczytu w Kotlin, jak pokazano w poniższym przykładzie:

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();
}

Aby uzyskać dostęp do wartości pola, użyj metod akcesora set() i get() lub użyj składni właściwości Kotlin:

Kotlin

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

Java

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

Obserwowalne kolekcje

Niektóre aplikacje przechowują dane za pomocą dynamicznych struktur. Dostrzegalne zbiory umożliwiają dostęp do tych struktur za pomocą klucza. Klasa ObservableArrayMap jest przydatna, gdy klucz jest typem odwołania, takim jak String, jak w tym przykładzie:

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);

Mapę możesz znaleźć w układzie za pomocą klawiszy ciągu znaków, jak w tym przykładzie:

<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"/>

Klasa ObservableArrayList jest przydatna, gdy klucz jest liczbą całkowitą:

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);

W układzie listy dostęp do listy możesz uzyskać przez indeksy, jak w tym przykładzie:

<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"/>

Obserwowalne obiekty

Klasa implementująca interfejs Observable umożliwia rejestrowanie detektorów, którzy chcą otrzymywać powiadomienia o zmianach właściwości obiektu obserwowalnego.

Interfejs Observable ma mechanizm dodawania i usuwania odbiorników, ale to Ty decydujesz, kiedy powiadomienia mają być wysyłane. Aby ułatwić programowanie, biblioteka wiązań danych udostępnia klasę BaseObservable, która implementuje mechanizm rejestracji detektora. Klasa danych, która implementuje BaseObservable, jest odpowiedzialna za powiadamianie o zmianach właściwości. Aby to zrobić, przypisz do metody pobierania adnotację Bindable i wywołaj metodę notifyPropertyChanged() w metodzie ustawiającej, jak w tym przykładzie:

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);
    }
}

Powiązanie danych powoduje wygenerowanie w pakiecie modułu klasy o nazwie BR, która zawiera identyfikatory zasobów używanych do wiązania danych. Adnotacja Bindable generuje wpis w pliku klasy BR podczas kompilacji. Jeśli nie można zmienić klasy podstawowej na potrzeby klas danych, możesz wdrożyć interfejs Observable za pomocą obiektu PropertyChangeRegistry, aby skutecznie rejestrować i powiadamiać detektory.

Obiekty rozpoznające cykl życia

Układy w aplikacji mogą też być powiązane ze źródłami powiązań danych, które automatycznie powiadamiają interfejs użytkownika o zmianach w danych. Dzięki temu powiązania są uwzględniane w cyklu życia i aktywowane tylko wtedy, gdy interfejs użytkownika jest widoczny na ekranie.

Powiązanie danych obsługuje StateFlow i LiveData. Więcej informacji o korzystaniu z funkcji LiveData do wiązania danych znajdziesz w artykule Używanie LiveData do powiadamiania interfejsu użytkownika o zmianach danych.

Użyj StateFlow

Jeśli Twoja aplikacja używa Kotlin w połączeniu z korutynami, jako źródła wiązań danych możesz używać obiektów StateFlow. Aby użyć obiektu StateFlow z klasą wiązania, określ właściciela cyklu życia w celu określenia zakresu obiektu StateFlow. Ten przykład określa aktywność jako właściciela cyklu życia po utworzeniu wystąpienia klasy powiązania:

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
    }
}

Jak opisano w sekcji Powiąż widoki układu z komponentami architektury, wiązanie danych działa bezproblemowo z obiektami ViewModel. Aplikacji StateFlow i ViewModel można używać jednocześnie w następujący sposób:

class ScheduleViewModel : ViewModel() {

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

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

W swoim układzie przypisz właściwości i metody obiektu ViewModel do odpowiednich widoków, używając wyrażeń powiązań, jak pokazano w tym przykładzie:

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

Interfejs jest automatycznie aktualizowany po każdej zmianie wartości nazwy użytkownika.

Wyłącz obsługę StateFlow

W przypadku aplikacji korzystających z Kotlin i AndroidaX obsługa StateFlow jest automatycznie dodawana wraz z powiązaniem danych. Oznacza to, że zależność od współuczestników jest automatycznie dodawana do aplikacji, jeśli nie jest jeszcze dostępna.

Możesz zrezygnować z tej funkcji, dodając do pliku build.gradle ten fragment:

Odlotowy

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

Kotlin

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

Możesz też wyłączyć usługę StateFlow globalnie w projekcie, dodając ten wiersz do pliku gradle.properties:

Odlotowy

android.defaults.databinding.addKtx = false

Kotlin

android.defaults.databinding.addKtx = false

Dodatkowe materiały

Więcej informacji o wiązaniach danych znajdziesz w tych artykułach:

Próbki

Ćwiczenia z programowania

Posty na blogu