На презентации Google I/O 15, компания Google представила новую версию библиотеки поддержки которая реализует несколько компонентов, сильно связанных со спецификациями Material Design, среди этих компонентов вы можете найти новые типы ViewGroup такие как
При правильном комбинировании и настройке данные Viewgroup могут быть очень мощным инструментом, по этому я решил написать статью с некоторыми настройками и советами.
Как и предполагает его название, цель и философия этой
Рассмотрим следующую картинку:
В этом примере можем видеть как view элементы размещены друг относительно друга, не прибегая к детальному просмотру, мы видим как одни
Это будет простейшая структура использования
Рассмотрим скелет данного layout. У этого
Проще говоря,
Это может прозвучать запутано сначала, но как, — «Лучше один раз увидеть, чем сто раз услышать», к вашему вниманию .gif-пример:
В данном случае
Мы можем управлять поведением элементов
Со значением:
Как видите настоящая мощь
Все эти параметры доступны в документации Google Developers. В любом случае, я рекомендую поиграть с примерами. В конце статьи размещены ссылки на репозитории Github с реализацией примеров.
Проведем не большой эксперимент, запустим Android Studio (>= 1.4) и создадим проект из шаблона: Scrolling Activity, ничего не изменяя, компилирием и вот что мы видим:
При рассмотрении сгенерированного кода, ни макеты layout ни java классы не имеют ничего относящегося к маштабированию анимации плавающей кнопки при прокрутке. Почему?
Ответ находится в исходном коде
За маштабирование анимации отвечает новый элемент, представленый вместе с design library, под названием
Продолжим углубление в код, если вы посмотрите внутрь пакета виджетов design support library, то сможете найти открытй клас под названием:
Создать свой шаблон поведения (Behavior) не так и сложно как может показаться, для начала мы должны принять во внимание несколько основных элементов: child и dependency.
child это элемент который усиливает поведение, dependency — тот кто будет обслуживать его как тригер для взаимодействия с child элементом. Посмотрим на пример, child — элемент ImageView, а dependency это Toolbar, таким образом, если Toolbar движется, ImageView тоже движется.
Теперь, когда мы определили концепт, можем поговорить о реализации, первым шагом будет наследование от:
Метод:
Всякий раз когда
А теперь все вместе:
Дополнительные материалы
Пример Coordinator Behavior — Github
Примеры Coordinator — Github
Введение в coordinator layout на Android — Grzesiek Gajewski
AppbarLayout
, CollapsingToolbarLayout
и CoordinatorLayout
.При правильном комбинировании и настройке данные Viewgroup могут быть очень мощным инструментом, по этому я решил написать статью с некоторыми настройками и советами.
CoordinatorLayout
Как и предполагает его название, цель и философия этой
ViewGroup
является координация view элементов, которые находятся внутри него.Рассмотрим следующую картинку:
В этом примере можем видеть как view элементы размещены друг относительно друга, не прибегая к детальному просмотру, мы видим как одни
View
зависят от других. (мы поговорим об этом позже).Это будет простейшая структура использования
CoordinatorLayout
:Посмотреть код
<?xml version="1.0" encoding="utf-8"?>
<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:background="@android:color/background_light"
android:fitsSystemWindows="true"
>
<android.support.design.widget.AppBarLayout
android:id="@+id/main.appbar"
android:layout_width="match_parent"
android:layout_height="300dp"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
android:fitsSystemWindows="true"
>
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/main.collapsing"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
android:fitsSystemWindows="true"
app:contentScrim="?attr/colorPrimary"
app:expandedTitleMarginStart="48dp"
app:expandedTitleMarginEnd="64dp"
>
<ImageView
android:id="@+id/main.backdrop"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:fitsSystemWindows="true"
android:src="@drawable/material_flat"
app:layout_collapseMode="parallax"
/>
<android.support.v7.widget.Toolbar
android:id="@+id/main.toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:layout_collapseMode="pin"
/>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20sp"
android:lineSpacingExtra="8dp"
android:text="@string/lorem"
android:padding="@dimen/activity_horizontal_margin"
/>
</android.support.v4.widget.NestedScrollView>
<android.support.design.widget.FloatingActionButton
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_margin="@dimen/activity_horizontal_margin"
android:src="@drawable/ic_comment_24dp"
app:layout_anchor="@id/main.appbar"
app:layout_anchorGravity="bottom|right|end"
/>
</android.support.design.widget.CoordinatorLayout>
Рассмотрим скелет данного layout. У этого
CoordinatorLayout
имеется только три дочерних элемента: AppbarLayout
, прокручиваемый view
и закрепленный FloatingActionBar
.Посмотреть код
<CoordinatorLayout>
<AppbarLayout/>
<scrollableView/>
<FloatingActionButton/>
</CoordinatorLayout>
AppBarLayout
Проще говоря,
AppBarLayout
это LinearLayout
на стероидах, их элементы размещены вертикально, с определенными параметрами элементы могут управлять их поведением, когда содержимое прокручивается.Это может прозвучать запутано сначала, но как, — «Лучше один раз увидеть, чем сто раз услышать», к вашему вниманию .gif-пример:
В данном случае
AppBarLayout
это синяя view, размещенная под исчезающим изображением, она содержит Toolbar
, LinearLayout
с заголовком и подзаголовком и TabLayout
с несколькими вкладками.Мы можем управлять поведением элементов
AppbarLayout
с помощью параметров: layout_scrollFlags
. Значение: scroll
в данном случае присутствует почти во всех элементах view, если бы этот параметр не был указан ни в одном из элементов AppbarLayout, он остался бы неизменным, позволяя прокручиваемому контенту проходить позади него.Со значением:
snap
, мы избегаем попадания в полу-анимационного-состояния, это значит, что анимация всегда скрывает или отображает полный размер view.LinearLayout
который содержит заголовок и подзаголовок будет всегда отображен при прокручивании вверх, (enterAlways
значение), и TabLayout
будет видим всегда так как на него не установлен ни один флаг.Как видите настоящая мощь
AppbarLayout
определяется должным управлением его флагами прокрутки в определенных view.Посмотреть код
<AppBarLayout>
<CollapsingToolbarLayout
app:layout_scrollFlags="scroll|snap"
/>
<Toolbar
app:layout_scrollFlags="scroll|snap"
/>
<LinearLayout
android:id="+id/title_container"
app:layout_scrollFlags="scroll|enterAlways"
/>
<TabLayout /> <!-- no flags -->
</AppBarLayout>
Все эти параметры доступны в документации Google Developers. В любом случае, я рекомендую поиграть с примерами. В конце статьи размещены ссылки на репозитории Github с реализацией примеров.
Флаги AppbarLayout
SCROLL_FLAG_ENTER_ALWAYS
: При использовании флага, view будет прокручиваться вниз не зависимо от других прокручиваемых view.SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED
: Дополнительный флаг для 'enterAlways', который изменяет возвращаемый view к изначально прокручиваемому, при исчезновении высоты.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED
: При выходе, view будет прокручен до тех пор пока не исчезнет.SCROLL_FLAG_SCROLL
: Элемент view будет прокручиваться в направлении события прокрутки.SCROLL_FLAG_SNAP
: В конце прокрутки, если view видим только частично, он будет докручен до его ближайшего края.CoordinatorLayout Behaviors
Проведем не большой эксперимент, запустим Android Studio (>= 1.4) и создадим проект из шаблона: Scrolling Activity, ничего не изменяя, компилирием и вот что мы видим:
При рассмотрении сгенерированного кода, ни макеты layout ни java классы не имеют ничего относящегося к маштабированию анимации плавающей кнопки при прокрутке. Почему?
Ответ находится в исходном коде
FloatingActionButton
, с тех пор как Android Studio v1.2 включает java декомпилятор, с помощью ctrl/cmd + click
мы можем проверить исходный код и посмотреть что происходит:Посмотреть код
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Floating action buttons are used for a
* special type of promoted action.
* They are distinguished by a circled icon
* floating above the UI and have special motion behaviors
* related to morphing, launching, and the transferring anchor point.
*
* blah.. blah..
*/
@CoordinatorLayout.DefaultBehavior(
FloatingActionButton.Behavior.class)
public class FloatingActionButton extends ImageButton {
...
public static class Behavior
extends CoordinatorLayout.Behavior<FloatingActionButton> {
private boolean updateFabVisibility(
CoordinatorLayout parent, AppBarLayout appBarLayout,
FloatingActionButton child {
if (a long condition) {
// If the anchor's bottom is below the seam,
// we'll animate our FAB out
child.hide();
} else {
// Else, we'll animate our FAB back in
child.show();
}
}
}
...
}
За маштабирование анимации отвечает новый элемент, представленый вместе с design library, под названием
Behavior
. В данном случае CoordinatorLayout.Behavior<FloatingAcctionButton>
, который зависит от некоторых факторов включая прокрутку, показывать FAB или нет, интересно, не правда ли?SwipeDismissBehavior
Продолжим углубление в код, если вы посмотрите внутрь пакета виджетов design support library, то сможете найти открытй клас под названием:
SwipeDismissBehavior
. С этим новым Behavior
мы можем очень легко реализовать функцию свайп для отмены в наших шаблонах с CoordinatorLayout
:Посмотреть код
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_swipe_behavior);
mCardView = (CardView) findViewById(R.id.swype_card);
final SwipeDismissBehavior<CardView> swipe
= new SwipeDismissBehavior();
swipe.setSwipeDirection(
SwipeDismissBehavior.SWIPE_DIRECTION_ANY);
swipe.setListener(
new SwipeDismissBehavior.OnDismissListener() {
@Override public void onDismiss(View view) {
Toast.makeText(SwipeBehaviorExampleActivity.this,
"Card swiped !!", Toast.LENGTH_SHORT).show();
}
@Override
public void onDragStateChanged(int state) {}
});
LayoutParams coordinatorParams =
(LayoutParams) mCardView.getLayoutParams();
coordinatorParams.setBehavior(swipe);
}
Custom Behaviors
Создать свой шаблон поведения (Behavior) не так и сложно как может показаться, для начала мы должны принять во внимание несколько основных элементов: child и dependency.
Child и dependency
child это элемент который усиливает поведение, dependency — тот кто будет обслуживать его как тригер для взаимодействия с child элементом. Посмотрим на пример, child — элемент ImageView, а dependency это Toolbar, таким образом, если Toolbar движется, ImageView тоже движется.
Теперь, когда мы определили концепт, можем поговорить о реализации, первым шагом будет наследование от:
CoordinatorLayout.Behavior<T>
, значение T
будет класс который принадлежит view, что необходим нам для координации, в данном случае ImageView, после чего мы должны переопределить следующие методы:- layoutDependsOn
- onDependentViewChanged
Метод:
layoutDependsOn
будет вызван каждый раз когда что-то случится в layout, чтобы вернуть true
, как только мы определили dependency, в примере, этот метод срабатывает автоматически при прокручивании (т.к. Toolbar
будет двигаться), таким образом, мы можем подать знак нашему child отреагировать соответствующим образом.Посмотреть код
@Override
public boolean layoutDependsOn(?
CoordinatorLayout parent,
CircleImageView, child,
View dependency) {
return dependency instanceof Toolbar;
}
Всякий раз когда
layoutDependsOn
возвращает true
будет вызван второй onDependentViewChanged
. Вот тут-то мы и должны реализовать нашу анимацию, перевод или движения всегда зависят от предоставленной зависемости.Посмотреть код
public boolean onDependentViewChanged(?
CoordinatorLayout parent,
CircleImageView avatar,
View dependency) {?
modifyAvatarDependingDependencyState(avatar, dependency);
}
private void modifyAvatarDependingDependencyState(
CircleImageView avatar, View dependency) {
// avatar.setY(dependency.getY());
// avatar.setBlahBlat(dependency.blah / blah);
}
А теперь все вместе:
Посмотреть код
public static class AvatarImageBehavior
extends ?CoordinatorLayout.Behavior<CircleImageView> {
@Override
public boolean layoutDependsOn(?
CoordinatorLayout parent,
CircleImageView, child,
View dependency) {
return dependency instanceof Toolbar;
} ??
public boolean onDependentViewChanged(?
CoordinatorLayout parent,
CircleImageView avatar,
View dependency) {?
modifyAvatarDependingDependencyState(avatar, dependency);
}
private void modifyAvatarDependingDependencyState(
CircleImageView avatar, View dependency) {
// avatar.setY(dependency.getY());
// avatar.setBlahBlah(dependency.blah / blah);
}
}
Дополнительные материалы
Пример Coordinator Behavior — Github
Примеры Coordinator — Github
Введение в coordinator layout на Android — Grzesiek Gajewski