Привет, Хабр! В данной статье я хочу поделиться опытом создания своего механизма для автоматизации показа различных View типа: ContentView, LoadingView, NoInternetView, EmptyContentView, ErrorView. Это был долгий путь. Путь проб и ошибок, перебор методов и вариантов, бессонные ночи и бесценный опыт, которым я хочу поделиться, и услышать критику, которую я обязательно приму во внимание. Скажу сразу, что буду рассматривать работу на RxJava, так как для coroutines я не делал подобного механизма — не дошли руки. А для других подобных инструментов (Loaders, AsyncTask и так далее) нет смысла использовать мой механизм, так как чаще всего применяется именно RxJava или coroutines. Один мой коллега сказал, что невозможно шаблонизировать поведение View, но я всё-таки попытался это сделать. И сделал. Стандартный экран приложения, данные которого берутся с сервера, минимально должен обрабатывать 5 состояний: Соответственно, для каждого такого состояния должна быть своя View. Я называю такие View — ActionViews, потому что они реагируют на какие-то действия. По факту, если вы можете точно определить, в какой момент ваша View должна показываться, а когда скрываться, то она тоже может быть ActionView. Существует один (а может, и не один) стандартный способ для того, чтобы с такими View работать. В методы, которые содержат работу с RxJava, нужно добавить входные аргументы для всех типов ActionViews и добавить в эти вызовы некоторую логику определения показа и скрытия ActionViews, как это сделано тут: Но такой способ содержит огромное количество бойлерплейта, а по умолчанию мы его не любим. И поэтому я начал работу над сокращением рутинного кода. Первым этапом модернизации стандартного способа для работы с ActionViews стало сокращение бойлерплейта путем вынесения логики в утильные классы. Код ниже придумал не я. Я — плагиатор и подсмотрел это у одного толкового коллеги. Спасибо, Arutar! Теперь наш код выглядит так: Код, который мы видим выше, хоть и лишён boilerplate-кода, но всё равно не вызывает такого фееричного восторга. Стало уже намного лучше, но осталась проблема передачи ссылок на ActionViews в каждый метод, где есть работа с Rx. А таких методов в проекте может быть бесконечное количество. Ещё и эти compose постоянно писать. Бууэээ. Кому это надо? Только трудолюбивым, упорным и не ленивым людям. Я не такой. Я поклонник лени и фанат написания красивого и удобного кода, поэтому было принято важное решение — любыми способами упростить код. Спустя многочисленные переписывания механизма я пришёл вот к такому варианту работы: Я переписывал свой механизм около 10-15 раз, и каждый раз он очень сильно отличался от предыдущего варианта. Я не стану вам показывать все версии, давайте сосредоточимся на двух финальных. Первый вы увидели только что. Согласитесь, выглядит симпатично? Я бы даже сказал, очень симпатично. Я стремился к таким решениям. И абсолютно все наши ActionViews будут работать корректно в нужное нам время. Достичь этого я смог с помощью написания огромного количества не самого красивого кода. В классах, которые позволяют использовать такой механизм, содержится очень много сложных логик, и мне это не нравилось. Одним словом — конфетка, которая под капотом является монстром. Такой код в будущем будет всё тяжелее и тяжелее поддерживать, да и сам он содержал достаточно серьёзные минусы и проблемы, которые были критичными: Разумеется, у меня были решения этих проблем, но все эти решения порождали другие проблемы. Я постарался максимально избавиться от самых критичных, и в итоге остались только необходимые требования к использованию библиотеки. Спустя какое-то время я сидел дома, баловался маялся дурью и вдруг я внезапно осознал — надо попробовать Kotlin и по-максимуму заюзать экстеншены, дефолтные значения, лямбды и делегаты. Сперва он выглядел очень не очень. Но теперь он лишён практически всех недостатков, которые, в принципе, могут быть.  Вот так выглядит наш предыдущий код, но уже в финальном варианте: Благодаря Extensions я смог сделать всю работу в одном потоке, не нарушая основной концепции реактивного программирования. Также я оставил возможность кастомизировать поведение. Если вы захотите изменить действие на старте или окончании показа загрузки, вы просто можете передать функцию в метод, и всё будет работать: Также изменение поведения доступно и для других ActionViews. Если вы захотите использовать стандартное поведение, но при этом у вас не дефолтные ActionView, то можно просто указать, какая View должна заменить нашу ActionView: Я показал вам самые сливки этого механизма, но и у него есть своя цена.
Во-первых, вам нужно будет создавать CustomViews для того, чтобы это работало: Может быть, это даже не потребуется делать. На данный момент я собираю отзывы и принимаю предложения по улучшению данного механизма. Основная причина того, что нам необходимо использовать CustomViews — наследование от интерфейса, который указывает, какому типу ActionView она принадлежит. Это нужно для безопасности, так как вы можете случайно ошибиться при указании типа View в методе withActionsViews.  Так выглядит сам метод withActionsViews: Выглядит страшновато, но зато удобно и быстро! Как видите, во входных параметрах он принимает loadingView: LoadingView?.. Это страхует нас от ошибки с типом ActionView. Соответственно, чтобы механизм заработал, нужно сделать несколько простых шагов: Или унаследоваться от класса, в котором уже переопределены наши ActionViews. В этом случае придётся использовать строго заданные id в ваших layout: Если будете использовать Kotlin Extensions, то не забывайте про то, что можно переименовать импорт в удобное для вас название: Когда я начинал работу над этим механизмом, я не думал о том, что из этого получится библиотека. Но так вышло, что я захотел поделиться своим творением, и теперь меня ждёт самое сладкое — публикация библиотеки, сбор issues, получение фидбека, добавление/улучшение функциональности и исправление багов.  Успел оформить всё в виде библиотек: Библиотека и сам механизм не претендуют на звание must have в вашем проекте. Я лишь хотел поделиться своей идеей, выслушать критику, комментарии и улучшить свой механизм, чтобы он стал более удобным, используемым и практичным. Возможно, вы сможете сделать подобный механизм лучше, чем я. Буду только рад. Я искренне надеюсь, что моя статья вдохновила вас на создание чего-то своего, возможно, даже подобного и более лаконичного. Если у вас есть пожелания и рекомендации по улучшению функциональности и работы самого механизма, буду рад выслушать их. Welcome в комментарии и, на всякий случай, мой Telegram: @tanchuev P.S. Я получил огромное удовольствие от того, что я создал что-то полезное своими руками. Возможно, ActionViews не будет пользоваться спросом, но опыт и кайф от этого никуда не денутся. P.P.S. Чтобы ActionViews превратился в полноценную используемую библиотеку, нужно собрать отзывы и, возможно, доработать функциональность или в корне изменить сам подход, если всё будет совсем плохо. P.P.P.S. Если вы заинтересовались моей наработкой, то мы можем обсудить её лично 28 сентября в Москве на Международной конференции мобильных разработчиков MBLT DEV 2018. Кстати, early bird билеты уже заканчиваются! Android, Flutter, Product Management