Привет! В этой статье я хочу рассказать, какие проблемы могут возникнуть с появлением мультиоконного режима на планшетных версиях приложений. Команда Android-разработки компании Лайв Тайпинг столкнулась с ними, когда адаптировала приложение ИЛЬ ДЕ БОТЭ под планшет. Будьте готовы к тому, что эти же проблемы будут и у вас.
Как вы все знаете, в конце августа 2016 года вышла Android 7.0, и одной из её основных особенностей является поддержка мультиоконности. Это прекрасная функция, которая поднимает удобство Android на новый уровень. Все пользователи будут на седьмом небе от счастья, но то, что является счастьем для пользователя, может обернуться болью для разработчика. К сожалению, с мультиоконностью на планшетах именно так и произошло. И именно на планшетах — на телефонах-то с ней как раз всё отлично, заранее говорю.
Если вы делаете планшетные версии приложений или вы просто неравнодушны к мультиоконному режиму, то добро пожаловать в статью!
Корень всех зол
Допустим, вы разработчик, который неожиданно решил сделать в телефонном приложении поддержку мультиоконности. Вы всё сделали правильно, верстали под ширину 320dp и вообще вы молодец, а режим, к вашему счастью, работает просто отлично. Даже если вы не совсем молодец, потому что не сделали поддержку маленьких экранов и верстали под 360dp или 480dp, то всё ещё остаётся в порядке. Самое ужасное, что может с вами произойти — немного поплывёт верстка или кое-где текст в кнопке не влезет в одну строку. Но давайте будем откровенны, что это всё мелочи. Пара часов, максимум день и вуаля, PROFIT! Вы снова молодец и можете добавлять новые фишки мультиоконности в ваше приложение.
Но что, если у вас есть не только телефонная версия приложения, но и планшетная?
У вас возникнут проблемы. Возможно, довольно большие проблемы. Даже, возможно, проблемы окажутся настолько огромными, что вам придётся радикально менять архитектуру приложения.
Работа мультиоконности на планшете
Ориентация экрана
Для простоты представим, что у нас есть вёрстка для телефонов и вёрстка для 10-дюймовых планшетов. Будем рассматривать всё на примере layout, то есть наши ресурсы будут лежать в следующих папках:
- layout-port
- layout-land
- layout-sw720dp-port
- layout-sw720dp-land
Начнём с вертикальной ориентации. Наше приложение может занимать либо весь экран, и будет использована вёрстка из layout-sw720dp-port:
Либо половину экрана, и ресурсы будут браться из layout-land:
Либо треть или две трети, и ресурсы будут браться из layout-land и layout-sw720dp-land соответственно:
Вы ожидаете, что в landscape-режиме land и port просто поменяются местами, но нет. Всё немного не так.
Если занят весь экран, то используется layout-sw720dp-land:
Если половина экрана, то layout-port:
Если треть или две трети, то layout-port и layout-sw720dp-land соответственно:
Демонстрационное приложение
С расположением мы разобрались. Многие, наверное, уже начинают осознавать возможные проблемы, но давайте по порядку. Сначала вообразим, что вы сделали абстрактное приложение, которое идеально работает на Android 6, но у которого будут проблемы на Android 7. Заранее прошу не придираться к тому, как оно продумано, ибо продумано оно так специально для демонстрации возможных проблем.
Итак, пусть приложение будет новостным и имеет три основных сущности: категория, подкатегория и новость. Телефонная версия может находится только в портретной ориентации и имеет три экрана, каждый из которых является отдельным Activity:
- Главный экран. На нём есть основные категории новостей, которые представлены в верхних табах. Внутри каждого таба есть баннер с самой главной новостью дня. Ниже представлен список подкатегорий внутри этой категории новостей. При нажатии на эту подкатегорию мы попадаем на экран с её содержимым.
- Экран списка новостей. При клике на новость мы попадаем на отдельный экран этой новости.
- Экран отдельной новости. Здесь мы показываем новость в полном её объеме.
Для планшета всё устроено немного по-другому: есть только два экрана, которые также являются Activity:
- Главный экран. Слева располагается список категорий с вложенностью в виде подкатегорий. Справа отображается список новостей. Также в планшете появляется новая фишка: можно кликнуть по категории и увидеть все новости по ней.
- Экран отдельной новости. Тут, в принципе, все по-старому, просто немного изменилась вёрстка.
Приложение имеет архитектуру MVP и у каждого экрана есть Presenter и View. В качестве View используется кастомный ViewGroup вроде такого. Для главного экрана и экрана списка новостей используются совершенно разные View и Presenter. Экраны новости на планшете и на телефоне абсолютно идентичны друг другу по логике, но из-за больших различий визуальной части нам нужно нечто большее, чем просто разные xml. Поэтому реализация View устроена так: абстрактный класс NewsView, в котором находится все общие и наследующиеся от него классы PhoneNewsView и TabletNewsView.
Теперь давайте ещё немного напряжём воображение и представим, что мы запустили наше приложение на 10-дюймовом планшете под управлением Android 7 и включили режим мультиоконности.
Представили? Давайте уже посмотрим, что у нас получилось.
Выглядит не очень, а всё потому, что раньше в телефонной версии вы не поддерживали альбомную ориентацию. Поэтому он берёт ресурсы из общих ресурсов. Хотя в принципе всё круто, немного потянуло баннер, но это мелочи. На радостях вы тыкаете на первую попавшуюся подкатегорию и попадаете на экран списка новостей, с которым тоже всё не так уж плохо.
Радости через край, и тут вас осеняет: «А что будет, если я вновь разверну приложение и перейду на вёрстку планшета? Ведь этого экрана не должно быть в логике работы на планшете». Вы молодцы, это очень правильная мысль. Произойдёт следующее: у вас будет отображаться экран, которого здесь не должно быть. Более того, вам покажут его телефонную версию, что испортит внешний вид.
Проблемы
Проблема номер 1: если экран планшетной версии объединяет в себе несколько экранов из телефонной, то начнется хаос.
Какие тут есть решения? Ну, можно, к примеру, переделать всё на фрагменты, в телефонной версии класть их друг на друга, а в планшетной — рядом. Profit! Но не стоит забывать, что это легко сделать только на нашем маленьком абстрактном приложении, а в реальном приложении придётся приложить уйму усилий. Также в вашем приложении может быть замороченная навигация, которую с помощью фрагментов нельзя реализовать из-за того, что они не могут сохранять состояния своих дочерних фрагментов.
Допустим вы переделали наше абстрактное приложение на фрагменты, но тут же вспоминаете о необычной особенности планшета — возможности показать все новости по всем подкатегориям какой-либо категории.
Проблема номер 2: если в планшетной версии есть функциональность, которой нет в телефонной версии, и наоборот, то всё будет плохо.
Что же делать? Придётся добавлять эту функциональность в телефонную версию, либо убирать её из планшетной. Как вы понимаете, в реальной жизни таких функциональностей может быть очень много, и, скорее всего, это потребует от вас множество, множество согласований, что в итоге выльется в клочья вырванных волос и полные стаканы горьких слёз. Хорошо, что в нашем маленьком абстрактном приложении его немного, а то пришлось бы попотеть.
Похоже, со списком категорий всё хорошо. Вы всё отладили и исправили, вы определённо молодец. Вы заходите на экран отдельной новости и пытаетесь перейти в мультиоконный режим, но — упс! — приложение упало. В чём же дело? А всё из-за того, что ваш PhoneNewsView и TabletNewsView имеют один и тот же id. Выходит, что onSaveInstanceState делается для одного класса, а onRestoreInstanceState уже для другого.
Проблема номер 3: если в xml планшетной версии и телефонной версии разные классы имеют один и тот же id, то вы определённо устанете это исправлять.
Как это обойти? Можно просто поставить им разные id, но тут сразу вырисовывается проблема с сохранением состояния при изменении режима мультиоконности, а именно то, что его не будет. Выход есть: сделать всё так, чтобы в коде не было никаких отличий между планшетной и телефонной версией, разве что в XML. Если уж есть острая необходимость добавить в планшете какой-то новый элемент, то лучше просто проверить его на null и уже потом делать что-либо с этим элементом.
С этой проблемой вы блестяще справились, поздравляю. Тут вы неожиданно вспоминаете, что на этом же экране, в телефонной версии, сразу же под картинкой новости у вас написано «Если у вас есть планшет, то попробуйте нашу планшетную версию». Ужасно! Ну, на самом деле, по сравнению с предыдущими проблемами это просто цветочки, но все же неприятненько.
Вообще, это не проблема номер 4. Просто, я хотел рассказать, как брать планшетные ресурсы, если приложение, по идее, должно брать телефонные.
Так что же делать? Если в режиме мультиоконности брать ресурсы от Application, то он будет возвращать их для планшета; если от Activity, View, Fragment и всего прочего, что не Application, то будет возвращать для их размера. То есть, если делить экран пополам, то Application будет возвращать планшетные ресурсы, а всё остальное — телефонные. Думаю это связано с тем, что, по идее, одно приложение можно разбить на несколько окон. Может быть, многие задумали хитрый план — делать setContentView в Activity не по id, а по «вьюхе», которую вы заранее заинфлейтили с помощью Application. НО! Делать так не стоит. Иначе ваше приложение будет выглядеть вот так:
С помощью Application вы разрешили дилемму с ненужной строчкой. Довольные собой и проделанной работой, вы откинулись на стуле и подумали о том, зачем Google ввели столько дополнительных «кейсов» и насколько теперь усложниться жизнь рядового Android-разработчика.
Важная деталь об Android SDK
На самом деле c мультиоконностью у Google всё в порядке. Почему же возникают проблемы с планшетом? Потому что в Android SDK понятия «планшет» не существует. Вообще. Есть маленькие, средние, большие экраны… Но понятия «планшет» нет. Не должно быть никакой отдельной логики, не должно быть никакой дополнительной функциональности.
Проблема не в том, что Google сделали всё плохо, просто как такового «особого приложения» под планшет не должно быть в принципе. Откуда вообще у всех мания делать приложения отдельно под планшет? Из iOS, но там действительно есть понятие планшета и для него есть возможность сделать все по-другому.
Android-приложения, на мой взгляд, в этом плане больше похожи на Web: у вас есть страница и определённый набор данных, и вам нужно сделать так, чтобы на всех экранах и во всех браузерах всё смотрелось хорошо. Ничего не напоминает? Много видов экранов и очень большая фрагментация устройств, многие из них со своими особенностями, вот это вот всё? По мне так похоже.
Особенность описанных мною проблем, в том, что их не должно быть, но я надеюсь, что помог вам разобраться с ними, если они возникли, или предупредил их возникновение. Взгляните на Google Play: вы можете открыть его на любом устройстве, и он будет выглядеть, может, и не отлично, но хотя бы хорошо. Это потому, что Google Play изначально создавали с возможностью отображаться на экране любого размера. При том, что набор данных всегда одинаков.
Это очень хорошая практика. Вы можете изменить количество столбцов, отступы, размер элементов, скрывать или показывать какой-то элемент и ещё кучу всего, но общая логика и набор данных должны оставаться неизменным. Именно это, на мой взгляд, залог крутого приложения.
Тема философская и, само собой, спорная, так что добро пожаловать в комментарии. Всем пока!
ссылка на оригинал статьи https://habrahabr.ru/post/315938/
Добавить комментарий