что такое data binding

Урок 18. Android Data Binding. Основы

В этом уроке знакомимся с Data Binding.

Полный список уроков курса:

В build.gradle файл модуля в секции android необходимо включить Data Binding:

Data Binding поможет организовать работу с View так, чтобы нам не пришлось писать кучу методов findViewById, setText, setOnClickListener и т.п. Давайте рассмотрим простой пример.

Есть класс Employee, который содержит в себе данные о работнике

поля: id, имя и адрес.

Мы хотим вывести имя и адрес работника на экран main_activity.xml:

Несложная задача. Для этого мы обычно пишем методы findViewById и setText. Тут все понятно.

Давайте рассмотрим, как это же можно сделать с помощью Data Binding.

Вносим изменения в main_activity.xml:

Обратите внимание, что мы не указываем id для View. В этом нет необходимости.

Как вы понимаете, нам остается лишь передать объект Employee в этот layout. И значения этого объекта будут подставлены в соответствующие TextView.

Это делается следующим образом.

Сначала создаем Employee объект.

Затем используем DataBindingUtil. Метод DataBindingUtil.setContentView внутри себя сделает привычный нам setContentView для Activity, а также настроит и вернет объект биндинга MainActivityBinding.

Метод setEmployee был сгенерирован в классе биндинга, т.к. мы описали переменную employee в layout файле. Этим методом мы передаем биндингу объект Employee. Биндинг возьмет значения employee.name и employee.address и поместит их (методом setText) в соответствующие TextView. Все, как мы и настраивали в layout.

Данные из Employee помещены в TextView.

Сразу хочу заметить, что если мы теперь в коде будем изменять объект Employee, то данные на экране меняться не будут. Они считались один раз и далее не отслеживаются (при такой реализации).

Чтобы экран получил новые данные, надо снова передать биндингу измененный объект Employee:

Или можно вызывать метод invalidateAll:

Биндинг считает новые данные с ранее полученного объекта Employee.

Поля в Employee в этом примере я сделал public. Но вы можете сделать их private и создать для них public get методы.

Возможно, использование биндинга для пары TextView кажется бессмысленным. Но когда таких TextView десятки, то биндинг может избавить вас от написания кучи кода.

Эти возможности мы рассмотрим в следующих уроках.

Присоединяйтесь к нам в Telegram:

— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.

— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование

— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня

— новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме

Источник

Урок 8. Android Data Binding – основы

Продолжаем курс по обучению основам разработки мобильных приложений в Android Studio на языке Kotlin. В этом уроке познакомимся с Android Data Binding.

Data Binding Library

Библиотека Data Binding Library, которая является частью Android Jetpack, позволяет привязывать компоненты пользовательского интерфейса в макетах к источникам данных в приложении, используя декларативный формат, а не программно. Другими словами, Data Binding поможет организовать работу с View так, чтобы нам не пришлось писать кучу методов findViewById, setText, setOnClickListener и т.п.

Чтобы более тесно на практике познакомиться с чистой архитектурой и архитектурными компонентами, записывайтесь на продвинутый курс по разработке приложения «Чат-мессенжер»

В этом цикле уроков вы узнаете, как настроить Data Binding в проекте, что такое layout expressions, как работать с observable objects и как создавать кастомные Binding Adapters чтобы свести избыточность (boilerplate) вашего кода к минимуму.

С чего начать

В этом уроке, мы возьмем уже существующий проект и конвертируем его в Data Binding:

Приложение имеет один экран, который показывает некоторые статические данные и некоторые наблюдаемые данные, что означает, что при изменении данных пользовательский интерфейс будет автоматически обновляться. Данные предоставлены ViewModel. Model-View-ViewModel — это шаблон уровня представления, который очень хорошо работает с Data Binding. Вот диаграмма паттерна MVVM:

Если вы еще не знакомы с классом ViewModel из библиотек компонентов архитектуры, вы можете посмотреть официальную документацию. Мы знакомились с этим классом на прошлом уроке, ссылку на который вы видите в правом верхнем углу этого видео. Это класс, который предоставляет состояние пользовательского интерфейса для представления (Activity, Fragment, т.п.). Он выдерживает изменения ориентации и действует как интерфейс для остальных слоев вашего приложения.

Что нам понадобится

На этом этапе мы загрузим и запустим простое приложение-пример. Выполните в консоли команду:

Вы также можете клонировать или скачать репозиторий в виде Zip-файла по ссылке на GitHub

Экран по умолчанию открывается и выглядит так:

Этот экран отображает несколько различных полей и кнопку, по нажатию которой можно увеличить счетчик, обновить индикатор выполнения и изображение. Логика действий, происходящих на экране, описана в классе SimpleViewModel. Откройте его и посмотрите.

Используйте Ctrl + N, чтобы быстро найти класс в Android Studio. Используйте Ctrl + Shift + N, чтобы найти файл по его имени.

На Mac найдите класс с помощью Command + O и файл с помощью Command + Shift + O.

В классе SimpleViewModel описаны такие поля:

Кроме того, он позволяет пользователю увеличивать количество лайков методом onLike().

Пока SimpleViewModel содержит не самый интересный функционал, но здесь все в порядке. С другой стороны, класс главного экрана MainActivity имеет ряд проблем:

В нашей серии уроков с помощью библиотеки Data Binding мы собираемся исправить все эти проблемы, переместив логику из активити в места, где ее можно повторно использовать и проще тестировать.

Подключаем Data Binding в проект

Первым шагом является включение библиотеки Data Binding в модули, которые будут ее использовать. Добавьте такие строки в файл сборки модуля app:

Источник

28 марта 2016 г. DataBinding: ускоряем разработку приложений под Android

Каждый раз при виде километров строк с findViewById и «тернарок» с visibility у тебя дергается глаз? Знай — выход есть! Подробности — под катом.

1. Что за за зверь

Ни для кого не секрет, что самая скучная часть разработки приложений — это описывание поведения UI в зависимости от логики изменения данных. Сколько слез было пролито при написании (а в последствии и в поддержке) тривиального и почти бесполезного «треша», который занимал десятки, а то и сотни строк в классах практически каждой активности или фрагмента. О читабельности и вовсе не стоит говорить — разобраться, а тем более найти баг (который очень-то просто допустить) в джунглях однообразной логики — то еще приключение.

Вывод очевиден: нужно каким-то образом это автоматизировать. Как раз эту задачу и выполняет DataBinding — библиотека, с помощью которой максимально минимизируется связующий код между логикой приложения и ее представлением.

2. Интеграция

Чтобы продемонстрировать возможности сего чуда мы напишем небольшое приложение на примере профиля пользователя. Но прежде чем приступить давайте подключим наш волшебный инструмент к проекту.

Для интеграции биндинга нам необходимо использовать плагин Gradle версии не ниже, чем 1.5.0, поэтому обновим файл build.gradle проекта, добавив следующую строку:

Далее остается добавить элемент dataBinding в модульный build.gradle:

Синхронизируем Gradle и радуемся передовым технологиям в нашем проекте 🙂

3. Пробный заезд

Ну что ж, теперь можно создавать наш пример. Начнем с простого — создадим активность, в которой будут отображаться основные данные о пользователе, а именно: имя, фамилия, статус и индикатор «онлайн».

Ничего сложного в задаче нет, так что приступим к реализации. Создадим модель User с нужными нам полями:

Читайте также:  что делать если у тебя в ушах вода

Следующим шагом создадим разметку. Ограничимся простыми TextView для отображения имени и статуса, и View — для индикатора:

Давайте вкратце пройдемся по всем странностям в разметке:

Теперь, по завершению всех необходимых шагов, давайте посмотрим, как будет выглядеть наш класс активности:

Здесь с помощью DataBindingUtil мы инициализируем лэйаут и в ответ получаем любезно сгенерированный для нас ActivityMainBinding (по умолчанию Binding-класс генерируется на основе имени файла макета переведенного в CamelCase с добавлением суффикса «Binding»), в котором и хранятся все ссылки на наши элементы интерфейса. Для того, чтобы заполнить их данными нужно просто задать объект пользователя с помощью метода setUser (название этого метода зависит от именования переменной в поле name блока variable).

Посмотрим на результат:

Да, этого кода достаточно для того, чтобы отобразить нужные нам данные 🙂 Но.

4. Это еще цветочки

У DataBinding есть собственный язык выражений для файлов разметки. Он в точности соответствует выражениям в Java и впечатляет своими возможностями. Ниже приведен перечень всех доступных операторов:

Однако, для того, чтобы файл разметки не превратился в место, где можно писать всю возможную логику, есть и несколько неподдерживаемых операторов:

Нельзя не упомянуть и так называемый Null Coalescing Operator «??», который достаточно лаконично позволяет решить множество проверок на null. Вот как это выглядит:

Обе строки кода эквивалентны. Ну разве не находка? 🙂

Код, сгенерированный библиотекой DataBinding, также автоматически проверяет все объекты и предотвращает NullPointerException. Например, если в выражении @ поле status является null, то его значением будет являться значение по умолчанию — т.е. «null». Этот принцип работает и для примитивных типов данных.

Знакомый нам тег имеет еще одно свойство — с его помощью можно импортировать типы, необходимые нам для работы. К тому же, их можно сокращать для удобства и экономии места с помощью alias.

Работа с ресурсами здесь заслуживает отдельных похвал — практически все возможные ресурсы можно вызывать напрямую и комбинировать с логическими операторами. В примере с индикатором «онлайн» мы уже видели одну из реализаций — в атрибут android:background подставлялся нужный Drawable в зависимости от того, находится ли пользователь в сети. Так вот, представьте себе гибкость разметки, используя все допустимые ссылки:

Тип Нормальная ссылка Ссылка в выражении
String[] @array @stringArray
int[] @array @intArray
TypedArray @array @typedArray
Animator @animator @animator
StateListAnimator @animator @stateListAnimator
Color int @color @color
ColorStateList @color @colorStateList

Помимо всего перечисленного, не остается без внимания и @BindingAdapter. С его помощью можно переопределять поведение существующих атрибутов и создавать свои, не думая о attrs.xml.

Для этого нужно создать публичный статический метод, на вход которому будет приходить View нужного нам типа и значение, которое мы указываем в разметке. Сам метод нужно пометить аннотацией @BindingAdapter и в ее теле указать строку с именем атрибута.

К примеру, такой адаптер позволяет «повесить» на view любой метод без параметров в качестве OnClickListener:

Адаптеры также можно создавать для нескольких параметров одновременно. Например, так можно задать url на изображение и ресурс на случай ошибки при загрузке:

Но бывают случаи, когда необходимо преобразовать типы автоматически (например boolean в int (Visibility)). Тогда на смену адаптеру приходит @BindingConversion:

Он отличается тем, что имеет возвращаемый тип, который потом применяется к атрибуту, в то время как адаптер полностью уводит поведение на себя.

5. Пишем себе в удовольствие

Расширим наше приложение дополнительными функциями. Во-первых, в профиле отсутствует фотография, что немаловажно. Во-вторых, отсутствие каких-либо действий делают его практически бесполезным.

Итак, первым делом добавим фотографию пользователя и возможность добавить его в друзья. Допишем необходимые поля в модель User:

Затем разместим в разметке ImageView для аватара профиля:

Для загрузки изображения сработает написанный нами ранее адаптер loadImage. Теперь добавим кнопки добавления и удаления из френдлиста:

Вы, наверное, заметили, что объект user был переименован на viewModel, хотя на самом деле это совсем другой объект. ViewModel — это связующее звено между View и моделью данных, в которой описывается вся логика поведения. О модели MVVM (Model-View-ViewModel) мы поговорим в следующий статье и подробно разберем как правильно ее реализовать, а пока просто запомним, что логика поведения должна размещаться в отдельном объекте. Вот наша VM для профиля:

Как видно, VM наследуется от BaseObservable. Это дает нам возможность оповещать биндинг о изменениях внутри с помощью notifyPropertyChanged, куда передается BR.variable. Название переменной генерируется так же, как id в стандартном R-классе, а помечаются они аннотацией @Bindable (в нашем случае — в геттерах).

Поле isLoaded будет служить флагом для индикатора загрузки, который будет переключаться на 1 секунду каждый раз, при вызове changeFriendshipStatus. Осталось только немного модифицировать лэйаут с кнопками и добавить ProgressBar:

Теперь каждый раз, когда мы будем нажимать на кнопку удаления или добавления в друзья — RelativeLayout будет заменяться ProgressBar’ом на секунду и возвращаться обратно. Самое время запустить и посмотреть на результат.

6. Дальше — интересней

ObservableField представляют собой автономные наблюдаемые объекты, которые имеют одно поле. К нему можно обращаться с помощью методов get() и set(), которые автоматически оповещают View об изменениях. Чтобы использовать его, нужно просто создать public final поле в классе VM. Для этого обновим нашу ProfileViewModel:

Нетрудно заметить, насколько меньше стало кода, хоть он и выполняет все те же функции, что и раньше. Стоит заметить, что почти для каждого примитивного типа есть его Observable-аналог. В свою очередь мы использовали ObservableBoolean.

В библиотеке, к сожалению, еще не реализован двусторонний биндинг, но не стоит расстраиваться — у нас есть все, что бы справиться с этой задачей самостоятельно! Двусторонний биндинг — это когда не только данные влияют на то, что отображается во View, но и наоборот — когда введенные пользователем данные изменяют модель. Давайте попробуем сделать это на примере EditText.

Первым делом обратим внимание на то, что по каким-то причинам отсутствует ObservableString, а потому создадим свой:

Логика простая и в пояснениях, не нуждается, поэтому перейдем к реализации адаптера. Основной трюк в том, чтобы добавить TextWatcher к EditText и в колбэке обновлять наш ObservableString:

Теперь можно просто указать в XML наш ObservableString и дело в шляпе!

7. Сладости в придачу

За время использования библиотеки у нашей команды появился ряд наработок, которые каждый день упрощают нам жизнь. Вот некоторые из них:

RecyclerBindingAdapter — это универсальный адаптер для простых списков. Один и на весь проект. Больше не нужно создавать их каждый раз отдельно для каждого списка 🙂 Давайте взглянем, как это происходит:

А создать его можно всего в одну строку:

BR.data — это имя variable в xml-файле, а list — наша выборка. Задаем адаптер RecyclerView и забываем о головной боли раз и навсегда. Забегая наперед, задам вопрос: а как нам конфигурировать RecyclerView не обращаясь к биндингу напрямую? Здесь-то и напрашивается следующая фича — RecyclerConfiguration:

Эта простая обертка не засоряет код ObservableFileds позволяет указывать конфигурацию прямо в XML, предварительно заполнив ее из кода.

Немного дописав ViewModel, мы получим вот такую имитацию realtime-событий:

8. Ложка дегтя

На сегодняшний день DataBinding находится на стадии разработки, а потому при использовании проявляется целый ряд неприятностей. К ним можно отнести отсутствие двустороннего биндинга и некоторых полей «из коробки». Помимо этого есть и проблемы с поддержкой в Android Studio: часто выражения в разметке распознаются как ошибки, пропадает класс BR или весь пакет binding целиком (решается с помощью Build → Clean Project). В разметке есть проблемы с кодированием (к примеру, вместо оператора «&&» приходится писать «&&») и прочие. Но стоит рассматривать эти проблемы как временные неудобства, ведь разработка ведется активно, а технология стоит того, чтобы немножко потерпеть 🙂

Читайте также:  что значит девушка обабилась

9. Заключение

Можно еще долго описывать возможности этой замечательной библиотеки, ведь они практически безграничны, но все хорошее рано или поздно заканчивается. Надеюсь, у нас получилось заинтересовать вас и наш опыт будет вам полезен.

В следующей статье мы рассмотрим, как лучше всего реализовать MVVM-архитектуру приложения, которая так тесно связана с DataBiding.

Спасибо за внимание!

Исходный код приложения на GitHub.

Нужен MVP, разработка под iOS, Android или прототип приложения? Ознакомьтесь с нашим портфолио и сделайте заказ уже сегодня!

Источник

Лекция 8 по архитектуре Android. Data Binding + MVVM

Восьмая лекция курса по архитектуре клиент-серверных android-приложений, в которой мы рассмотрим паттерн Model-View-ViewModel и Data Binding.

Чтобы на практике познакомиться с архитектурным паттерном MVVM и Data Binding, записывайтесь на продвинутый курс по разработке приложения «Чат-мессенжер»

Введение

В рамках предыдущих лекций мы рассмотрели наиболее актуальные варианты клиент-серверного взаимодействия и архитектурные паттерны. Мы можем строить архитектуру приложения, чтобы писать чистый и понятный код. Мы также умеем тестировать приложение на самых разных уровнях. То есть мы достигли всех целей, которые ставились в начале курса. Поэтому на этом этапе можно было закончить рассмотрение курса, но он был бы неполным без рассмотрения последней темы – паттерна MVVM. В пятой лекции мы разбирали самые известные паттерны, позволяющие разделить работу с UI и логику приложения. Мы обсудили, почему в Android не получается использовать паттерн MVC, и в деталях разобрали паттерн MVP. Из самых известных паттернов мы обошли стороной только MVVM, и теперь настала пора исправить это упущение.

MVVM – это сокращение для Model-View-ViewModel. Мы хорошо знаем, за что отвечают View и Model, поэтому нам осталось понять, что такое ViewModel и чем он отличается от Controller и Presenter.

Ключевой особенностью паттерна MVVM является то, что ни один компонент (Model, View, ViewModel) не знает о другом явно. Эти компоненты взаимодействуют между собой за счет механизма связывания данных (Bindings), который реализуется средствами той или иной системы. При этом изменение данных во ViewModel автоматически меняет данные, отображаемые во View. Аналогично, любое событие или изменение данных во View (нажатие на кнопку, ввода текста и другое) изменяет данные во ViewModel. Это позволяет не хранить явные ссылки на View во ViewModel и наоборот, а также держать эти компоненты очень слабо связными, что удобно при тестировании.

Разумеется, такое связывание данных выполняется не само по себе и не магическим образом, а должно реализовываться за счет каких-то фреймворков. В Android таким фреймворком стал Data Binding, который был представлен на конференции Google I/O 2015. Поэтому, прежде чем двигаться дальше в рассмотрении паттерна MVVM, нужно изучить этот фреймворк.

Data Binding

Data Binding – это фреймворк от Google, который позволяет выполнить связывание Java-кода и xml-файлов. При этом можно полностью избавиться от работы со View в Java-коде. И более того, это избавляет от рутинных вызовов для изменения View. С помощью Data Binding View в xml-разметке можно задать любые свойства, что очень удобно и скрывает некоторые детали реализации.

Подключить Data Binding очень легко – для этого нужно добавить пару строк в build.gradle:

Чтобы сразу понять, о чем идет речь и как можно применять Data Binding, давайте рассмотрим простой пример. Допустим, у нас есть экран, на котором отображается имя пользователя и его возраст. Данные пользователя определяются классом User, в котором определены два поля и методы для получения значений этих полей. Для решения этой задачи мы бы создали примерно такую xml-разметку:

И всем полям в Java-коде задали бы текст, используя экземпляр класс User:

С использованием Data Binding мы могли бы сделать это немного проще и, что самое главное, лучше для понимания:

Код уменьшился незначительно, но во втором варианте мы можем сразу сказать, что данные пользователя передаются для отображения в xml-файл, и нам не нужно знать, как именно они отображаются, то есть мы избавляемся от низкоуровневых подробностей.

Конечно, в таком случае нужно модифицировать и файл разметки. Во-первых, корневым элементом разметки должен стать layout. Во-вторых, мы должны объявить все переменные, которые будут дальше использоваться, внутри тега :

И как видно, нам даже не нужно получать View из Java-кода, чтобы менять поля во View, что может сэкономить нам немало кода.

В xml-разметке можно писать и выражения, например, стандартный тернарный оператор. Например, у нас есть текст, который можно показывать только совершеннолетним пользователям, а для остальных его нужно скрывать. Тогда мы можем написать следующую разметку:

Чтобы такой код заработал, нужно сделать две вещи: во-первых, добавить метод isAdult в класс User:

И импортировать класс View в разметку:

В разметке можно также вызывать методы любых импортированных классов, писать лямбда-выражения и многое другое. Но не нужно реализовывать какую-то сложную логику в xml, так как это будет можно протестировать только с помощью UI-тестов. Вы должны использовать Data Binding, чтобы избежать частой работы со View и рутинной установкой значений, но никак не для того, чтобы писать всю логику в xml.

Разумеется, в таком подходе нет никакой магии. Data Binding по созданным xml-файлам генерирует специальные классы, которые и управляют всеми процессами передачи данных от ViewModel к View и наоборот.

Продолжим рассмотрение возможностей Data Binding. Как уже говорилось, для реализации паттерна МVVM фреймворк Data Binding должен обеспечить возможность связывания данных, то есть чтобы при изменении данных во ViewModel они автоматически менялись и в отображаемой разметке.

Рассмотрим связывание от модели к разметке. Допустим, мы написали код, который присваивает всем View начальные данные, а теперь хотим изменить данные модели:

В такой ситуации мы бы хотели, чтобы данные в разметке автоматически обновились, чтобы нам не пришлось заново вызывать binding.setUser при каждом изменении. И это возможно, нужно только внести небольшие изменения в класс User.

Чтобы указать, что значение может меняться и что эти изменения нужно отслеживать, используется аннотация Bindable, которая применяется к методу для получения значения какого-то поля:

Если после этого выполнить сборку проекта, то мы увидим, что в сгенерированном классе BR (этот класс очень похож на класс R – класс всех ресурсов, только служит для идентификаторов всех свойств) появится поле age. Это поле используется для уведомления о том, что связанное поле класса было изменено. Дальше нужно унаследовать класс User от BaseObservable:

И теперь мы можем использовать метод notifyPropertyChanged и передать ему идентификатор изменившегося свойства:

Использовать связывание нужно очень аккуратно. Например, здесь мы забыли, что от значения поля mAge зависит не только сам возраст, но и значение свойства adult. Поэтому корректная реализация метода setAge выглядит следующим образом:

Есть и другие способы для реализации связывания, например, можно использовать классы ObservableInt, ObservableField и другие. Так работает связывание от ViewModel к View. Обратное связывание (например, ввод текста в EditText) выполняется аналогично.

Еще один важный момент, который нужно учитывать. После присвоения данных во ViewBinding (вызов binding.setUser) они отображаются во View не мгновенно, а с небольшой задержкой. Это сделано для того, чтобы отрисовки изменений совпадали с тактами вызова перерисовки системой. Но если вам нужно, чтобы после задания модели данные отобразились мгновенно, нужно использовать метод executePendingBindings:

Читайте также:  что значит с отчет с гашением

Еще одной крайне важной возможностью Data Binding являются кастомные атрибуты. До этого момента мы задавали значения View только с помощью стандартных методов и атрибутов. Но точно также можно создавать и свои атрибуты. Например, мы бы хотели задать ImageView url изображения с помощью атрибута android:src, и чтобы Data Binding загрузил картинку по url:

Разумеется, Data Binding не может знать о том, что нужно загрузить картинку и каким образом это сделать, зато он позволяет нам обработать такую ситуацию. Для этого нужно написать свой метод для обработки атрибута и в нем реализовать все действия, которые требуются (загрузить картинку с помощью Picasso):

В аннотации BindingAdapter передается атрибут или список атрибутов, которые будут обрабатываться статическим методом. В качестве аргументов методу передаются значения, которые используются в xml-разметке.

С помощью таких кастомных атрибутов можно перенести в xml почти все приложение, но нужно снова понимать, что такие адаптеры не предназначены для написания сложной логики, а только для нашего удобства.

Конечно, это далеко не все возможности Data Binding, но для дальнейшего использования этого фреймворка при построении архитектуры с MVVM нам хватит рассмотренных примеров. Узнать больше о том, что еще позволяет делать Data Binding, вы можете в документации или в статьях, ссылки на которые есть в конце лекции.

Model-View-ViewModel

Примеры, рассмотренные при изучении возможностей Data Binding, были общими и не строились в рамках архитектуры. Теперь нам осталось встроить Data Binding в архитектуру с паттерном MVVM и посмотреть, какие плюсы мы получим и с какими проблемами столкнемся.

В большинстве примеров не рассматривается использование чистого MVVM в Android-приложениях. Обычно Data Binding используется в качестве вспомогательного инструмента для взаимодействия View и Presenter. То есть мы передаем уже не какие-то отдельные данные для отображения во View, а целые объекты, которые передаются во ViewModel и отображаются через xml с Data Binding. Такая схема позволяет упростить View (в частности логику реализации методов View и количество методов в интерфейсе), но она же и добавляет лишние прослойки для взаимодействия. Такой способ можно использовать для сложных экранов, когда часть работы хотелось бы перенести в xml. Это небольшое упрощение, которое, разумеется, можно использовать на практике. Но здесь нет ничего принципиально нового, что заслуживало бы отдельного рассмотрения. Знаний паттерна MVP и фреймворка Data Binding вполне достаточно для того, чтобы применять такой подход.

Но это тривиальный вариант использования Data Binding, который не дает видимых преимуществ, так как мы только увеличиваем количество классов. К тому же, это все еще паттерн MVP, а не MVVM, который мы хотим рассмотреть. Чтобы можно было получить выгоду от использования Data Binding и паттерна MVVM, нужно попробовать использовать другой подход, который бы действительно мог упростить взаимодействие.

В чем может заключаться это упрощение? В паттерне MVP класс, ответственный за логику приложения, передавал команды View через интерфейс, и Presenter со View были связаны друг с другом. То есть условно схему взаимодействия можно представить следующим образом:

То есть делегат (Presenter) каким-то образом получает данные, выполняет над ними определенные преобразования в соответствии с логикой приложения и использует интерфейс View для того, чтобы передать данные для отображения пользователю.

В варианте, когда Data Binding является лишь инструментом для паттерна MVP, взаимодействие между View и Presenter все равно осуществляется через интерфейс, так как Presenter должен передать данные во ViewModel. Но тогда почему-то бы не попробовать объединить ViewModel и Presenter? То есть использовать в качестве делегата для бизнес-логики не Presenter, а ViewModel. Тогда ViewModel сможет заменить Presenter, выполнять всю его работу, а данные отдавать напрямую в xml с помощью Data Binding. Тогда схема взаимодействия будет следующей:

В таком случае мы вообще избавляемся от необходимости использовать View, так как в роли View теперь выступает xml-разметка. При этом ViewModel все так же является делегатом и управляет бизнес-логикой, не работая с View непосредственно. Если использовать терминологию Data Binding, то ViewModel – это тот объект, который передается в xml-разметку. Все действия пользователя также делегируются этому объекту.

В остальном принципы, которые мы рассматривали ранее (разбиение приложения на модули, разбиение на слой логики и слой UI, тестирование) остаются неизменными.

Конечно, при таком подходе есть немало спорных моментов. Во-первых, действительно ли у MVP есть проблемы, которые требуют такого непростого решения? Как уже было сказано, это актуально скорее для экранов, которые содержат очень много UI-элементов, которые также часто меняются во время работы приложения. Когда в паттерне MVP во View определено 2-3 метода, использовать Data Binding и MVVM не имеет смысла.

Во-вторых, как бы мы не хотели использовать только работу с логикой во ViewModel, так не получится. Поскольку при использовании Data Binding мы не должны вообще использовать View классы в Java-коде, то и их настройку придется выполнять в xml-файлах, а значит, через ViewModel. Какая это может быть настройка? Например, задание LayoutManager-а RecyclerView или же создание адаптера для данных. Использование их во ViewModel означает, что мы используем UI-классы в классах, которые должны отвечать только за логику приложения. Но на самом деле, в этом нет ничего страшного. Для того, чтобы связать UI-классы с xml через ViewModel, мы будем использовать методы, аннотированные как Bindable (опять же для примера это методы, которые возвращают LayoutManager, адаптер и другие UI-объекты). И самое главное – все такие методы должны быть методами без состояния, то есть мы не должны сохранять UI-объекты в качестве полей ViewModel.

При соблюдении таких условий мы будем использовать в качестве полей ViewModel только обычные Java-объекты, и у нас не будет проблем с тестированием методов, влияющих на логику. Но методы, возвращающие какие-то UI-объекты, разумеется, не будут протестированы с помощью тестов на JUnit. Есть и другой вариант решения этой проблемы – использование двух ViewModel, одна из которых будет отвечать за UI-классы, а другая – за логику. Какой из этих подходов лучше и удобнее, сказать сложно.

И в-третьих, не совсем понятно, как можно тестировать ViewModel. В случае MVP мы проверяли, что Presenter вызывает правильные методы View. При использовании MVVM такой возможности у нас больше нет, так как отсутствует сам интерфейс View. Понятно, что такие проблемы актуальны только для тестирования на JUnit, в случае UI-тестирования ничего не меняется, так как оно выполняется с точки зрения пользователя. Для решения проблем тестирования на JUnit можно объявлять package-private методы во ViewModel и тестировать их вызовы. Есть и еще один вариант – изменение View происходит за счет вызова метода notifyPropertyChanged, поэтому можно проверять то, что этот метод был вызван с нужным аргументом и в нужный момент. Далее мы разберем оба этих способа.

Теперь перейдем к практическому изложению и рассмотрим небольшой пример использования MVVM и Data Binding – приложение для просмотра фотографий дня от NASA. Для простоты приложение будет состоять из одного экрана, на котором будет один запрос и несколько UI-элементов. Реализуем предложенную схему для этого экрана.

Создаем класс DayPictureViewModel, который будет играть роль ViewModel на экране. Поскольку этот класс является ViewModel, он должен отдавать все данные View и быть связанным с ней. Поэтому этот класс будет наследоваться от BaseObservable:

Источник

Строительный портал