410013796724260
• Webmoney
R335386147728
Z369087728698
Вернуться к «Разработчикам Android»
Model-View-ViewModel в AndroidЗаметка : данная статья является продолжением описания комплексного использования Data Binding и MVVM (Model-View-ViewModel). Процесс разработки android-приложения обычно предполагает создание пользовательского интерфейса и определение java-кода, который взаимодействует с интерфейсом. При расширении функций приложения могут возникать сложности, не предусмотренные на этапе первичного проектирования и разработки. Эти проблемы включают тесную связь между компонентами интерфейса и бизнес-логикой, что увеличивает затраты на внесение изменений пользовательского интерфейса и сложность модульного тестирования кода. Шаблон Model-View-ViewModel (MVVM) позволяет четко отделить интерфейс представления от бизнес-логики (java-кода). Поддержка разделения логики приложения и пользовательского интерфейса помогает исключить многочисленные проблемы разработки и упростить тестирование, обслуживание и развитие приложения. MVVM также может значительно улучшить возможности повторного использования кода и позволяет программистам и дизайнерам пользовательского интерфейса упростить групповую разработку соответствующих частей приложения. MVVM — это шаблон определения архитектуры клиентских приложений, предложенный Джоном Госсманом (John Gossman) как альтернатива шаблонам MVC и MVP при использовании технологии связывания данных (Data Binding). Концепция MVVM связана с отделением логики представления данных от бизнес-логики путем вынесения её в отдельный класс для более четкого разграничения. В шаблоне MVVM есть три основных компонента : модель (Model), представление (View) и модель представления (ViewModel). Каждый из компонентов выполняет отдельную функцию. На скриншоте показаны связи между тремя компонентами. ![]() Рассмотрим каждый из компонентов шаблона Model-View-ViewModel :
Преимущества и недостаткиКакие преимущества несет технология использования Data Binding и MVVM :
Оборотная сторона медали, связанная с недостатками :
Сценарий использования Data Binding и MVVMРассмотрим пример использования технологии Data Binding и MVVM. Допустим, у нас имеется простой объект типа User, класс которого имеет следующий вид : Листинг User.java import androidx.annotation.NonNull; import androidx.databinding.Bindable; import androidx.databinding.BaseObservable; public class User extends BaseObservable { private int age; private String name; //--------------------------------- public User(int age, String name) { this.age = age; this.name = name; } //--------------------------------- @Bindable public int getAge() { return age; } //--------------------------------- public void setAge(int age) { this.age = age; notifyPropertyChanged(BR.age); notifyPropertyChanged(BR.adult); } //--------------------------------- @Bindable @NotNull(message="Name cannot be empty") public String getName() { return name; } //--------------------------------- public void setName(String name) { this.name = name; notifyPropertyChanged(BR.name); } @Bindable public boolean isAdult() { return age >= 18; } } Класс User имеет всего 2 поля (name, age) и методы обращения к ним Get/Set для определения и получения значений. Выделенные в листинге строки кода относятся к элементу шаблона ModelView для наблюдения за полями объекта. Для этого, класс User наследует свойства BaseObservable, а к методам Get (getName, getAge) добавляются аннотации @Bindable, согласно которым изменение значений этих полей/свойств будут контролироваться (наблюдаться). При этом, функция isAdult() также участвует в наблюдении, хотя физического представления поля adult объект не имеет. Аннотацию @Bindable можно использовать только в том случае, если класс наследует свойства BaseObservable. При изменении значений свойств объекта вызываются методы Set (setName, setAge), в которых выполняется вызов метода notifyPropertyChanged, который уведомляет объект связи Binding об изменениях значения поля. Аннотация @NotNull используется в классе для контроля за заполнением значения поля, которое не может быть пустым. Observable поляВ листинге класса User используется наблюдение за всем объектом, поскольку User расширяет (extends) свойства BaseObservable. Но можно следить не за всем объектом, а за отдельными его свойствами. Для этого имеются готовые Observable поля: ObservableBoolean, ObservableInt, ObservableFloat и т.п. Кроме этого, можно использовать поле ObservableField с указанием типа. Ниже представлен класс User, в котором осуществляется наблюдение не за всем объектом, а за его полями age, name : public class User { public ObservableInt age; public ObservableField<String> name; age = new ObservableInt(); name = new ObservableField<String>(); //--------------------------------- public User(int age, String name) { this.age .set( age); this.name.set(name); } //--------------------------------- } Примечание : а вот здесь мы не можем включить в наблюдение метод isAdult(), поскольку функция зависит от age, а реализация методов скрыта. Если включить в класс поле adult типа ObservableBoolean, то необходимо будет определять его значение при изменении age. Определение бизнес-логики в layoutНиже представлен листинг макета (layout) activity_main.xml, используемый в примере. Особенности использования Data Binding и MVVM в макете :
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:android="http://schemas.android.com/apk/res/android"> <data> <variable name="user" type="com.example.dbmvvm.User"/> <import type="android.view.View" /> </data> <androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <TextView android:id="@+id/userName" android:layout_width="160dp" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginTop="16dp" android:text="@{user.name}" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <TextView android:id="@+id/userAge" android:layout_width="160dp" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginTop="16dp" android:text="@{@string/age_format(user.age)}" android:visibility="@{user.adult ? View.VISIBLE : View.INVISIBLE}" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf= "@+id/userName" /> <Button android:id="@+id/button" android:layout_width="160dp" android:layout_height="48dp" android:layout_marginStart="16dp" android:layout_marginTop="16dp" android:text="@string/button" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf=" @+id/userAge" /> </androidx.constraintlayout.widget.ConstraintLayout> </layout> |