Android-роботы версий 5 и 6 продолжают гордо шагать по смартфонам и планшетам радостных пользователей, сверкая красотами Material Design. При этом, надо отдать должное Google, старые девайсы никто не забывал, они тоже примерили шкурки материального дизайна, пусть и не в полном объеме. О том, как все это работает на устройствах с Android версий со второй по четвертую, мы сегодня и поговорим. Если же ты разрабатываешь приложения исключительно для Android 6, то информация, приведенная ниже, тоже будет тебе полезна.

 

Модный приговор

Material Design — дизайн программного обеспечения и приложений операционной системы Android от компании Google, впервые представленный на конференции Google I/O в 2014 году. Идея дизайна заключается в создании приложений, которые открываются и сворачиваются как физические (то есть материальные) карточки. Как и все физические объекты, они должны отбрасывать тень и иметь некоторую инерционность. По идее дизайнеров Google, у приложений не должно быть острых углов, карточки должны переключаться между собой плавно и практически незаметно (рис. 1).

Рис. 1. Основные элементы Material Design
Рис. 1. Основные элементы Material Design

Вообще, эффект тени позволяет визуально расположить все элементы на разной высоте, то есть получается некоторая совокупность слоев (рис. 2).

Не менее значима концепция плавающей кнопки (Floating Action Button), отражающей главное действие во фрагменте или активности. Например, в Gmail данный виджет позволяет создать новое письмо. Плавающей эта кнопка названа потому, что ее положение не фиксировано (да, не только правый нижний угол) и может меняться. Причем это изменение должно быть плавно и осмысленно анимировано, то есть, например, при скроллинге компонента ListView или переключении фрагмента FAB кнопка может «уезжать» за экран или «растворяться».

Рис. 2. Слои? Слои!
Рис. 2. Слои? Слои!

Формат журнальной статьи не позволяет описать все нюансы Material Design (в пересчете на бумажный формат ты нафигачил целых полторы статьи :). — Прим. ред.), тем более что не все из них можно реализовать библиотеками совместимости в преLollipop версиях Андроида. Наиболее тяжело в этом плане дела обстоят с анимацией. Например, у нас не получится увидеть Ripple-эффект (расходящиеся круги при нажатии на кнопку), так как данная анимация реализуется аппаратно и недоступна для старых устройств. Разумеется, это решается сторонними библиотеками, но об этом мы поговорим в следующий раз.

Ознакомиться с гайдами по Material Design можно (даже нужно!) на официальном сайте Google, а по адресу доступен перевод на русский язык.

 

Android AppCompat vs. Design Support Library

После выхода в свет Android 5 в SDK от Google произошло существенное обновление библиотеки AppCompat (в девичестве ActionBarCompat), получившее седьмую версию aka v7. Самой вкусной в этой версии стала возможность использования элементов Material Design в ранних версиях Андроида — начиная с 2.1 (API Level 7). Один из таких элементов — виджет Toolbar, пришедший на замену скучному ActionBar — панели, расположенной в верхней части активности (той самой, где висит «гамбургер», открывающий боковое меню). Кроме того, новое Material-оформление коснулось и других стандартных элементов: EditText, Spinner, CheckBox, RadioButton, Switch, CheckedTextView.

Помимо этого, были добавлены новые библиотеки — RecyclerView (крутейшая замена ListView), CardView (карточка) и Palette (динамическая палитра). К слову, в декабрьском Хакере эти элементы уже были рассмотрены — срочно ищи и изучай, повторяться не будем.

Казалось бы, мы у цели, вот оно — счастье, но, взглянув, например, на почтовый клиент Gmail в том же Android 4, потихоньку начинаешь понимать, что с одной лишь AppCompat такое приложение не накодишь. Ведь по какой-то космической причине в библиотеке AppCompat нет даже плавающей кнопки — едва ли не главного элемента Material Design.

К счастью, в Google рассуждали точно так же, но, правда, почему-то не стали дополнять AppCompat, а представили совершенно новую библиотеку совместимости — Design Support Library. Здесь уже все по-взрослому: удобное боковое меню (Navigation View), плавающая кнопка (Floating Action Button), всплывающее сообщение (Snackbar), анимационный Toolbar и многое другое. Далее мы зарядим все библиотеки в обойму знаний и познакомимся поближе с этими прекрасными нововведениями.

Хочу только заметить, что библиотеки и виджеты можно использовать как по отдельности, так и все вместе.
Итак, обновляем SDK, запускаем Android Studio и начинаем кодить…

 

Импорт библиотек

Так как мы используем Android Studio, импорт модулей необходимо выполнять в секции dependencies файла build.gradle проекта:

dependencies {
  compile fileTree(dir: 'libs', include: ['*.jar'])
  compile 'com.android.support:appcompat-v7:22.2.1'
  compile 'com.android.support:design:22.2.1'
  compile 'com.android.support:recyclerview-v7:22.2.1'
  compile 'com.android.support:palette-v7:22.2.1'
  compile 'com.android.support:cardview-v7:22.2.1'
}

На момент выхода журнала уже будет доступна версия библиотек под номером 23.1.1, но для нас это не принципиально. Кстати, IDE любезно подскажет номер последней версии, а также автоматически скачает ее из репозитория, если она отсутствует.

 

CoordinatorLayout, Toolbar и все-все-все

Начнем с весьма эффектного компонента — CoordinatorLayout, позволяющего связывать (координировать) виджеты, помещенные в него (по сути, CoordinatorLayout является продвинутым FrameLayout). Чтобы было понятно, на рис. 3 приведено исходное состояние фрагмента приложения. Стоит только начать перелистывать список, как размер заголовка плавно вернется к традиционному размеру (уменьшится), освобождая место для полезной информации (рис. 4). И это все, что называется, прямо из коробки, без всяких костылей.

Рис. 3. Было
Рис. 3. Было
Рис. 4. Стало
Рис. 4. Стало

Разметка фрагмента приведена ниже (несмотря на размер, код достаточно тривиален):

<android.support.design.widget.CoordinatorLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:app="http://schemas.android.com/apk/res-auto"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:fitsSystemWindows="true" >

  <android.support.design.widget.AppBarLayout
    android:layout_width="match_parent"
    android:layout_height="192dp"
    android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
    android:id="@+id/appbar"
    android:fitsSystemWindows="true" >

    <android.support.design.widget.CollapsingToolbarLayout
      android:id="@+id/collapsing_toolbar"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      app:contentScrim="?attr/colorPrimary"
      app:layout_scrollFlags="scroll|exitUntilCollapsed"
      android:fitsSystemWindows="true"
      app:expandedTitleMarginBottom="32dp"
      app:expandedTitleMarginEnd="64dp"
      app:expandedTitleMarginStart="48dp" >

      <ImageView
        android:id="@+id/toolbarImage"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="centerCrop"
        app:layout_collapseMode="parallax"
        android:fitsSystemWindows="true" />

      <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        app:layout_collapseMode="pin"
        app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />

    </android.support.design.widget.CollapsingToolbarLayout>
  </android.support.design.widget.AppBarLayout>

  <!-- Контейнер для остальных компонентов фрагмента или активности -->
  <FrameLayout
    android:id="@+id/frame_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"/>

</android.support.design.widget.CoordinatorLayout>

Видно, что у CoordinatorLayout два дочерних элемента: AppBarLayout и контейнер FrameLayout. Последний может содержать любые прокручиваемые элементы интерфейса: например, RecyclerView или ListView. В приведенном на рис. 3 приложении в этом контейнере находятся RecyclerView и кнопка (FAB). Теперь AppBarLayout и FrameLayout будут зависеть друг от друга при скроллинге, но только в том случае, если у последнего указать специальный флаг layout_behavior="@string/appbar_scrolling_view_behavior", который инициирует передачу прикосновений в AppBarLayout.

Идеологически AppBarLayout в чем-то напоминает вертикальный LinearLayout, элементы которого могут вести себя по-разному (в зависимости от флагов) при прокручивании содержимого. В приведенном примере используется виджет CollapsingToolbarLayout, являющийся удобной оберткой для компонента Toolbar. Собственно, CollapsingToolbarLayout специально спроектирован для использования внутри AppBarLayout. Размер самого AppBarLayout в развернутом виде определяется параметром layout_height, и в листинге он равен 192dp.

Флаг layout_scrollFlags определяет поведение компонента при прокручивании. Если не указать scroll, AppBarLayout останется на месте, а контент уплывет (забавный эффект). Второй флаг, exitUntilCollapsed, определяет, как именно будет прокручиваться Toolbar и остальной контент. К сожалению, описывать на словах отличие флагов друг от друга бесполезно, поэтому отсылаю тебя по адресу, где наглядно (с анимацией) расписаны все варианты. Как говорится, лучше один раз увидеть…

Параметр contentScrim="?attr/colorPrimary" задает цвет фона, в который переходит фоновое изображение при свертывании CollapsingToolbarLayout. Внимательный читатель заметит, что на рис. 4 Toolbar вовсе не окрашен в какой-либо цвет, а немного затененное изображение осталось на месте. Чтобы получить такой эффект, нужно указать константу @android:color/transparent.

Наконец, виджеты, непосредственно определяющие внешний вид фрагмента (активности), — Toolbar («гамбургер», заголовок, кнопки меню) и ImageView (фон) завернуты в CollapsingToolbarLayout. Флаг layout_collapseMode="parallax" у ImageView обеспечивает плавное затенение фонового изображения при сворачивании Toolbar’a. По опыту использования могу сказать, что «параллакс» работает не на всех устройствах.

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

public class MainActivity extends AppCompatActivity {
  private Toolbar mToolbar;
  private CollapsingToolbarLayout mCollapsingToolbar;
  private ImageView im;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    mToolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(mToolbar);
    mCollapsingToolbar = (CollapsingToolbarLayout) findViewById(R.id.collapsing_toolbar);
    mCollapsingToolbar.setTitle("Меню");
    ImageView im = (ImageView) findViewById(R.id.toolbarImage);
    Picasso.with(this).load(R.drawable.back).into(im);
  }
}

Вот, собственно, и весь код! SetSupportActionBar переключает ActionBar на Toolbar с сохранением почти всех свойств и методов первого. В частности, устанавливается заголовок с помощью setTitle. Полное описание виджета Toolbar доступно на официальном сайте Android Developers.

Далее находим ImageView фона и с помощью сторонней библиотеки Picasso устанавливаем соответствующее изображение. Обычно я не склонен к критике Google, но тут не могу удержаться. Неужели за столько времени существования Android нельзя было написать нормальную стандартную библиотеку для загрузки изображений? Чтобы метод setImageResource не вызывал Out of Memory для изображений в нормальном разрешении? Гайдлайны призывают делать яркие и стильные приложения со множеством графики, а такая вещь, как загрузка картинки, реализована спустя рукава. Нет, конечно, можно использовать BitmapFactory, придумывать кеширование, но это решение так и просится в отдельную библиотеку, что, собственно, сделано и в Picasso, и в UniversalImageLoader. Одним словом, непонятно…

 

Snackbar

Snackbar представляет собой небольшое информационное окно, расположенное в нижней части активности (рис. 5). Помимо информационного сообщения, имеется небольшая плоская кнопка (так называемый Action), позволяющая взаимодействовать с пользователем (например, отменить удаление сообщения). После тайм-аута Snackbar автоматически закрывается (как и традиционный компонент Toast).

Продолжение статьи доступно только подписчикам

Вариант 1. Оформи подписку на «Хакер», чтобы читать все статьи на сайте

Подписка позволит тебе в течение указанного срока читать ВСЕ платные материалы сайта, включая эту статью. Мы принимаем оплату банковскими картами, электронными деньгами и переводами со счетов мобильных операторов. Подробнее о подписке

Вариант 2. Купи одну статью

Заинтересовала статья, но нет возможности оплатить подписку? Тогда этот вариант для тебя! Обрати внимание: этот способ покупки доступен только для статей, опубликованных более двух месяцев назад.


Комментарии

Подпишитесь на ][, чтобы участвовать в обсуждении

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

Check Also

Мобильные приложения ряда крупных банков уязвимы перед MitM-атаками

Исследователи из университета Бирмингема предупредили, что приложения многих крупных банко…