-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathmaterial.Rmd
More file actions
428 lines (281 loc) · 37 KB
/
material.Rmd
File metadata and controls
428 lines (281 loc) · 37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
<!-- Ran out of time
Should show shorter video.
RecyclerView wasn't clear; could take more time??
Skipped cards (fine)
Fab/Snackbar fun
Skipped custom behavior (sad)
need to fix transition/fab bug... -->
# Material Design
This lecture discusses [Material design](https://material.io/guidelines/material-design/introduction.html): the _design language_ created by Google to support mobile user interfaces. The design language focuses on mobile designs (e.g., for Android), but can also be used across platforms (e.g., on the web through [css frameworks](https://material.io/components/web/)).
<p class="alert alert-info">This lecture references code found at <https://github.com/info448/lecture05-material>.</p>
<p class="alert alert-warning">Material Design support was introduced in API 21 Lollipop, and so a device running that version of Android or later is required to access all the Material features supported by Android. However, most functionality is also available via [compatibility libraries](https://developer.android.com/training/material/compatibility.html) (i.e., `AppCompat`), and so can be utilized on older devices as well.</p>
<p class="alert alert-warning">Google is in the process of updating their Material Design implementation to use [Material Design Components (MDC)](https://material.io/develop/android/). As that version is still in beta and only available on Android Pie, this lecture describes how to use the "legacy" version of the Material support library.</p>
## The Material Design Language `r #[10min]`
**Material design** is a _design language_: a "vocabulary" of visual and interactive patterns that are shared across systems. Material forms both a "look and feel" for Google applications and modern (API 21+) Android apps in general, as well as providing a
<div class="figure">
<iframe width="560" height="315" src="https://www.youtube.com/embed/YaG_ljfzeUw?rel=0" frameborder="0" allowfullscreen></iframe>
<p class="caption">A video explanation of the Material language (via https://developer.android.com/design/material/index.html)</p>
</div>
In summary, the Material Design language is based around [three main principles](https://material.io/guidelines/material-design/introduction.html#introduction-principles):
- ___Material is the metaphor___. The Material design language is built around presenting user interfaces as being made of virtual [**materials**](https://material.io/guidelines/material-design/material-properties.html), which ara [paper-like](https://material.io/guidelines/layout/principles.html#principles-how-paper-works) surfaces floating in space. Each surface has a uniform thickness (`1dp`), but different _elevation_ (conveyed primarily through shadows and _perspective_). This physical metaphor helps to indicate different affordances and user interactions: for example, a button is "raised" and can be "pushed down".
- ___Motion provides meaning___. Material also places great emphasis on the use of [**motion**](https://material.io/guidelines/motion/material-motion.html) of these materials in order to help describe the relationships between components as well as make design overall more "delightful". Material applications include lots of animations and flourishes, making the app's usage continuous and connected. Surfaces are able to change shape, size, and position (as long as they stay in their plane—they cannot fold, but they can split and rejoin) in response to user input.
- ___Bold, graphic, intentional___. Material applications follow a particular aesthetic in terms of things such as [color](https://material.io/guidelines/style/color.html) (not muted), [imagery](https://material.io/guidelines/style/imagery.html) (usually lots of it). Material applications _look_ like material applications, though they can still be customized to meet your particular needs.
For more information on the Material design language, see the [**official guidelines**](https://material.io/guidelines/) (click the hamburger button on the left to navigate). This documentation contains extensive examples and instructions, ranging from suggested font sizes to widget usage advice.
<p class="alert">This lecture focuses on ways to implement specific aspects of the Material Design language in Android applications, rather than on specifics of how to obey the language guidelines.</p>
## Material Styles & Icons `r #[10min?]`
The first and easiest step towards making a Material-based application is to utilize the provided [Material Themes](https://developer.android.com/training/material/theme.html) in order to "skin" your application. These themes are available on API 21 Lollipop and later; for earlier operating system, you can instead use equivalent themes in `AppCompat` (which are in fact the default themes for new Android apps!)
Applying themes (including Material themes) are discussed in more detail in the [Styles & Themes](#styles-themes) lab.
- You can see what specific properties are applied by these styles and themes by browsing the [source code](https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/res/res/values) for the Android framework—check out the `styles_material.xml` and `themes_material.xml` (these will also reference values defined in the variable `color` and `dimens` resources). The `AppCompat` styles and themes can be [source code](https://android.googlesource.com/platform/frameworks/support.git/+/refs/heads/master/v7/appcompat/res/values/) for the `v7` support library.
<!-- App bar stuff here? Also check old book (s17)-->
<!-- Don't do this in lecture... -->
Let's start by pointing to one of the most prominent visual components in the default app: the [___App Bar___](https://developer.android.com/training/appbar/index.html) or ___Action Bar___. This acts as the sort of "header" for your app, providing a dedicated space for navigation and interaction (e.g., through menus). The [`ActionBar`](https://developer.android.com/reference/android/support/v7/app/ActionBar.html)^[http://developer.android.com/reference/android/support/v7/app/ActionBar.html] is a specific type of [`Toolbar`](https://developer.android.com/reference/android/support/v7/widget/Toolbar.html) that is most frequently used as the App Bar, offering a particular "look and feel" common to Android applications.
While the `AppCompatActivity` used throughout this course automatically provides an Action Bar for the app, it is also possible to add it directly (such as if you are using a different Activity subclass). To add your own Action Bar, you specify a **theme** that does _not_ include an `ActionBar`, and then include an `<android.support.v7.window.Toolbar>` element inside your layout wherever you want the toolbar to go. See [Setting up the App Bar](http://developer.android.com/training/appbar/setting-up.html) for details. This will also allow you to put the Toolbar anywhere in the application's layout (e.g., if you want it to be stuck to the bottom).
In practice, the biggest part of utilizing a Material Theme is defining the [color palette](https://developer.android.com/training/material/theme.html#ColorPalette) for your app. The Material design specification describes a broad color palette (with available [swatches](https://material.io/guidelines/style/color.html#color-color-palette)); however, it is often more useful to pick your colors using the provided [**color picker tool**](https://material.io/color/#!/)^[https://material.io/color/#!/], which allows you to easily experiment with different color combinations.
- Pick your _primary_ and _secondary_ color, and then assign the resource values `colorPrimary`, `colorPrimaryDark`, and `colorAccent` in `res/values/colors.xml`. This will let you easily "brand" your application.
Material-specific attributes such as `android:elevation` are also available in API 21+, though making shadows visible on arbitrary elements requires some additional work.
In addition to styles, Material also includes a large set of [**icons**](https://material.io/icons/) for use in applications. These icons supplement the built in `ic_*` [drawables](http://androiddrawables.com/) that are built into the platform and are available for use (and show up as auto-complete options in the IDE).
Instead these icons are available as [vector drawables](https://developer.android.com/training/material/drawables.html#VectorDrawables)—rather than being a `.png` file, images are defined as using an XML schema similar to that used in [Scalable Vector Graphics (SVG)](https://en.wikipedia.org/wiki/Scalable_Vector_Graphics).
<p class="alert alert-info">SVG is used to represent [vector graphics](https://en.wikipedia.org/wiki/Vector_graphics)—that is, pictures defined in terms of the connection between points (lines and other shapes), rather than in terms of the pixels being displayed (called a [raster image](https://en.wikipedia.org/wiki/Raster_graphics)). In order to be shown on a screen, these lines are converted (rastered) into grids of pixels based on the size and resolution of the display. This allows SVG images to "scale" or "zoom" independent of the size of the display or the image—you never need to worry about things getting blurry as you make them larger!</p>
In order to include a Material icon, you will need to generate the XML code for that particular image. Luckily, Android Studio includes XML definitions for all the Material icons (though you can also define your own vector drawables).
- To create a vector drawable, select `File > New > Vector Asset` from the Android Studio menu. You can then click on the icon to browse for which Material icon you want to create.
- By default the icon will be colored _black_. If you wish to change the color of the icon when included in your layout, specify the color through the [`android:tint`](https://developer.android.com/training/material/drawables.html#DrawableTint) attribute of the View in your XML (e.g., on the `ImageButton`).
It is also possible to define arbitrary vector shapes in API 21+. For example, the starter code includes a resource for a `<shape>` element that is shaped like an oval.
## Design Support Libraries
In addition to the styling and resource properties available as part of the Android framework, there are also additional components available through extra [**support libraries**](https://developer.android.com/training/material/design-library.html).
Similar to Volley, these libraries are not built into Android and so need to be explicitly listed under `dependencies` in your app's `build.gradle` file. For example:
```gradle
//note the version number needs to match your SDK version
implementation 'com.android.support:design:28.0.0'
```
will include the latest version (as of this writing) of the **design support library**, which includes elements such as the Floating Action Button and Coordinator Layouts (described below).
<div class="alert alert-warning"><p>Note that if you have issues installing the dependency, the [setup for including support libraries](https://developer.android.com/topic/libraries/support-library/setup.html) was changed in July 2017 to support downloading dependency libraries through [maven](https://maven.apache.org/) rather than directly from Android Studio. To support this, you will need to change the _project's_ `build.gradle` repository declaration to look like:
```gradle
allprojects {
repositories {
jcenter()
maven {
url "https://maven.google.com"
}
}
}
```
</div>
<!-- 40min? -->
### Widgets {-}
The support libraries support a number of useful widgets (specialized Views) for creating Material-styled apps.
#### RecyclerView {-}
The [`RecyclerView`](https://developer.android.com/training/material/lists-cards.html#RecyclerView) is a more advanced version of a `ListView`, providing a more robust system for support interactive Views within the list as well as providing animations for things like adding and removing list items.
This class is part of the `v7` support library (and not the Material Design library specifically), and so you will need to include it specifically in your _app's_ `build.gradle` file (the same way you included Volley):
```gradle
implementation 'com.android.support:cardview-v7:28.0.0'
```
Implementing a `RecyclerView` is very similar to implementing a `ListView` (with the addition of the [ViewHolder pattern](https://developer.android.com/training/improving-layouts/smooth-scrolling.html#ViewHolder)), though you also need to declare a `LayoutManager` to specific if your RecyclerView should use a List or a Grid.
The best way to understand the RecyclerView is to look at the example and modify that to fit your particular item view. For example, you can change a ListView into a RecyclerView by adapting the sample code provided by Google's [official documentation](https://developer.android.com/guide/topics/ui/layout/recyclerview)^[https://developer.android.com/guide/topics/ui/layout/recyclerview]:
- First, you will need to replace the XML View declaration with a `<android.support.v7.widget.RecyclerView>` element. In effect, we're just modifying the **controller** for the list.
- There are a few extra steps when setting up the `RecyclerView` in the Java code. In particular, you will also need to associate a `LayoutManager` object to with the View—for example, a `LinearLayoutManager` to show items in a line (a la a ListView), or a `GridLayoutManager` to show items in a grid (a la a GridView).
- In Kotlin, these can be assigned using the `.apply()` method. This method takes a callback function, and executes each line in that callback function with the object (e.g., the `recyclerView`) scoped as `this`. This is a shortcut for calling lots of methods on the same object in a row.
Similar shortcuts are provided by the `run()`, `with()` and `let()` functions. See [this guide](https://medium.com/@elye.project/mastering-kotlin-standard-functions-run-with-let-also-and-apply-9cd334b0ef84) for a clear explanation.
- All `RecyclerViews` require _custom adapters_: we cannot just use a built-in adapter such as `ArrayAdapter`. To do this, create a class that `extends RecyclerView.Adapter<VH>`.
- The generic in this class is a class representing a [View Holder](https://developer.android.com/training/improving-layouts/smooth-scrolling.html#ViewHolder). This is a pattern by which each individual View that will be inflated and referenced is stored as an individual _object_ with the particular Views to be modified (e.g., `TextView`) saved as instance variables. This avoids the need for the adapter to repeatedly use `findViewById()`, which is an expensive operation (since it involves crawling the View hierarchy tree!)
The `ViewHolder` will be _another_ class—usually an inner class of the adapter. We can treat it as a `data` class (a simple "container" object, similar to a `struct` in C). but don't need to declare it as such directly.
If needed, you can assign these instance variables the results of the `findViewById()` calls when the ViewHolder object is initialized.
- The `RecyclerView` (not the `ViewHolder`) requires you to override the `onCreateViewHolder()` method, which will `inflate()` the View and instantiate a `ViewHolder` for each item when that item needs to be displayed for the first time.
- In the overridden `onBindViewHolder()` you can do the actual work of assigning model content to the particular View (e.g., called `setText()`). You don't need to use `findViewById()` here, because you've already saved a reference to that View in the `ViewHolder`, so you can assign to it directly!
- The `getItemCount()` is used internally for the RecyclerView to determine when you've gotten to the "end" of the list.
- Finally, you can assign the custom adapter to the `RecyclerView` in order to associate the model and the View.
And that's about it! While it is more code and more complex than a basic ListView, as soon as you've added in the ViewHolder pattern or done any other kinds of customizations, you're basically at the same level. Using a RecyclerView instead of a ListView also enables built-in animations, as well as common user actions such as "drag-to-order" or "swipe-to-dismiss". See [this guide](https://medium.com/@ipaulpro/drag-and-swipe-with-recyclerview-b9456d2b1aaf) for a walkthrough on adding these capabilities.
Note that _unlike_ with a ListView, we usually modify the items shown in a `RecyclerView` by modifying the data model (e.g., the array or ArrayList) directly. But we will need to [notify](https://guides.codepath.com/android/using-the-recyclerview#notifying-the-adapter) the `RecyclerView`'s adapter of these changes by calling one of the various `.notify()` methods on the adapter (e.g., `adapter.notifyItemInserted(position)`). This will cause the adapter to "refresh" the display, and it will actually animate the changes to the list by default!
#### Cards {-}
The `v7` support library also provides a View for easily styling content as [**Cards**](https://developer.android.com/training/material/lists-cards.html#CardView). A `CardView` is basically a `FrameLayout` (a ViewGroup that contains one child View), but include borders and shadows that make the group look like a card.
- You will need to load the `CardView` class as a gradle dependency using `compile 'com.android.support:cardview-v7:26.1.0'`.
To utilize a `CardView`, simply include it in your layout as you would any other ViewGroup:
```xml
<android.support.v7.widget.CardView
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="@dimen/card_width"
android:layout_height="@dimen/card_height"
android:layout_gravity="center"
card_view:cardCornerRadius="4dp">
<!-- A single TextView in the card -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/card_text" />
</android.support.v7.widget.CardView>
```
- Notice the `card_view:cardCornerRadius` attribute; this is an example of how specific Views may have their own custom properties (sometimes available via a different schema).
Since Cards are `FrameLayouts`, they should only contain a single child. If you want to include multiple elements inside a Card (e.g., an image, some text, and a button), you will need to nest another ViewGroup (such as a `LinearLayout`) inside the Card.
For design suggestions on using Cards, including spacing information, see the [Material Design guidelines](https://material.io/guidelines/components/cards.html).
<p class="alert alert-info"> If you want to include a circular image in your card (or anywhere else in your app), the easiest solution is to include an external library that provides such a View, the most popular of which is [`de.hdodenhof.circleimageview`](https://github.com/hdodenhof/CircleImageView).</p>
#### Floating Action Buttons (FAB) {-}
While RecyclerViews and Cards are found in the `v7` support library, the most noticeable and interesting components come from the [Design Support Library](https://developer.android.com/training/material/design-library.html), which specifically includes components for supporting Material Design.
- This library should be included in gradle as `com.android.support:design:27.1.1`, as in the above example.
The most common element from this library is the [**Floating Action Button (FAB)**](https://developer.android.com/training/material/design-library.html#CreateFAB). This is a circular button that "floats" above the content of the screen (at a higher elevation), and represents the _primary action_ of the UI.
- A screen should only ever have one FAB, and only if there is a single main action that should be performed. See the [design guidelines](https://material.io/guidelines/components/buttons-floating-action-button.html) for more examples on how to use (and not use) FABS.
Like a Card, you can include a FAB in your application by specifying it as an element in your XML:
```xml
<android.support.design.widget.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_margin="@dimen/fab_margin"
android:src="@drawable/ic_my_icon" />
```
- Because FABs are a subclass of `ImageButton`, we can just replace an existing button with a FAB without breaking anything.
Fabs support a number of additional effects. For example, if you make the FAB clickable (via `android:clickable`), you can specify an `app:rippleColor` to give it a rippling effect when pressed. Further details will be presented below.
#### Snackbars {-}
The Design Support Library also includes an alternative to Toast messages called [**Snackbars**](https://developer.android.com/training/snackbar/showing.html). This is a user-facing pop-up message that appears at the bottom of the screen (similar to a Toast).
Snackbars are shown using a similar structure to Toasts:
```kotlin
val snack = Snackbar.make(view, "Let's go out to the lobby!", Snackbar.LENGTH_LONG).show();
```
- Instead of calling the `Toast.makeText()` factory method, we call the `Snackbar.make()` factory method. The first parameter in this case needs to be a View that the Snackbar will be "attached" to (so shown with)—however, it doesn't really matter which View is given, since the method will search up the view hierarchy until it gets to the root content view or a special layout called a `CoordinatorLayout`.
- You'll notice that the Snackbar overlays the content (including the FAB). This will be addressed below by introducing a `CoordinatorLayout`.
Additionally, it is possible to give Snackbars their own _action_ that the user can activate by clicking on the Snackbar. This allows the bar to, for example, show a delete confirmation but providing an "undo" action. The action is specified by calling the `.setAction()` method on the Snackbar, and passing in a title for the action as well as an `OnClickListener`:
```java
mySnackbar.setAction("Click", new View.OnClickListener() {
@Override
public void onClick(View view) {
//...
}
});
```
```kotlin
snack.setAction("Click") {
//...
};
```
- For practice, make the Snackbar `.hide()` the FAB, but provide an "undo" action that will `.show()` it!
Again, see the [design guidelines](https://material.io/guidelines/components/snackbars-toasts.html) for more examples on how to use (and not use) Snackbars.
<!-- Break here! -->
### Coordinator Layout {-}
In order to fix the Snackbar and Fab overlap, we'll need to utilize one of the most powerful but complex classes in the Material support library: the [**CoordinatorLayout**](https://developer.android.com/reference/android/support/design/widget/CoordinatorLayout.html). This layout is described as "a super-powered Framelayout", and provides support for a number of interactive and animated behaviors involves other classes. A lot of the "whizbang" effects in Material are built on top of the CoordinatorLayout (or other classes that rely on it).
To start our exploration of CoordinatorLayout, let's begin by fixing the Snackbar overlap. To do this, we'll take the existing layout for the activity and "wrap" it in a `<android.support.design.widget.CoordinatorLayout>` element:
```xml
<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">
<!-- Previous layout elements -->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- etc -->
</RelativeLayout>
</android.support.design.widget.CoordinatorLayout>
```
We will also need to move the FAB definition so that it is a _direct child_ of the CoordinatorLayout. Once we have done so, we should be able to click the button and watch it move up to make room for the Snackbar!
- How this works is that the CoordinatorLayout allows you to give [**Behaviors**](https://developer.android.com/reference/android/support/design/widget/CoordinatorLayout.Behavior.html) to its child views; these Behaviors will then be executed when the state of the CoordinatorLayout (or its children) changes. For example, the built-in `FloatingActionButton.Behavior` defines how the button should move in response to its parent changing size, but Behaviors can also be defined in response to use interactions such as swipes or other gestures.
#### Scrolling Layouts {-}
Indeed, the built-in behaviors can be quite complex (and wordy to implement), and the best way to understand them is to see them in action. To see an example of this, create a **new** Activity for your application (e.g., `File > New > Activity`). But instead of creating an _Empty_ Activity as you've done before, you should instead create a new **ScrollingActivity**.
- Modify the FAB action so that when you click on it, you send an `Intent` for to open up this new Activity:
```java
startActivity(new Intent(MainActivity.this, ScrollingActivity.class));
```
- And once you've opened up the Activity... try scrolling! You should see the ActionBar collapse while the FAB moves up and disappears.
This is an example of a collection of behaviors built into `CoordinatorLayout` and other classes in the Design Support library. To get a sense for how they work, open up the newly created `activity_scrolling.xml` layout resource and see how this layout was constructed!
- At the root of the layout we find the `CoordinatorLayout`, ready to "coordinate" all of its children and allow them to interact.
- The first child is an [`AppBarLayout`](https://developer.android.com/reference/android/support/design/widget/AppBarLayout.html). This layout specifically supports responding to scroll events produced from within the CoordinatorLayout (e.g., when the user scrolls through the text content). You can control the visibility of this element based on scrolling using the `app:layout_scrollFlags` attribute.
The AppBarLayout works together with its child [`CollapsingToolbarLayout`](http://developer.android.com/reference/android/support/design/widget/CollapsingToolbarLayout.html), which does just what it says on the tin. It shows a larger title, but then shrinks down in response to scrolling. Here the `scrollFlags` are declared: `scroll|exitUntilCollapse` indicates two flags (combined with a bitwise OR `|`): that the content should scroll, and that it should shrink to its minimum height until it collapses.
The Toolbar itself is finally defined as a child of `CollapsingToolbarLayout`, though other children could be added here as well. For example, an `ImageView` could be included as a child of the CollapsingToolbarLayout in order to create a collapsing image!
(Check the documentation for details about all of the specific attributes).
- After the collapsing AppBar, the `CoordinatorLayout` includes a [`NestedScrollView`](https://developer.android.com/reference/android/support/v4/widget/NestedScrollView.html) (declared in a separate file for organization). This is a scrollable view (similar to what is used in a ListView), but can both include and be included within scrollable layouts.
- Notice that this element includes an `app:layout_behavior` attribute, which refers to a particular class: `AppBarLayout$ScrollingViewBehavior` (the `$` is used to refer to a compiled nested class). This Behavior will "automatically scroll any AppBarLayout siblings", allowing the scrolling of the page content to _also_ cuase the AppBarLayout to scroll!
- Finally, we have the FAB for this screen. The biggest piece to note here is how the FAB includes the `app:layout_anchor` attribute assigned a reference to the AppBarLayout. This indicates that the FAB should follow (scroll with) the AppBarLayout; the `app:anchorGravity` property indicates where it should be relative to its anchor. Moreover, the FAB's default behavior will cause it to disappear when then is no room... and since the AppBarLayout exits on collapse, the FAB disappears as well!
In sum: the `NestedScrollingView` has a Behavior that will cause the AppBarLayout to scroll with it. The AppBarLayout has Behaviors that allow it to collapse, and the FAB is connected to that layout to moves up with it and eventually disappear.
#### Custom Behaviors {-}
We can also create our own custom behaviors if we want to change the way elements interact with the CoordinatorLayout. For example, we can create a Behavior so that the FAB on the `MainActivity` shrinks and disappears when the Snackbar is shown, rather than moving up out of the way!
First, we will create a new Java class to represent our `ShrinkBehavior`. This class will need to `extend CoordinatorLayout.Behavior<FloatingActionButton>` (because it is a `CoordinatorLayout.Behavior` and it will be applied to a `FloatingActionButton`).
- We will need also need to override the constructor so that we can declare/instantiate this class from the XML:
```java
public ShrinkBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
```
The next step is to make sure the Behavior is able to react to changes in the Snackbar. To do this we have to make the Behavior report that is has the Snackbar as a _dependency_. This way when the CoordinatorLayout is propagating events and changes to all of its children, it will know that it should also inform the FAB about changes to the Snackbar. We do this by overriding the `layoutDependsOn()` method:
```java
public boolean layoutDependsOn(CoordinatorLayout parent,
FloatingActionButton child, View dependency) {
//add SnackbarLayout to the dependency list (if any)
return dependency instanceof Snackbar.SnackbarLayout ||
super.layoutDependsOn(parent, child, dependency);
}
```
- (Technically the `super` class doesn't have any other dependencies, but it's still good practice to call up the tree).
Finally, we can specify what should happen when one of the dependency's Views change by overriding the `onDependentViewChange()` callback:
```java
public boolean onDependentViewChanged(CoordinatorLayout parent, FloatingActionButton child, View dependency) {
if(dependency instanceof Snackbar.SnackbarLayout){
//calculate how much Snackbar we see
float snackbarOffset = 0;
if(parent.doViewsOverlap(child, dependency)){
snackbarOffset = Math.min(snackbarOffset, dependency.getTranslationY() - dependency.getHeight());
}
float scaleFactor = 1 - (-snackbarOffset/dependency.getHeight());
child.setScaleX(scaleFactor);
child.setScaleY(scaleFactor);
return true;
}else {
return super.onDependentViewChanged(parent, child, dependency);
}
}
```
- This method will be passed a reference to the `CoordinatorLayout` that is managing the changes, the `FloatingActionButton` who is receiving the change, and _which dependency_ had it's View changed. We check that the dependency is actually a `Snackbar` (since we might have multiple dependencies and want to respond differently to each one), and then call some getters on that dependency to figure out how tall it is (and thus how much we should shrink by). Finally, we use setters to change the scaling of the `child` (the FAB), thereby having it scale!
- And because this scale is dependent on the Snackbar's height, the FAB will also "grow back" when the Snackbar goes away!
This is about the simplest form of Behavior we can have: more complex behaviors can be based on scroll or fling events, utilizing different state attributes for different dependencies!
<p class="alert alert-warning">Custom behaviors are very tricky to write and design; the overridden functions are not very well documented, and by definition these behaviors involve coordinating lots of different classes! Most custom behaviors are designed by reading the Android source code (e.g., for `FloatingActionButton.Behavior`) and modifying that as an example. My suggestion is to search online for behaviors similar to the one you're trying to achieve, and work from there.</p>
## Animations `r #time allowing....`
One of the key principles of Material Design was the use of _motion_: many of the previous examples have involved adding animated changes to elements (e.g., moving or scrolling). The Material theme available in API 21+ provides a number of different techniques for including [**animations**](https://developer.android.com/training/material/animations.html) in your application—in particular, using animation to give user feedback and provide connections between elements when the display changes. As a final example, this section will cover how to add simple [Activity Transitions](https://developer.android.com/training/material/animations.html#Transitions) so that Views "morph" from one Activity to the next.
In order to utilize Activity Transitions, you will need to enable them in your application's _theme_ by adding an addition `<item>` in the theme declaration (in `res/values/styles.xml`):
```xml
<!-- in style: enable window content transitions -->
<item name="android:windowActivityTransitions">true</item>
```
There are three different kinds of Activity Transitions we can specify:
1. _enter_ transitions, or how Views in an Activity enter the screen
2. _exit_ transitions, or how Views in an Activity leave the screen
3. _shared element_ transitions, or how Views that are shared between Activities change
We'll talk about the later, though the previous two follow a similar process.
In order to animate a **shared element** between two Activities, we need to give them matching identifiers so that the transition framework knows to animate them. We do this by giving each of the shared elements an `android:transitionName` attribute, making sure that they have the _same_ value.
- For example, we can give the FAB in each activity an `android:transitionName="fab"`.
- Because the FAB is _anchored_, we actually need to do some extra work (because FAB will morph to the "unanchored" point and then get moved once the anchorage is calculated). The easiest workaround for this is to wrap the anchored FAB inside an anchored `FrameLayout`—the FAB just then becomes a normal element with the FrameLayout handling the scrolling behavior.
```xml
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_anchor="@id/app_bar"
app:layout_anchorGravity="bottom|end"
android:elevation="12dp"
>
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:transitionName="same_fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/fab_margin"
app:srcCompat="@android:drawable/ic_dialog_email" />
</FrameLayout>
```
Finally, we need to make sure that the Intent used to start the Activity also starts up the transition animation. We do this be including an additional argument to the `startActivity()` method: an options `Bundle` containing details about the animation:
```java
//transition this single item
ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(MainActivity.this, button, "fab");
// start the new activity
startActivity(new Intent(MainActivity.this, ScrollingActivity.class), options.toBundle());
```
This should cause the FAB to "morph" between Activities (and even morph back when you hit the "back" button)!
For more about what makes effective animation, see the [Material deisgn guidelines](https://material.io/guidelines/motion/material-motion.html).
<p class="alert alert-warning">I find Activity Transitions to be slick, but finicky: getting them to work properly takes a lot of effort and practice. Most professional apps that utilize Material Design use extensive custom animations for generating these transitions. For examples of more complex Material Design animations and patterns, check out sample applications such as [cheesesquare](https://github.com/chrisbanes/cheesesquare) or [plaid](https://github.com/nickbutcher/plaid).</p>
<!-- Property Animation here??? -->
## Resources {-}
<div class="list-condensed">
- [Material Design Guidelines](https://material.io/guidelines/) check the whole document (via the hamburger menu on the left).
- [Material Design for Developers (Google)](https://developer.android.com/training/material/index.html) official documentation for implementing material design
- [Material Design Primer (CodePath)](https://guides.codepath.com/android/Material-Design-Primer) excellent compiled documentation and examples for implementing Material patterns (CodePath in general is an excellent resource).
- [Android Design Support Library (Google Blog)](https://android-developers.googleblog.com/2015/05/android-design-support-library.html) an introduction to the support library features
- [Mastering the Coordinator Layout (Blog)](http://saulmm.github.io/mastering-coordinator) a great set of examples of how to use CoordinatorLayout.
- [Using CoordinatorLayout in Android Apps (Blog)](http://www.androidauthority.com/using-coordinatorlayout-android-apps-703720/) another good explanation of CoordinatorLayout
- https://lab.getbase.com/introduction-to-coordinator-layout-on-android/
- https://medium.com/@andkulikov/animate-all-the-things-transitions-in-android-914af5477d50
</div>