Material design в android: продолжаем изучать модную тему

Содержание:

Сохраняйте масштабируемость

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

При создании анимации или градиентов всегда помните о масштабируемости с самого начала. Добавить её потом будет сложнее. Это просто. Вместо того, чтобы использовать фиксированные точки для градиентов, рисуйте их при помощи кода. Вместо того, чтобы думать о ширине экрана, используйте значения, которые предоставляет система Android. При возможности используйте значения в процентах, а если нужны абсолютные значения, помните про DiP.

Вот и все. Как вы видите, в нашем случае приложение не зафиксировано в одной ориентации экрана и мы не запрещаем установку на планшеты.

Usage

The library’s public API is nearly identical to the version from the support library, so you can use it as a drop-in replacement. We only added a attribute and corresponding getter and setter functions to the to make it possible to change the maximum number of lines, which is set to 3 by default.

As the Design Support Library, it should be compatible with API 14 (Android 4.0) and above.

XML layout example:

<android.support.design.widget.AppBarLayout
        androidlayout_height="192dp"
        androidlayout_width="match_parent">
    <net.opacapp.multilinecollapsingtoolbar.CollapsingToolbarLayout
            androidlayout_width="match_parent"
            androidlayout_height="match_parent"
            applayout_scrollFlags="scroll|exitUntilCollapsed"
            appmaxLines="3">
        <android.support.v7.widget.Toolbar
                androidlayout_height="?attr/actionBarSize"
                androidlayout_width="match_parent"
                applayout_collapseMode="pin"/>
    </net.opacapp.multilinecollapsingtoolbar.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>

You can find a simple demo application in the module.

Attaching a Behavior in XML

Of course, doing everything in code every time would be a bit of a mess. As with most custom LayoutParams, there’s a corresponding layout_ attribute to do the same thing. In this case, that is the layout_behavior attribute:

<FrameLayout  android:layout_height=”wrap_content”  android:layout_width=”match_parent”  app:layout_behavior=”.FancyBehavior” />

Here, unlike the code case, the FancyBehavior(Context context, AttributeSet attrs) constructor is always the one called. As a bonus though, you can declare any other custom attributes you want and extract them from the XML AttributeSet — important if you want developers to be able to customize your Behavior’s functionality via XML (which you do).

Styling the Toolbar

The Toolbar can be customized in many ways leveraging various style properties including , , . Each of these can be mapped to a style. Start with:

Now, we need to create the custom styles in with:

This results in:

Displaying an App Icon

In certain situations, we might want to display an app icon within the . This can be done by adding this code into the

Next, we need to remove the left inset margin that pushes the icon over too far to the left by adding to the :

With that the icon should properly display within the as expected.

Custom Title View

A is just a decorated and as a result, the title contained within can be completely customized by embedding a view within the Toolbar such as:

This means that you can style the like any other. You can access the inside your activity with:

Note that you must hide the default title using . This results in:

Translucent Status Bar

In certain cases, the status bar should be translucent such as:

To achieve this, first set these properties in your within the main theme:

The activity or root layout that will have a transparent status bar needs have the property set in the layout XML:

You should be all set. Refer to this stackoverflow post for more details.

Transparent Status Bar

If you want the status bar to be entirely transparent for KitKat and above, the easiest approach is to:

and then add this style to your within the main theme:

You should be all set. Refer to this stackoverflow post for more details.

Реализация

UI-фреймворк Android удивительно мощен и гибок. Если вы уделите время, чтобы поучиться тому, что вы можете сделать с его помощью, вы получите очень сильный инструмент в свой арсенал. Лично я считаю, что нативный UI Android является самым сильным доступным инструментом прототипирования. Почти все идеи вашего дизайнера можно воплотить за несколько часов (или хотя бы создать приблизительный набросок функции).

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

Для сохранения масштабируемости на экранах Android часто используются прокручивающиеся контейнеры. Поэтому Google представили специальные компоненты для того, чтобы добавлять интересное поведение в панель инстументов Android: AppBarLayout и CollapsingToolbarLayout.

С помощью этих компонентов и некоторых кастомных наработок мы можем сделать волшебный дизайн Toolbar-панели.

Creating a Behavior

Creating a behavior is simple enough: extend Behavior.

public class FancyBehavior<V extends View>    extends CoordinatorLayout.Behavior<V> {  /**   * Default constructor for instantiating a FancyBehavior in code.   */  public FancyBehavior() {  }  /**   * Default constructor for inflating a FancyBehavior from layout.   *   * @param context The {@link Context}.   * @param attrs The {@link AttributeSet}.   */  public FancyBehavior(Context context, AttributeSet attrs) {    super(context, attrs);    // Extract any custom attributes out    // preferably prefixed with behavior_ to denote they    // belong to a behavior  }}

Note the generic type attached to this class. Here, what we’re saying is you can attach a FancyBehavior to any View class. However, if you wanted to only allow the Behavior to be attached to a specific kind of View, you could instead write it as:

public class FancyFrameLayoutBehavior    extends CoordinatorLayout.Behavior<FancyFrameLayout>

This would save you from having to cast many of the parameters you receive in method calls from View to the correct subtype — simple convenience is all.

There are methods to save temporary data with / as well as save Behavior-related instance state with /. I’d encourage you to build your Behaviors as lightweight as you can, but these methods help make stateful Behaviors possible.

Attaching a Behavior

Of course, Behaviors don’t do anything on their own — they need to be attached to a child View of a CoordinatorLayout to actually be called. There are three main ways this can be done: programmatically, in XML, or automatically via an annotation.

Snackbar

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

Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте

Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», увеличит личную накопительную скидку и позволит накапливать профессиональный рейтинг Xakep Score!
Подробнее

Вариант 2. Открой один материал

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

Я уже участник «Xakep.ru»

Contributing

This library is probably not complete and might contain bugs that only occur in constellations we did not
yet test. Please do not hesitate to create an issue on GitHub for any problems that cross your way. Please
understand that we cannot afford to spend time fixing problems that do not affect our products, but we’ll
be happy to merge pull requests if you or someone else is able to improve this library.

If you get stuck anywhere in the process, please do not hestitate to ask us anytime at info@opacapp.de.

Please note that we have a Code of Conduct
in place that applies to all project-related communication.

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 и начинаем кодить…

Troubleshooting Coordinated Layouts

is very powerful but error-prone at first. If you are running into issues with coordinating behavior, check the following tips below:

  • The best example of how to use coordinator layout effectively is to refer carefully to the source code for cheesesquare. This repository is a sample repo kept updated by Google to reflect best practices with coordinating behaviors. In particular, see the layout for a tabbed ViewPager list and this for a layout for a detail view. Compare your code carefully to the cheesesquare source code.
  • Make sure that the property is applied to the direct child of the . For example, if there’s pull-to-refresh that the property is applied to the that contains the rather than the 2nd-level descendant.
  • When coordinating between a fragment with a list of items inside of a and a parent activity, you want to put the property on the so the scrolls within the pager are bubbled up and can be managed by the . Note that you should not put that property anywhere within the fragment or the list within.
  • Keep in mind that does not work with . You will need to use the instead as shown in . Wrapping your content in the and applying the property will cause the scrolling behavior to work as expected.
  • Make sure that the root layout of your activity or fragment is a . Scrolls will not react to any of the other layouts.

There’s a lot of ways coordinating layouts can go wrong. Add tips here as you discover them.

Атрибуты BottomAppBar

В таблице ниже показаны атрибуты BottomAppBar.

fabAlignmentMode

Атрибут определяет положение FAB (либо в центре, либо в конце BottomAppBar). Ниже показано выравнивание FAB в конце BottomAppBar.

fabAttached

Атрибут предназначен для привязки FAB к BottomAppBar и может быть true или false. Хотя по не рекомендуется размещать FAB за пределами BottomAppBar, возможность такой настройки имеется. Ниже показана ситуация, когда для атрибута установлено значение false.

Определяет диаметр «колыбели», содержащей FAB.

Задаёт радиус угла в точке встречи «колыбели» и горизонтальной части BottomAppBar.

fabCradleVerticalOffset

Указывает смещение «колыбели» снизу.

Вот весь XML-файл макета, который использовался для приведённых выше примеров.

Мы разобрались с основами нового компонента Android Material — BottomAppBar, а также новыми функциями FAB. Виджет BottomAppBar сам по себе не является сложным в использовании, поскольку он расширяет обычный Toolbar, но он кардинально меняет подход к проектированию интерфейса приложения.

Вторая и третья части этой серии про BottomAppBar будут посвящены работе с меню и навигацией и реализацией различных поведений BottomAppBar в соответствии с принципами Material Design.

→ Реализация BottomAppBar. Часть 2: Меню и элемент управления Navigation Drawer→ Реализация BottomAppBar. Часть 3: Поведения для Android

Настройка

Для начала требуются небольшие первоначальные настройки.

Подробное объяснение того, как включить Material компоненты для вашего Android проекта, вы можете найти на этой странице. Кроме того, в этом туториале вам необходимо использовать Android Studio 3.2 или выше.

Ниже приведены необходимые шаги настройки.

1.Добавьте репозиторий Google Maven в файле .

2.Добавьте зависимость для material компонентов в файле . Имейте в виду, что версия регулярно обновляется.

3.Установите в качестве и версию API минимум для Android P (т.е. 28 и выше).

4.Убедитесь, что ваше приложение наследует тему Theme.MaterialComponents, чтобы BottomAppBar использовал самый последний стиль. В качестве альтернативы вы можете задавать стиль для BottomAppBar при объявлении виджета в XML-файле макета следующим образом:

And this is just the beginning

While each individual part of a Behavior is interesting, when they all come together — that’s where the magic happens. I’d strongly encourage you to look at the source of the Design Library for more advanced behavior — the Android SDK Search Chrome extension is still one of my favorite resources for exploring AOSP code (although the source included in the <android-sdk>/extras/android/m2repository is always the latest).

With a solid foundation in what a Behavior can do, let me know how you use them to #BuildBetterApps

Join the discussion on the Google+ post and follow the Android Development Patterns Collection for more!

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

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

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

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

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

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

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

Параметр задает цвет фона, в который переходит фоновое изображение при свертывании CollapsingToolbarLayout. Внимательный читатель заметит, что на рис. 4 Toolbar вовсе не окрашен в какой-либо цвет, а немного затененное изображение осталось на месте. Чтобы получить такой эффект, нужно указать константу .

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

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

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

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

Custom Behaviors

One example of a custom behavior is discussed in using .

CoordinatorLayout works by searching through any child view that has a CoordinatorLayout Behavior defined either statically as XML with a tag or programmatically with the View class annotated with the decorator. When a scroll event happens, CoordinatorLayout attempts to trigger other child views that are declared as dependencies.

To define your own a CoordinatorLayout Behavior, the layoutDependsOn() and onDependentViewChanged() should be implemented. For instance, AppBarLayout.Behavior has these two key methods defined. This behavior is used to trigger a change on the AppBarLayout when a scroll event happens.

The best way to understand how to implement these custom behaviors is by studying the and sources.

1.2 enterAlways, enterAlwaysCollapsed, snap & exitUntilCollapsed.

Figure 3: enterAlways, enterAlwaysCollapsed, snap, exitUntilCollapsed — all behave the same when used alone.

On their own, each of the four flags above behave similarly to our control. At this point you’re probably wondering why aren’t they different? To see their differences we must combine them with the flag mentioned in Section 1.1 above.

2. Combining ScrollFlags

ScrollFlags can be combined to leverage the unique motion from multiple behaviors. To combine scrollflags we just include them separated by the vertical bar character | e.g. To combine the and attribute we would do something like this.

Let’s look at some examples. In Section 1, we saw that the flag was the only one that did something of significance. Lets combine it with the others flags and see if we get anything different.

Android CoordinatorLayout

Android CoordinatorLayout is a super-powered FrameLayout. It has a lot more to offer than it seems. It has additional level of control over it’s child views. It coordinates the animations and transitions of child views with one another.

Let’s create a new Android Studio project and choose the Basic Activity template that has by default. The layout consists of a Floating Action Button. Clicking it displays a SnackBar as shown below.

Did you notice that the Floating Action Button animates up to make way for the SnackBar and comes back when the SnackBar disappear?

This is no magic. It’s how the Floating Action Button behaves inside a CoordinatorLayout.

Note : CoordinatorLayout can also expand the ToolBar to show more content or collapse it while scrolling, something that is commonly seen when you scroll a WhatsApp User’s profile screen. Don’t worry, we’ll look into this in a later tutorial.

A question that would be popping up in our heads now – How does the CoordinatorLayout know what to do with the child view? The answer lies in the next section.

CoordinatorLayout Behaviors

The FAB within a CoordinatorLayout has been specified a default Behavior that causes it to animate accordingly when another view interacts with it.

Do a Ctrl/CMD+ Click on the FloatingActionButton in the layout/activity and you shall see a Behavior has been defined on the class with an annotation.
It should look like this:

FloatingActionButton.Behavior is the default Behavior class used on the FAB.
We can define our own Behaviors by extending the class CoordinatorLayout.Behavior.

Here T is the class whose Behavior we wish to define. In the above case it is CoordinatorLayout.Behavior.

  • The Behaviors only work on the direct child of the CoordinatorLayout.
  • It’s necessary for the CoordinatorLayout to be the root layout of the activity

Now let’s add a Button widget at the bottom of our screen.

We’ve commented out the FAB from the above layout. Now replace the FloatingActionButton listener with the AppCompatButton in the MainActivity.java as shown below.

This is how the application looks now. Any guesses?

To define the custom Behavior we need to be aware of two important elements:

  • child : It’s the view on which the behavior would be performed.
  • dependency : It’s the view which will trigger the behavior on the child

In the above case the AppCompatButton is the child and the SnackBar is the dependency.

Note: For the FloatingActionButton default behavior, the dependency is not just the SnackBar. There are other View elements too that trigger a behavior on the FloatingActionButton.

Let’s start by creating our own custom Behavior class that moves up the AppCompatButton. Let’s name it CustomMoveUpBehavior.java.

The two neccessary methods that should be overridden in the above class are layoutDependsOn and onDependentViewChanged.
Let’s add override them in our class.

The layoutDependsOn checks whether the dependency that’ll trigger the behavior is an instanceof SnackBar.
The onDependentViewChanged is used to move up the child view(AppCompatbutton) based on a basic math calculation.

Attaching the Behavior to android CoordinatorLayout

To attach the CustomMoveUpBehavior.java we’ll create a Custom AppCompatButton and add the annotations as shown below.

Do the following changes in the activity_main.xml and MainActivity.java

Replace the xml tag with the following one.

Replace the respective Button in the MainActivity.java.

The application when run now should look like this:

Wasn’t it cool? Now let’s try to implement a Custom Behavior for the FAB. We’ll trigger it to rotate and move up when the SnackBar is displayed.
We’ve implemented a CustomRotateBehavior.java class. It’s given below.

The method calculates how far up the screen should the SnackBar come up for the FAB to start changing. Before we do the relevant changes, let’s browse through our project structure.

Android CoordinatorLayout Example Code

Now instead of extending the FloatingActionButton we can just define the app:layout_behavior in the FloatingActionButton view and point it to our subclass.

This is how our activity_main.xml looks now.

The MainActivity.java looks like this now.

Let’s run our application one last time to see the new Behavior.

This brings an end to Android CoordinatorLayout Example. We started with browsing through the default Behavior of the FAB widget and have ended up with overriding it with our own Rotation Behavior.

Not to miss out the Behavior on a Button too. It’s a long way. You can download the Android CoordinatorLayoutBehaviours Project from the below link.

Reference: Official Doc

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

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

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

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

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

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

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

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

Реализация изменения времени суток

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

Чтобы все надежно работало, я добавил ещё один scale в компонент View панели, timeScale. Это число от 0 до 1, которое сообщает View, как далеко от левого края находятся солнце или луна. isNight определяет, какую цветовую палитру и какое небесное тело нужно использовать.

fun setTimeScale(isNight: Boolean, timeScale: Float) {
    this.timeScale  = timeScale.coerceIn(0f, 1f)

    this.isNight = isNight
    invalidate()
}

Цвет toolbar создан из нескольких предопределенных цветов, интерполирован при помощи ArgbEvaluator.evaluate() и помещен в качестве фона с использованием шейдера градиента. Чтобы улучшить цвет, мы добавили интерполятор в timeScale перед вычислением цвета. Он добавляет эффект рассвета и заката в ранние и поздние часы, более точно воспроизводя реальное освещение.

private fun calculateColour2(): Int {
    return colourEvaluator.evaluate(scale, 
      ContextCompat.getColor(context, 
      R.color.toolbar_gradient_2_noon), calculateColour2Base())
      as Int

}



private fun calculateColour2Base(): Int {

    val interpolatedScale = interpolate(timeScale)

    return if (isNight) {
        when (interpolatedScale) {
            in 0.0f..0.5f -> 
                colourEvaluator.evaluate(interpolatedScale * 2,
                ContextCompat.getColor(context, 
                R.color.toolbar_gradient_2_evening), 
                ContextCompat.getColor(context,
                R.color.toolbar_ gradient_2_midnight)) as Int
            else -> colourEvaluator.evaluate((interpolatedScale - 
                0.5f) * 2, ContextCompat.getColor(context, 
                R.color.toolbar_gradient_2_midnight), 
                ContextCompat.getColor(context, 
                R.color.toolbar_gradient_2_morning)) as Int
        }
    } else {
        when (interpolatedScale) {
            in 0.0f..0.5f -> 
                colourEvaluator.evaluate(interpolatedScale * 2, 
                ContextCompat.getColor(context, 
                R.color.toolbar_gradient_2_morning), 
                ContextCompat.getColor(context, 
                R.color.toolbar_gradient_2_noon)) as Int
            in 0.5f..0.75f -> 
                colourEvaluator.evaluate((interpolatedScale - 0.5f) 
                * 4, ContextCompat.getColor(context, 
                R.color.toolbar_gradient_2_noon), 
                ContextCompat.getColor(context, 
                R.color.toolbar_gradient_2_noon_evening)) as Int
            else -> colourEvaluator.evaluate((interpolatedScale - 
                0.75f) * 4, ContextCompat.getColor(context, 
                R.color.toolbar_gradient_2_noon_evening), 
                ContextCompat.getColor(context, 
                R.color.toolbar_gradient_2_evening)) as Int
        }
    }
}

Для вечернего освещения мы добавили одно ручное значение (0,75f), так как интерполированный цвет между днем и вечером выглядел плохо. Чтобы убедиться, что toolbar всегда возвращается к цвету бренда при сжатии, второй цвет градиента также интерполируется к цвету бренда при определенном положении при скроллинге.

LinearGradient(0f, 0f, scale * width, scale * height, calculateColour1(), calculateColour2(), Shader.TileMode.CLAMP)

Реализация арки

Вся магия происходит в ToolbarArcBackground. Это довольно простой подкласс Android View. Так как у нас есть компонент, вычисляющий масштаб, нам нужно только понять, как получить то, что мы хотим.

Я экспериментировал с несколькими разными подходами. Первый из них – использование Path, чтобы обрезать нижнюю часть макета. К сожалению, в таком случае край получался неровным.

Как и во многих случаях, самым лучшим способом оказался самым простой… то есть, обман. Я воспользовался тем фактом, что фон главного экрана был однотонным. Самым простым способом оказалось нарисовать белый эллипс поверх всего контента toolbar. Чтобы избежать слишком острых концов, я нарисовал эллипс немного за пределами экрана слева и справа.

Метод setScale кастомного View хранит текущее значение и аннулирует контент.

fun setScale(scale: Float) {
    this.scale = if (scale < 0) {
        0f
    } else {
        scale
    }

    invalidate()
}

OnDraw затем просто рисует подходящий эллипс внизу.

override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)
// draw some other stuff here first
    canvas.drawOval(
        (-extendOverBoundary).toFloat(), height - arcSize * scale,    
        (width + extendOverBoundary).toFloat(),
        height + arcSize * scale,
        ovalPaint)
}

Когда значение достигает нуля, а пользователь долистывает до точки перехода, в которой край toolbar становится прямым, эллипс полностью исчезает.

Reacting to Scroll

We can configure the to react and change as the page scrolls:

For example, we can have the toolbar hide when the user scrolls down on a list or expand as the user scrolls to the header. There are many effects that can be configured by using the . First, we need to make sure we add the design support library to our file:

Next, inside the activity layout XML such as , we need to setup our coordinated layout with a and a scrolling container such as a :

Of course, the could also be replaced with a which could then allow for fragments to be loaded instead:

This type of layout results in the following:

Refer to the for additional explanation and specifics. For troubleshooting, refer to this .

Advanced Scrolling Behavior for Toolbar

The proper way of reacting to simple scroll behavior is leveraging the built into the Design Support Library as shown in the previous section. However, there are a few other relevant resources around reacting to scrolling events with a more manual approach:

  • Hiding or Showing Toolbar on Scroll — Great guide on an alternate strategy not requiring the to replicate the behavior of the «Google Play Music» app. Sample code can be found here.
  • Hiding or Showing Toolbar using CoordinatorLayout — Great guide that outlines how to use to hide the Toolbar and the FAB when the user scrolls.

With these methods, your app can replicate any scrolling behaviors seen in common apps with varying levels of difficulty not captured with the method shown above.

3.3 scroll|enterAlways|enterAlwaysCollapsed

Figure 9:

Given our descriptions of & in Section 2.1 above, they behave very differently. As a result the app gets quite confused as to how to handle downward-scrolls. I would recommend not to use this

4. ScrollFlags Observations & Considerations

  1. scrollflags depend heavily on the scrolling motion of a or with some attached to be able to see the effects of the scroll-flags (see article of CoordinatorLayout Behaviors).
  2. The flag is key to being able to enable the scrolling.
  3. The ordering of flags has no impact. e.g and perform the exact same function.
  4. Be wary of mixing scrollflags that may conflict with regards to their function. See section 3.3 on how and conflict when a downward-scroll to expand the CollapsingToolbar is introduced.

Conclusion

ScrollFlags are essential to giving your some personalized character. As we’ve seen, there are several ways of combining flags to get distinct outcomes that can enhance our apps visual aesthetic and better promote our business rules. We’ve also seen some flags when combined can clash and give a negative experience to your users.

Thanks again for reading!

Check out my other article on Behaviors to learn how to create your own!

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *