# Lab - Material Design This week we will be playing with android's beautiful new Material Design theme. We will be covering the following material widgets and animations that were introduced in Android 5.0 (API level 21). * [RecyclerView](http://guides.codepath.org/android/Using-the-RecyclerView) (creat efficient lists of scrollable items) * [CardView](http://guides.codepath.org/android/Using-the-CardView) (custom outline and shadow) * [Snackbar](http://guides.codepath.org/android/Displaying-the-Snackbar) (display toast like text with an optional single action) * [CoordinatorLayout](http://guides.codepath.org/android/Handling-Scrolls-with-CoordinatorLayout) (handling scroll effects) * [Ripple Animation](http://guides.codepath.org/android/ripple-animation) (touch feedback) * [Palette](http://guides.codepath.org/android/Dynamic-Color-using-Palettes) (incorporate dynamic color) * [Shared Element Activity Transition](http://guides.codepath.org/android/Shared-Element-Activity-Transition) * [Floating Action Button](http://guides.codepath.org/android/Floating-Action-Buttons) * [Circular Reveal Animation](http://guides.codepath.org/android/Circular-Reveal-Animation) Review our [Material Design Primer](http://guides.codepath.org/android/Material-Design-Primer) guides for a more detailed overview of all aspects of a material designed app. ### Prerequisites Make sure you are working with a device or emulator running on API 21 or above. While support libraries are available for material views and widgets, none of the animations would work on API versions lower than 21. Android introduced a dedicated [render thread](https://www.youtube.com/watch?v=3TtVsy98ces&t=554) with API 21 for many of the Material Design animations, so they don't hold up the primary UI thread. At this moment it is not possible to work with Material Design animations on API versions lower than 21 due to the absence of this render thread. ### Objectives: ![imgur|300](https://courses.codepath.com/course_images/android_university_fast_track/lollipop_circular_reveal.gif) 1. Import base app * The app loads a list of contacts into a `RecyclerView` where each list item is represented by a `CardView`. You will be building new features on top of this app. * Clone the [android-lollipop-exercise](https://github.com/codepath/android-lollipop-exercise/) repository to your machine, this is the starter project. Import the project in Android Studio by choosing the project's root-level `build.gradle`: ![Import|600](http://i.imgur.com/joPKoTk.gif) 2. Test your app * Test to verify that the starter project runs without any errors. * If the app loads successfully, you should see the following output: ![Screenie|300](https://courses.codepath.com/course_images/android_university_fast_track/lollipop_contact_list.gif) 3. Understand existing code * The app uses two new widgets that were introduced in API 21: `RecyclerView` and `CardView`. * Make sure you understand these concepts before moving forward. * Refer to [Using the RecyclerView](http://guides.codepath.org/android/Using-the-RecyclerView) guide for more information on `RecyclerView`. * Refer to [Using the CardView](http://guides.codepath.org/android/Using-the-CardView) guide for detailed guide on using a `CardView`. 4. Add Action Item to ActionBar * Add an "Add" `menuItem` to the ActionBar of `ContactsActivity` in `menu > menu_contacts.xml` file to add items to the contacts list. Refer [Adding Action Items](http://guides.codepath.org/android/Defining-The-ActionBar#adding-action-items) guide. * <b>Tip</b>: Use `@android:drawable/ic_input_add` value for the `android:icon` attribute and add `app:showAsAction="always"` for the icon to appear. 5. Test your app * If you used `@android:drawable/ic_input_add` as the icon for your menu item, you should see the following: ![Screenie|300](https://courses.codepath.com/course_images/android_university_fast_track/lollipop_add_action_item.png) 6. Display Snackbar * `Snackbar` is just like a Toast message except that it has an option for user feedback and can be shown indefinitely. You can call it as a toast message with an action button. Usually this type of widget is used in a situation where user needs an option to reverse or undo his actions. * In this example, we'll use the action item to add a new contact to the list and provide an option to undo that action using `Snackbar`. For more details, refer [Using Snackbar](http://guides.codepath.org/android/Displaying-the-Snackbar) guide. * Implement `onClick` for the action item you added in step 5. Refer [Handling ActionBar Clicks](http://guides.codepath.org/android/Defining-The-ActionBar#handling-actionbar-clicks) guide. * You should have static `Contact.getRandomContact()` method in the `Contact` class. As the name suggests, this method returns a reference to a random contact object. * On click of the "Add" action item, add a random contact at the start of the list. Refer [notifying the adapter](http://guides.codepath.org/android/Using-the-RecyclerView#notifying-the-adapter) guide. * [Display a Snackbar](http://guides.codepath.org/android/Displaying-the-Snackbar) for a fixed time. For this example, we'll provide an option to undo the add action by clicking on the custom `UNDO` action button of the `Snackbar`. * <b>Tip</b>: You need to pass in a container view to the `Snackbar`, so pass in a reference to the RecyclerView. * <b>Tip</b>: Use `.setActionTextColor(ContextCompat.getColor(ContactsActivity.this, R.color.accent))` to change the accent color of action text. * <b>Tip</b>: RecyclerView will not change the position as you add elements. If you want the list to scroll to the position after a new contact is added, see this [section](http://guides.codepath.org/android/Using-the-RecyclerView#scrolling-to-new-items) of the adapter guide. * You should be able to undo the action of adding a contact to the contacts list by clicking on the `UNDO` action button. 7. Test your app * You should be able to add items to the list and also see a `Snackbar` with an option to undo that action when items are added to the list. ![Screenie|300](https://courses.codepath.com/course_images/android_university_fast_track/lollipop_show_snackbar.gif) 8. Dismiss Snackbar by swiping * You must have noticed that Snackbar appears on the screen from the bottom. It is possible to dismiss this Snackbar by swiping if a [CoordinatorLayout](http://guides.codepath.org/android/Handling-Scrolls-with-CoordinatorLayout) is used as its parent. * The main function of a `CoordinatorLayout` is to act as a container and perform specific interactions between child views. The most common use case of a `CoordinatorLayout` is to shift up a floating action button when a `Snackbar` appears. We'll look at [floating action button](http://guides.codepath.org/android/Floating-Action-Buttons) in detail later in this exercise. * Go to `activity_contacts.xml` and replace the container layout (`RelativeLayout`) with a [CoordinatorLayout](http://guides.codepath.org/android/Handling-Scrolls-with-CoordinatorLayout). Make sure to specify the fully qualified class name such as `androidx.coordinatorlayout.widget.CoordinatorLayout` as the XML element name for `CoordinatorLayout` to find it. * Thanks to the `CoordinatorLayout`, your `Snackbar` will have the ability to be dismissed by swiping it to the right. 9. Test your app * If you were able to successfully have a `CoordinatorLayout` as the parent to your `Snackbar`, you should be able to dismiss your `Snackbar` by holding the mouse down over it and swiping quickly from left to right as shown below: ![Screenie|300](https://courses.codepath.com/course_images/android_university_fast_track/lollipop_snackbar_coordinatorlayout.gif) 10. Connect Detail View * You will find `DetailsActivity.java` and it's layout file `activity_details.xml` in your project to display more information about the contact selected from the list. * This activity is used to show contact's profile picture, name and phone number. * The item click listener for the contacts list can be found inside the static internal class `ContactViewHolder` of `ContactsAdapter.java`. * Fire an intent when the user selects a contact from the list. * Pass the contact through the intent and populate the detail view. Make sure to use `DetailsActivity.EXTRA_CONTACT` as the key while passing the contact object around since that key is being used to extract contact object in `DetailsActivity`. * Refer to the [Using Intents to Create Flows](http://guides.codepath.org/android/Using-Intents-to-Create-Flows) guide for how to pass data between activities. 11. Test your app * If you run your app and click on a contact from the list, you should be able to view the detail view. ![Screenie|300](https://courses.codepath.com/course_images/android_university_fast_track/lollipop_contact_details.gif) 12. Add Ripple Animation * Add touch feedback animation, also called as 'ripple effect' to the `CardView` to provide visual effect to the user when an item in the list is touched. * Refer to the [Adding Ripple Effect](http://guides.codepath.org/android/Using-the-CardView#adding-ripple-effect) guide. * Read more about touch animations in [Ripple Animations](http://guides.codepath.org/android/ripple-animation) guide. 13. Test your app * You should see the following output if you run your app now. Notice the ripple effect when you click on a list item. ![Imgur|300](https://courses.codepath.com/course_images/android_university_fast_track/lollipop_ripples.gif) 14. Use Palette * Use the new `Palette` API to extract the most vibrant color from the contact's profile image . * Use this color to set the background color of the `R.id.vPalette` view containing contact's name in `ContactsActivity` and `DetailsActivity`. * The `Palette` API requires a bitmap of the image you want to extract the colors from as a parameter. You can use `Glide` to get a callback with a bitmap, which can then be used to extract the most vibrant color from the `Palette`. * In `ContactsAdapter.java` and `DetailsActivity.java`, you'll need to change the way the profile image is being loaded into the `ImageView` as follows: ```java // Use Glide to get a callback with a Bitmap which can then // be used to extract a vibrant color from the Palette. // Define an asynchronous listener for image loading CustomTarget<Bitmap> target = new CustomTarget<Bitmap>() { @Override public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) { // TODO 1. Instruct Glide to load the bitmap into the `holder.ivProfile` profile image view // TODO 2. Use generate() method from the Palette API to get the vibrant color from the bitmap // Set the result as the background color for `holder.vPalette` view containing the contact's name. } @Override public void onLoadCleared(@Nullable Drawable placeholder) { // can leave empty } }; // TODO: Clear the bitmap and the background color in adapter // Instruct Glide to load the bitmap into the asynchronous target defined above Glide.with(mContext).asBitmap().load(contact.getThumbnailDrawable()).centerCrop().into(target); ``` * See the [Dynamic Color using Palette](http://guides.codepath.org/android/Dynamic-Color-using-Palettes#asynchronous-methods) guide for information on using the `Palette` API. 15. Test your app * If you were able to successfully extract the vibrant color from the profile image using the `Palette` API to set the background color of your views, you should see the following output: ![imgur|300](https://courses.codepath.com/course_images/android_university_fast_track/lollipop_palette.gif) 16. Implement Shared Element Activity Transition * A shared element transition determines how shared element views—also called hero views are animated from one activity to another during a scene transition. Shared element transitions are governed by changes to each shared element view's position, size, and appearance. * Add shared element activity transition to provide a seamless experience by emphasizing continuity between activity transitions. * `ContactsActivity` and `DetailsActivity` share three views: `ivProfile`, `vPalette` and `tvName`. * Animate these three views while transitioning from `ContactsActivity` to `DetailsActivity` and vice versa. * Refer to the [Shared Element Activity Transition](http://guides.codepath.org/android/Shared-Element-Activity-Transition) guide for information on implementing hero transitions. * <b>Tip</b>: The first parameter of `makeSceneTransitionAnimation()` expects an Activity, so make sure to type cast your context to an activity or use the `mContext` variable. * Note that the reverse animation automatically takes place after implementing the Shared Element Activity Transition if you hit the back button. However, if you hit the back arrow from the ActionBar, the reverse animation does not occur. To solve this, you'll need to listen for the up-navigation button click in `DetailsActivity` and call `finishAfterTransition()` directly: ```java @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: finishAfterTransition(); return true; } return super.onOptionsItemSelected(item); } ``` 17. Test your app * Your activity transition should look like following: ![imgur|300](https://courses.codepath.com/course_images/android_university_fast_track/lollipop_shared_element.gif) 18. Add Floating Action Button * Floating action buttons are used for a special type of promoted action. They are distinguished by a circled icon floating above the UI. * The floating action button should be placed 16dp min from the edge on mobile. * Add a [floating action button](http://guides.codepath.org/android/Floating-Action-Buttons) to `DetailsActivity` to dial a phone call to the selected contact. * Add the `FloatingActionButton` view to your `activity_details.xml` layout file as follows: ```xml <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> ... <FrameLayout xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_below="@+id/ivProfile" android:layout_marginTop="-50dp"> <com.google.android.material.floatingactionbutton.FloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="16dp" android:src="@drawable/ic_call" app:fabSize="normal" /> </FrameLayout> </RelativeLayout> ``` ** Note: Add the FrameLayout right below the root level RelativeLayout, not the inner RelativeLayout ** * Extract the floating action button in your `DetailsActivity.java` file as follows: ```java private FloatingActionButton fab; @Override protected void onCreate(Bundle savedInstanceState) { // ... fab = findViewById(R.id.fab); // ... } ``` 19. Test your app * Verify that you see a green floating action button when you run your app. ![imgur|300](https://courses.codepath.com/course_images/android_university_fast_track/lollipop_fab.gif) 20. Setup Click Listener * Setup a click listener on the fab button within your activity and dial the person's number on click of the button using an implicit intent. ```java // ... private FloatingActionButton fab; // ... @Override protected void onCreate(Bundle savedInstanceState) { // ... // Extract FAB fab = findViewById(R.id.fab); // Dial contact's number. // This shows the dialer with the number, allowing you to explicitly initiate the call. fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String uri = "tel:" + mContact.getNumber(); Intent intent = new Intent(Intent.ACTION_DIAL); intent.setData(Uri.parse(uri)); startActivity(intent); } }); // ... } ``` 21. Test your app * Your app, with a floating action button should look like this: ![imgur|300](https://courses.codepath.com/course_images/android_university_fast_track/lollipop_fab_call.gif) 22. Add Circular Reveal Animation * Add circular reveal animation effect on the floating action button so that the button is revealed while entering the activity. Reverse this transition while exiting the activity. * When the activity starts, you would be revealing a previously invisible view. Hide your button in the `onCreate()` method before starting the animation to make the animation look right. ```java fab.setVisibility(View.INVISIBLE); ``` * Follow [Circular Reveal Animation](http://guides.codepath.org/android/Circular-Reveal-Animation) guide to add `enterReveal()` and `exitReveal()` transitions on the floating action button. * Make sure to enable exit reveal transition on press of `ActionBar` up button or back button, call `exitReveal()` from `onOptionsItemSelected()` and `onBackPressed()` as mentioned in the guide. * Also make sure to remove `finishAfterTransition()` on press of `ActionBar` up button or back button since that step is already taken care of inside `exitReveal()`. 23. Test your app * Heres the final app with all the material widgets and animations you implemented during this exercise. ![imgur|300](https://courses.codepath.com/course_images/android_university_fast_track/lollipop_circular_reveal.gif)