И вам даже не понадобятся API из Android N!
Если вы просматривали недавно видеоролик, озаглавленный «Что нового в Android N», то вы, возможно, успели уже задуматься о поддержке мультиоконности.
Мультиоконность будет работать в режиме разбиения экрана, что означает, что два приложения будут находиться на экране одновременно, бок о бок. Чтобы понять, как же работает эта магия, я тут же прошерстил документацию в поисках новых API.
Оказывается, их не так уж и много. Несколько XML атрибутов, указывающих, собираетесь ли вы вообще поддерживать мультиоконность, да несколько методов
Activity
, позволяющих понять, работает ли ваше приложение сейчас в режиме мультиоконности. И где тут магия? Магия вообще-то была здесь всегда.И под магией подразумевается система ресурсов Android'а. Одна из самых мощных её частей это способность предоставлять альтернативные ресурсы — менять размеры, макеты, drawables, меню, и прочее в зависимости от различных квалификаторов.
Поддержка мультиоконности базируется на системе ресурсов в том плане, что при переходе приложения в мультиоконный режим меняются параметры конфигурации, имеющие отношение к размеру области, в которой отрисовывается приложение. Очевидным примером таких параметров будет размер экрана, чуть менее очевидными, но тоже изменяющимися будут наименьшая ширина (то есть минимум между высотой и шириной экрана) и ориентация.
И это приводит нас к первому совету.
Первый совет: Используйте правильный контекст
Загрузка правильных ресурсов требует правильного контекста. Если для получения ресурсов, создания иерарахии виджетов по файлу макета, и прочего вы иcпользуете контекст Activity, то у вас всё хорошо.
А вот если вы используете контекст приложения для чего усгодно, связанного с графическим интерфейсом, то вы обнаружите, что загруженные ресурсы пребывают в блаженном неведении относительно мультиоконности. Помимо проблем, связанных с тем, что вы не используете тему вашей
Activity
, вы ещё и будете порой загружать абсолютно неправильные ресурсы! Поэтому лучше всего для подгрузки ресурсов, требуемых UI, использовать контекст Activity
.Второй совет: Правильно обрабатывайте изменения конфигурации
Используя правильный контекст, вы точно сможете загрузить корректные ресурсы, зная текущий размер экрана (вне зависимости от того, находится ли ваше приложение в полноэкранном режиме, или же делит экран с кем-то другим). Процесс перезагрузки корректных ресурсов базируется на том, как вы обратываете изменения конфигурации.
По умолчанию изменение конфигурации происходит так: вся ваша
Activity
уничтожается, а затем создается заново, восстанавливая все те данные, что вы сохранили в onSaveInstanceState(), и перезагружая все ресурсы/макеты. При этом вы точно знаете, что все что нужно будет консистентным с новой конфигурацией, и что каждый тип конфигурации обработан.Само собой разумеется, что каждая смена конфигурации должна быть при этом быстрой. Убедитесь, что вы не делаете много работы в onResume(), и подумайте об использовании загрузчиков для того, чтобы гарантировать сохранение и восстановление ваших данных при смене конфигурации.
Но вы так же можете обрабатывать смену конфигурации самостоятельно — в этом случае ваши активити (и фрагменты) уничтожаться не будут. У них будет вызван метод onConfigurationChanged(), в котором вам и нужно будет вручную обновить свои виджеты, перезагрузить ресурсы и так далее.
Чтобы перехватывать изменения конфигурации, связанные с мультиоконностью, вам понадобится добавить в манифест атрибут android:configChanges с как минимум следующими значениями:
<activity
android:name=".MyActivity"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation" />
Убедитесь, что вы обрабатываете каждый ресурс, который может измениться (собственно, вы и должны этим заниматься, если вы решили самостоятельно обрабатывать смену конфигурации).
Это относится и к перезагрузке тех ресурсов, которые раньше вы считали константами. Например, у вас есть ресурс, обозначающий размер (dimension), определённый в values и в values-sw600dp. До появления мультиоконности вы не стали бы переключаться между этими двумя значениями во время работы приложения, так как наименьшая ширина никогда не менялась (она всегда была бы равной меньшему из двух измерений экрана вашего устройства). Теперь же вы можете и должны будете переключаться между этими ресурсами после изменения размеров области, в которой отрисовывается ваше приложение.
Третий совет: работайте со всеми ориентациями экрана
Помните как в самом начале мы обсуждали изменение ориентации, происходящее при изменении размера окна? Да-да: даже если устройство находится в ландшафтном режиме, ваше приложение может быть в портретной ориентации.
Всё очень просто: «портретная» ориентация означает, что высота больше ширины, а «ландшафтная» — наоборот. Таким образом, ваше приложение действительно может перейти из одной ориентации в другую при переходе в мультиоконный режим.
А ещё это означает, что переходы между ориентациями должны быть как можно более плавными. Процитируем раздел спецификации материального дизайна, посвященный режиму разбиения экрана:
Изменение ориентации устройства не должно приводить к неожиданным изменениям интерфейса. Например, приложение, воспроизводящее видео в одной из двух половиной экрана (в портретном режиме), не должно переходить к воспроизведению видео в полноэкранном режиме если устройство переходит в ландшафтный режим.
Замечание: если вам всё-таки нужно реализовать такое поведение в случае, когда ваше приложение находится в полноэкранном режиме, вы можете использовать метод inMultiWindowMode(), чтобы узнать, в каком режиме вы находитесь в данный момент.
Мультиоконность также влияет и на возможность жёстко задать ориентацию экрана атрибутом android:screenOrientation. Использование этого атрибута в приложениях, не поддерживающих Android N (у которых в качестве
targetSdkVersion
прописан Marshmallow или ниже) означает, что мультиоконность вами поддерживаться не будет — пользователь будет всегда выкидываться из мультиоконного режима. Если же вы решите поддержать Android N, то поведение изменится: поведение атрибута android:screenOrientation
будет игнорироваться в случае, если вы переходите в мультиоконный режим.Не забудьте также о том, что попытка зафиксировать ориентацию через setRequestedOrientation() не возымеет в мультиоконном режиме никакого эффекта, поддерживаете вы N или нет.
Четвёртый совет: создавайте отзывчивый (responsive) интерфейс для всех размеров экрана
При проектировании дизайна с учётом возможности мультиоконного режима учитывать придётся не только размер экрана. Мультиоконный режим — это первый случай, когда интерфейс, созданный вами для планшета (он у вас есть, да? Не забывайте, 12.5% от 1.4 миллиарда устройств — это много устройств...) будет ужиматься до миниатюрных размеров.
Если вы спроектировали отзывчивый интерфейс, который не привязан к конкретному размеру экрана, и макеты которого для телефонов и планшетов относительно похожи друг на друга, то вы уже хорошо подготовились к мультиоконности. Вообще, есть хорошая рекомендация по тому, как можно подготовиться к грядущим изменениям: представьте, что по высоте/ширине вам доступно только 220dp, и рассматривайте всё остальное пространство как дополнительное, которого может и не быть. И переделывайте интерфейс с учётом этого ограничения.
Но, если ваши интерфейсы для планшета и телефона сильно расходятся — не взрывайте пользователям мозг переключением между ними двумя — оставьте интерфейс для планшета, и попробуйте уменьшить его так, что он станет применим и в случае телефона. Есть несколько шаблонов для построения отзывчивого интерфейса, которые вы можете применить, чтобы сделать изменение размеров незаметным для пользователей, и для этого вам не нужны API из Android N.
Пятый совет: Activity
, запущенные другими приложениями, всегда должны поддерживать работу в мультиоконном режиме
В мультиоконном мире ваша задача представлена одним окном. Поэтому если вы хотите запустить смежную
Activity
, вам нужно запускать новую задачу — новая задача, новое окно.И обратное тоже верно, цитируем ту же самую страницу:
Если вы запускаетеActivity
в существующем стеке задачи, новаяActivity
замещаетActivity
, находившуюся на экране, наследуя при этом все её мультиоконные свойства.
Это означает, что если у вас есть
Activity
, которая может быть запущена другими приложениями, то она унаследует при старте мультиоконные свойства той Activity
, которая её запустила. В том числе и минимальный размер. В случае startActivityForResult() ваша Activity
станет частью того же самого стека задачи, и даже если она будет запущена через явный Intent
, нет никакой гарантии, что она будет запущена с флагом FLAG_ACTIVITY_NEW_TASK.Таким образом, каждая из ваших
Activity
, которые могут быть запущены из других приложений (а также активити, запускаемые из ваших активити) должны поддерживать мультиоконность, вплоть до самых маленьких размеров экрана. Тестируйте тщательно!Повторю ещё раз: тестируйте!
Лучший способ приготовиться к мультиоконности — это протестировать ваше приложение. Даже если вы не станете сразу же переписывать свой код, или же устанавливать Android N SDK, а просто поставите своё приложение на эмулятор, или же устройство с Android N — вы сделаете большой шаг вперёд, к тому, чтобы сделать свои приложения лучше.
Подпишитесь на Android Development Patterns Collection, если хотите узнать больше!