Kotlin Android Fundamentals 10.2: Material Design, dimens, and colors

This codelab is part of the Android Kotlin Fundamentals course. You'll get the most value out of this course if you work through the codelabs in sequence. All the course codelabs are listed on the Android Kotlin Fundamentals codelabs landing page.

Introduction to Material Design

Material Design is a cross-platform design system from Google, and is the design system for Android. Material Design provides detailed specs for everything in an app's user interface (UI), from how text should be shown, to how to lay out a screen. The Material Design website at material.io has the complete specifications.

On material.io you can also find a list of additional views that ship with Material Design components. The views include bottom navigation, a floating action button (FAB) that you use in this codelab, chips that you explore in the next codelab, and collapsing toolbars.

In addition to these views that you can use to implement Material Design, the Material Design components library exports the MaterialComponents theme that your app already uses. The MaterialComponents theme implements Material Design for controls, uses theme attributes, and is customizable.

To see examples of apps that use Material Design, open Gmail, or check out the Google Design website, in particular the annual Material Design awards.

What you should already know

  • How to create apps with activities and fragments, and navigate between fragments passing data
  • Using views and view groups to lay out a UI, in particular RecyclerView
  • How to use Architecture Components, including ViewModel, with the recommended architecture to create a well-structured and efficient app
  • Data binding, coroutines, and how to handle click events
  • How to connect to the internet and cache data locally using a Room database
  • How to set view attributes, and how to extract resources into and use resources from XML resource files
  • How to use styles and themes to customize the look of your app

What you'll learn

  • How to apply Material Design principles to the UI of your app
  • How to use Material components to make your app beautiful
  • How to extract and use dimensions
  • How to create and use custom and Material color schemes for your app's UI

What you'll do

  • Improve the UI of a given app using Material components, dimensions, and color.

The GDG-finder starter app builds on everything you've learned so far in this course.

The app uses ConstraintLayout to lay out three screens. Two of the screens are just layout files that you'll use to explore colors and text on Android.

The third screen is a GDG finder. GDGs, or Google Developer Groups, are communities of developers that focus on Google technologies, including Android. GDGs around the world host meetups, conferences, study jams, and other events.

As you develop this app, you work on the real list of GDGs. The finder screen uses the device's location to sort the GDGs by distance.

If you're lucky and there's a GDG in your region, you can check out the website and sign up for their events! GDG events are a great way to meet other Android developers and learn industry best practices that didn't fit in this course.

The screenshots below show how your app will change from the beginning to the end of this codelab.

In this task, you add a floating action button (FAB) to the home screen of the GDG Finder app.

A FAB is a large round button that represents a primary action, which is the main thing a user should do on the screen. The FAB floats above all other content, as shown in the screenshot on the left below. When the user taps the FAB, they are taken to a list of GDGs, as shown on the right.

Step 1: Add a FAB to the home fragment layout

  1. Download and run the GDGFinderStyles app, which is the starter app for this codelab. If you did the preceding codelab, you can continue from the final code of that codelab.
  2. In build.gradle(Module: app), verify that the material library is included. To use Material Design components, you need to include this library.
implementation 'com.google.android.material:material:1.1.0-alpha04'
  1. Open res/layout/home_fragment.xml and switch to the Text tab.

    Currently, the home screen layout uses a single ScrollView with a ConstraintLayout as a child. If you added a FAB to the ConstraintLayout, the FAB would be inside the ConstraintLayout, not floating over all the content, and it would scroll with the rest of the content of the ConstraintLayout. You need a way to float the FAB above your current layout.

    CoordinatorLayout is a view group that lets you stack views on top of each other. While CoordinatorLayout doesn't have any fancy layout abilities, it is sufficient for this app. The ScrollView should take up the full screen, and the FAB should float near the bottom edge of the screen.
  1. In home_fragment.xml, add a CoordinatorLayout around the ScrollView.
<androidx.coordinatorlayout.widget.CoordinatorLayout
       android:layout_height="match_parent"
       android:layout_width="match_parent">
...

</androidx.coordinatorlayout.widget.CoordinatorLayout>
  1. Replace <ScrollView> with <androidx.core.widget.NestedScrollView>. Coordinator layout knows about scrolling, and you need to use NestedScrollView inside another view with scrolling for scrolling to work correctly.
androidx.core.widget.NestedScrollView
  1. Inside and at the bottom of the CoordinatorLayout, below the NestedScrollView, add a FloatingActionButton.
<com.google.android.material.floatingactionbutton.FloatingActionButton
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"/>
  1. Run your app. You see a colored dot in the top-left corner.

  1. Tap the button. Notice that the button visually responds.
  2. Scroll the page. Notice that the button stays put.

Step 2: Style the FAB

In this step, you move the FAB to the bottom-right corner and add an image that indicates the FAB's action.

  1. Still in home_fragment.xml, add the layout_gravity attribute to the FAB and move the button to the bottom and end of the screen. The layout_gravity attribute tells a view to lay out on the top, bottom, start, end, or center of the screen. You can combine positions using a vertical bar.
 android:layout_gravity="bottom|end"
  1. Check in the Preview pane that the button has moved.
  2. Add a layout_margin of 16dp to the FAB to offset it from the edge of the screen.
android:layout_margin="16dp"
  1. Use the provided ic_gdg icon as the image for the FAB. After you add the code below, you see a chevron inside the FAB.
 app:srcCompat="@drawable/ic_gdg"
  1. Run the app, and it should look like the screenshot below.

Step 3: Add a click listener to the FAB

In this step, you add a click handler to the FAB that takes the user to a list of GDGs. You've added click handlers in previous codelabs, so the instructions are terse.

  1. In home_fragment.xml, in the <data> tag, define a variable viewModel for the provided HomeViewModel.
<variable
   name="viewModel"
   type="com.example.android.gdgfinder.home.HomeViewModel"/>
  1. Add the onClick listener to the FAB and call it onFabClicked().
android:onClick="@{() -> viewModel.onFabClicked()}"
  1. In the home package, open the provided HomeViewModel class and look at the navigation live data and functions, which are also shown below. Notice that when the FAB is clicked, the onFabClicked()click handler is called, and the app triggers navigation.
private val _navigateToSearch = MutableLiveData<Boolean>()
val navigateToSearch: LiveData<Boolean>
   get() = _navigateToSearch

fun onFabClicked() {
   _navigateToSearch.value = true
}

fun onNavigatedToSearch() {
   _navigateToSearch.value = false
}
  1. In the home package, open the HomeFragment class. Notice that onCreateView() creates the HomeViewModel and assigns it to viewModel.
  2. Add the viewModel to the binding in onCreateView().
binding.viewModel = viewModel
  1. To eliminate the error, clean and rebuild your project to update the binding object.
  2. Also in onCreateView(), add an observer that navigates to the list of GDGs. Here is the code:
        viewModel.navigateToSearch.observe(viewLifecycleOwner,
            Observer<Boolean> { navigate ->
                if(navigate) {
                    val navController = findNavController()
                    navController.navigate(R.id.action_homeFragment_to_gdgListFragment)
                    viewModel.onNavigatedToSearch()
               }
             })
  1. Make the necessary imports from androidx. Import this findNavController and Observer:
import androidx.navigation.fragment.findNavController
import androidx.lifecycle.Observer
  1. Run your app.
  2. Tap the FAB and it takes you to the GDG list. If you are running the app on a physical device, you are asked for the location permission. If you are running the app in an emulator, you may see a blank page with the following message:


    If you get this message running on the emulator, make sure you are connected to the internet and have location settings turned on. Then open the Maps app to enable location services. You may also have to restart your emulator.

To get the most out of Material Design components, use theme attributes. Theme attributes are variables that point to different types of styling information, such as the primary color of the app. By specifying theme attributes for the MaterialComponents theme, you can simplify your app styling. Values you set for colors or fonts apply to all widgets, so you can have consistent design and branding.

Step 1: Use Material theme attributes

In this step, you change the styling of the title headers on the home screen to use Material Design theme attributes to style your views. This helps you follow the Material style guide while customizing your app's style.

  1. Open the Material web page for typography theming:
    https://material.io/develop/android/theming/typography/
    The page shows you all the styles available with Material themes.
  2. On the page, search or scroll to find textAppearanceHeadline5 (Regular 24sp) and textAppearanceHeadline6 (Regular 20sp). These two attributes are good matches for your app.
  3. In home_fragment.xml, replace the current style (android:textAppearance="@style/TextAppearance.Title") of the title TextView with style="?attr/textAppearanceHeadline5". The syntax ?attr is a way to look up a theme attribute and apply the value of Headline 5, as defined in the current theme.
<TextView
       android:id="@+id/title"
       style="?attr/textAppearanceHeadline5"
  1. Preview your changes. Notice that when the style is applied, the font of the title changes. This happens because the style set in the view overrides the style set by the theme, as shown by the style-priority pyramid diagram below.

In the pyramid diagram, TextAppearance is below theme. TextAppearance is an attribute on any view that applies text styling. It's not the same as a style, and it only lets you define things about how to display text. All the text styles in Material Design components can also be used as textAppearance; that way, any theme attributes that you define take priority.

  1. In the title TextView, replace the styling you just added with a textAppearance.
  1. Preview your changes, or run the app to see the difference. For comparison, the screenshots below show the differences when the Material style is applied to the title and overrides the Title style.

MATERIAL STYLE:

style="?attr/textAppearanceHeadline5"

TEXT APPEARANCE

android:textAppearance="?attr/textAppearanceHeadline5"

Step 2: Change the style in the Material theme

The textAppearanceHeadline6 would be a good Material choice for the subtitle, but its default size is 20sp, not 18sp as defined in the Title style of the app. Instead of overriding the size in each subtitle view, you can modify the Material theme and override its default style.

  1. Open styles.xml.
  2. Delete the Title and Subtitle styles. You are already using textAppearanceHeadline5 instead of Title, and you are going to remove the need for Subtitle.
  3. Define a CustomHeadline6 style with a textSize of 18sp. Give it a parent of TextAppearance.MaterialComponents.Headline6 so that it inherits everything that you are not explicitly overriding.
<style name="TextAppearance.CustomHeadline6" parent="TextAppearance.MaterialComponents.Headline6">
   <item name="android:textSize">18sp</item>
</style>
  1. Inside this style, override the default textAppearanceHeadline6 of the theme, by defining an item that styles textAppearanceHeadline6 with the custom style that you just added.
<item name="textAppearanceHeadline6">@style/TextAppearance.CustomHeadline6</item>
  1. In home_fragment.xml, apply textAppearanceHeadline6 to the subtitle view and reformat your code (Code > Reformat code).
                  android:textAppearance="?attr/textAppearanceHeadline6"
  1. Run the app. Notice the difference in font color, which is subtle, but makes a big difference in how readable the screen is.

ORIGINAL:

AFTER:

Sometimes you may want to change portions of your screen to a different theme, but not all of it. For example, you could make the toolbar use the dark Material components theme. You do this using theme overlays.

A Theme is used to set the global theme for the entire app. A ThemeOverlay is used to override (or "overlay") that theme for specific views, especially the toolbar.

Theme overlays are "thin themes" that are designed to overlay an existing theme, like icing on top of a cake. They are useful when you want to change a subsection of your app, for example, change the toolbar to be dark, but continue using a light theme for the rest of the screen. You apply a theme overlay to a view, and the overlay applies to that view and all its children.

You do this by applying the desired theme to the root view of the view hierarchy for which you want to use it. This does not change anything yet! When you want a view in the hierarchy to use a particular attribute that's defined in the overlay theme, you specifically set the attribute on the view and set the value to ?attr/valuename.

Step: Use theme overlays

The MaterialComponents theme does not have an option for a dark toolbar on a light screen. In this step, you change the theme for just the toolbar. You make the toolbar dark by applying the Dark theme, which is available from MaterialComponents, to the toolbar as an overlay.

  1. Open activity_main.xml and find where the Toolbar is defined (androidx.appcompat.widget.Toolbar). The Toolbar is part of Material Design and allows more customization than the app bar that activities use by default.
  2. To change the toolbar and any of its children to the dark theme, start by setting the theme of the Toolbar to the Dark.ActionBar theme. Make sure you do this in the Toolbar, not in the ImageView.
<androidx.appcompat.widget.Toolbar
    android:theme="@style/ThemeOverlay.MaterialComponents.Dark.ActionBar"
  1. Change the background of the Toolbar to colorPrimaryDark.
android:background="?attr/colorPrimaryDark"

Notice the image in the header (drawable/logo.png), which includes both the colored arrow and the gray "GDG Finder" text on a transparent background. With the changed background color, the text does not stand out as much anymore. You can create a new image, or to increase contrast without creating a new image, you can set a tint on the ImageView. That causes the whole ImageView to be "tinted" to the specified color. The colorOnPrimary attribute is a color that meets accessibility guidelines for text or iconography when drawn on top of the primary color.

  1. In the ImageView inside the Toolbar, set the tint to colorOnPrimary. Because the drawable includes both the image and the GDG Finder text, both will be light.
android:tint="?attr/colorOnPrimary"
  1. Run the app and notice the dark header from the theme. The tint is responsible for the light logo image, which includes the icon and the "GDG Finder" text.

A professional-looking app has a consistent look and feel. It has the same colors and similar layouts on every screen. This makes the app not only look better, but makes it easier for users to understand and interact with the screens.

Dimens, or dimensions, allow you to specify reusable measurements for your app. Specify things like margins, heights, or padding using dp. Specify font sizes using sp.

In this task, you define a dimen that will be used to apply a consistent margin on the right and left sides of the screen.

Step 1: Examine your code

  1. Open home_fragment xml.
  2. Look at the Design tab and make sure the blueprint is visible.
  3. In the Component Tree, select the start_guideline and end_guideline. They should be slightly highlighted in the blueprint.

  1. Notice the number 16 on the left and the number 26 on the right, indicating the inset of the start and end guidelines, respectively.
  2. Switch to the Text tab.
  3. Notice that at the bottom of the ConstraintLayout, two Guidelines are defined. Guidelines define vertical or horizontal lines on your screen that define the edges of your content. Everything is placed inside these lines except for full-screen images.
<androidx.constraintlayout.widget.Guideline
   android:id="@+id/start_guideline"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:orientation="vertical"
   app:layout_constraintGuide_begin="16dp" />

<androidx.constraintlayout.widget.Guideline
   android:id="@+id/end_guideline"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:orientation="vertical"
   app:layout_constraintGuide_end="26dp" />

The layout_constraintGuide_begin="16dp" is correct according to the Material specifications. But app:layout_constraintGuide_end="26dp" should be 16dp also. You could just fix this here manually. However, it is better to create a dimension for these margins and then apply them consistently throughout your app.

Step 2: Create a dimension

  1. In home_fragment xml, using the Text view, put your cursor on the 16dp of app:layout_constraintGuide_begin="16dp".
  2. Open the intentions menu and select Extract dimension resource.

  1. Set the Resource Name of the dimension to spacing_normal. Leave everything else as given and click OK.
  2. Fix layout_constraintGuide_end to also use the spacing_normal dimension.
<androidx.constraintlayout.widget.Guideline
       android:id="@+id/end_grid"
       app:layout_constraintGuide_end="@dimen/spacing_normal"
  1. In Android Studio, open Replace All (Cmd+Shift+R on the Mac or Ctrl+Shift+R on Windows).
  2. Search for 16dp and replace all occurrences, except the one in dimens.xml, with @dimen/spacing_normal.

  1. Open res/values/dimens.xml and notice the new spacing_normal dimension. Make sure you didn't accidentally replace the 16dp with a self-reference!
  2. Run your app.
  3. Notice that the spacing on the left and right of the text is now the same. Also, notice that the spacing affects line breaks, and the text in the right screenshot is one line shorter.

BEFORE:

AFTER:

By using color resources and Material theming, you can apply consistent colors throughout your app. Effective use of color can dramatically improve the usability of your app. Picking the best colors and color combinations can be challenging, but there are tools to help.

One of the available tools is the Color Tool. To get a full Material color scheme for your app from the tool, you choose two main base colors, and then the tool creates the remaining colors appropriately.

In this task, you create a Material color scheme and apply it to your app.

Step 1: Create a Material color scheme

  1. Open https://material.io/tools/color/. You can use this tool to explore color combinations for your UI.

  1. Scroll down in the MATERIAL PALETTE on the right to see more colors.
  2. Click Primary, then click a color. You can use any color you like.
  3. Click Secondary and click a color.
  4. Click Text to choose a text color if you want a color that is different from the one the tool has calculated for you. Pick various text colors to explore contrast.
  5. Click the ACCESSIBILITY tab. You will see a report like the one below. This gives you a report about how readable the currently selected color choices are.

  1. Look for the triangular exclamation mark icons.

  1. In the tool, switch to the CUSTOM tab and enter the following two colors.
  • Primary: #669df6
  • Secondary: #a142f4

The primary color is a blue that's based on the color used in the GDG logo. The secondary color is based on the balloons in the image on the home screen. Type the colors into the Hex Color field. Keep the black and white fonts suggested by the tool.

Note that this color scheme still has some accessibility warnings. Most color schemes do. You work around this in the next step.

  1. On the top-right of the window, select EXPORT and ANDROID. The tool initiates a download.
  2. Save the colors.xml file in a convenient location.

Step 2: Apply the Material color scheme to your app

  1. Open the downloaded colors.xml file in a text editor.
  2. In Android Studio, open values/colors.xml.
  3. Replace the resources of values/colors.xml with the contents of the downloaded colors.xml file.
  4. Open styles.xml.
  5. Inside the AppTheme, delete the colors that show as errors: colorPrimary, colorPrimaryDark, and colorAccent.
  6. Inside AppTheme, define 6 attributes with these new colors, as shown in the code below. You can find the list in the Color Theming guide.
<item name="colorPrimary">@color/primaryColor</item>
<item name="colorPrimaryDark">@color/primaryDarkColor</item>
<item name="colorPrimaryVariant">@color/primaryLightColor</item>
<item name="colorOnPrimary">@color/primaryTextColor</item>
<item name="colorSecondary">@color/secondaryColor</item>
<item name="colorSecondaryVariant">@color/secondaryDarkColor</item>
<item name="colorOnSecondary">@color/secondaryTextColor</item>
  1. Run the app. This looks pretty good...

  1. However, notice that colorOnPrimary isn't light enough for the logo tint (including the "GDG Finder text") to stand out sufficiently when displayed on top of colorPrimaryDark.
  2. In activity_main.xml, find the Toolbar. In the ImageView, change the logo tint to colorOnSecondary.
  3. Run the app.

Android Studio project: GDGFinderMaterial.

Floating action buttons

  • The floating action button (FAB) floats above all other views. It is usually associated with a primary action the user can take on the screen. You add a click listener to a FAB in the same way as to any other UI element.
  • To add a FAB to your layout, use a CoordinatorLayout as the top-level view group, then add the FAB to it.
  • To scroll content inside a CoordinatorLayout, use the androidx.core.widget.NestedScrollView.

Material Design

  • Material Design provides themes and styles that make your app beautiful and easier to use. Material Design provides detailed specs for everything, from how text should be shown, to how to lay out a screen. See material.io for the complete specifications.
  • To use Material components, you need to include the Material library in your gradle file.
implementation 'com.google.android.material:material:1.1.0-alpha04'
  • Use ?attr to apply material theme attributes to a view: style="?attr/textAppearanceHeadline5"

Themes and styles

  • Use styles to override theme attributes.
  • Use theme overlays to apply a theme to just a view and its children. Apply the theme to the parent view. For example:
android:theme="@style/ThemeOverlay.MaterialComponents.Dark.ActionBar"

Then use the ?attr notation to apply attributes to a view. For example:

android:background="?attr/colorPrimaryDark"

Colors

  • The Color Tool helps you create a Material palette for your app and download the palette as a color.xml file.
  • Setting a tint on the ImageView causes the ImageView to be "tinted" to the specified color. For example, you might use android:tint="?attr/colorOnPrimary". The colorOnPrimary color is designed to look good on colorPrimary.

Dimensions

  • Use dimension for measurements that apply to your whole app, such as margins, guidelines, or spacing between elements.

Udacity course:

Android developer documentation:

Other resources:

This section lists possible homework assignments for students who are working through this codelab as part of a course led by an instructor. It's up to the instructor to do the following:

  • Assign homework if required.
  • Communicate to students how to submit homework assignments.
  • Grade the homework assignments.

Instructors can use these suggestions as little or as much as they want, and should feel free to assign any other homework they feel is appropriate.

If you're working through this codelab on your own, feel free to use these homework assignments to test your knowledge.

Answer these questions

Question 1

Which of the following is true about the floating action button (FAB)?

▢ The FAB is usually associated with a primary action the user can take on the screen.

▢ The FAB must be positioned in the bottom-right corner, 16 dp from the edge of the screen.

▢ The FAB uses a special click handler so that you don't have to write your own view model code.

▢ The FAB is a mandatory element for apps that implement Material Design principles.

Question 2

Which ViewGroup allows you to stack views on top of each other?

CoordinatorLayout

StackedViewsLayout

ConstraintLayout

▢ You cannot stack views

Question 3

Which of the following are reasons for using Material Design components? Select all that apply.

▢ They are designed to be beautiful, functional, and work together.

▢ They help you create an app that uses consistent styling.

▢ They help you make your app accessible for vision impaired users.

▢ Android Studio will give you a warning if you are using a poor color scheme.

Question 4

What is a theme overlay for? Select all that are true.

▢ Allows you to apply attributes from a different theme to a view and all its children.

▢ With a theme overlay, all the styles of that theme are applied automatically.

▢ You create a theme overlay by defining styles in XML.

▢ When you apply a theme to a view, you can use that theme's attributes for the view and all its children.

Question 5

Which of the following are reasons for defining and using dimensions? Select all that apply.

▢ Makes it easier to apply the same measurements across the app.

▢ Allows you to give meaningful semantic names to measurements.

▢ Makes it easier to change measurements across your app.

▢ Dimensions are required for using Material Design.

Start the next lesson: 10.3: Design for everyone

For links to other codelabs in this course, see the Android Kotlin Fundamentals codelabs landing page.