This practical codelab is part of Unit 1: Get started in the Android Developer Fundamentals (Version 2) course. You will get the most value out of this course if you work through the codelabs in sequence:

Introduction

The Android SDK includes the Android Support Library, which is a collection of several libraries. These libraries provide features that aren't built into the Android framework, including the following:

What you should already know

You should be able to:

What you'll learn

What you'll do

In this practical you'll create an app called HelloCompat with one TextView that displays "Hello World" on the screen, and one Button that changes the color of the text. There are 20 possible colors, defined as resources in the color.xml file, and each button click randomly picks one of those colors.

The methods to get a color value from the app's resources have changed with different versions for the Android framework. This example uses the ContextCompat class in the Android Support Library, which allows you to use a method that works for all versions.

For this task you'll set up a new project for the HelloCompat app and implement the layout and basic behavior.

1.1 Verify that the Android Support Repository is available

The Android Support Library is downloaded as part of the Android SDK, and available in the Android SDK manager. In Android Studio, you'll use the Android Support Repository—the local repository for the support libraries—to get access to the libraries from within your Gradle build files. In this task you'll verify that the Android Support Repository is downloaded and available for your projects.

  1. In Android Studio, select Tools > Android > SDK Manager, or click the SDK Manager icon.

The Android SDK Default Preferences pane appears.

  1. Click the SDK Tools tab and expand Support Repository, as shown in the figure below.

  1. Look for Android Support Repository in the list.

If Installed appears in the Status column, you're all set. Click Cancel.

If Not installed or Update Available appears, click the checkbox next to Android Support Repository. A download icon should appear next to the checkbox. Click OK.

  1. Click OK again, and then Finish when the support repository has been installed.

1.2 Set up the project and examine build.gradle

  1. Create a new project called HelloCompat.

On the Target Android Devices page, API 15: Android 4.0.3 (IceCreamSandwich) is selected for the minimum SDK. As you've learned in previous lessons, this is the oldest version of the Android platform your app will support.

  1. Click Next, and choose the Empty Activity template.
  2. Click Next, and ensure that the Generate Layout file and Backwards Compatibility (App Compat) options are checked. The latter option ensures that your app will be backwards-compatible with previous versions of Android.
  3. Click Finish.

Explore build.gradle (Module:app)

  1. In Android Studio, make sure the Project > Android pane is open.
  2. Expand Gradle Scripts and open the build.gradle (Module: app) file.

Note that build.gradle for the overall project (build.gradle (Project: helpcompat)) is a different file from the build.gradle for the app module. In the build.gradle (Module: app) file.

  1. Locate the compileSdkVersion line near the top of the file. For example:
compileSdkVersion 26

The compile version is the Android framework version your app is compiled with in Android Studio. For new projects the compile version is the most recent set of framework APIs you have installed. This value affects only Android Studio itself and the warnings or errors you get in Android Studio if you use older or newer APIs.

  1. Locate the minSdkVersion line in the defaultConfig section a few lines down.
minSdkVersion 15

The minimum version is the oldest Android API version your app runs under. It's the same number you chose in Step 1 when you created your project. The Google Play store uses this number to make sure that your app can run on a given user's device. Android Studio also uses this number to warn you about using deprecated APIs.

  1. Locate the targetSdkVersion line in the defaultConfig section. For example:
targetSdkVersion 26

The target version indicates the API version your app is designed and tested for. If the API of the Android platform is higher than this number (that is, your app is running on a newer device), the platform may enable compatibility behaviors to make sure that your app continues to work the way it was designed to. For example, Android 6.0 (API 23) provides a new runtime permissions model. If your app targets a lower API level, the platform falls back to the older install-time permissions model.

Although the target SDK can be the same number as the compile SDK, it is often a lower number that indicates the most recent version of the API for which you have tested your app.

  1. Locate the dependencies section of build.gradle, near the end of the file. For example:
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:26.1.0'
    implementation 
           'com.android.support.constraint:constraint-layout:1.0.2'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.1'
    androidTestImplementation 
           'com.android.support.test.espresso:espresso-core:3.0.1'
}

The dependencies section for a new project includes several dependencies to enable testing with Espresso and JUnit, as well as the v7 appcompat support library. The version numbers for these libraries in your project may be different than those shown here.

The v7 appcompat support library provides backward-compatibility for older versions of Android all the way back to API 9. It includes the v4 compat library as well, so you don't need to add both as a dependency.

  1. Update the version numbers, if necessary.

If the current version number for a library is lower than the currently available library version number, Android Studio highlights the line and warns you that a new version is available ("A newer version of com.android.support:appcompat-v7 is available"). Edit the version number to the updated version.

  1. Update the compileSdkVersion number, if necessary.

The major version number of the support library (the first number) must match your compileSdkVersion. When you update the support library version, you may also need to update compileSdkVersion to match.

  1. Click Sync Now to sync your updated Gradle files with the project, if prompted.
  2. Install missing SDK platform files, if necessary.

If you update compileSdkVersion, you may need to install the SDK platform components to match. Click Install missing platform(s) and sync project to start this process.

For this task you'll implement the layout and basic behavior for the MainActivity class.

2.1 Change the layout and colors

In this task you will modify the activity_main.xml layout for the app.

  1. Open activity_main.xml in the Project > Android pane.
  2. Click the Design tab (if it is not already selected) to show the layout editor.
  3. Select the "Hello World" TextView in the layout and open the Attributes pane.
  4. Change the TextView attributes as follows:

Attribute field

Enter the following:

ID

hello_textview

textStyle

B (bold)

textAlignment

Center the paragraph icon

textSize

100sp

This adds the android:id attribute to the TextView with the id set to hello_textview, changes the text alignment, makes the text bold, and sets a larger text size of 100sp.

  1. Delete the constraint that stretches from the bottom of the hello_textview TextView to the bottom of the layout, so that the TextView snaps to the top of the layout, and choose 8 (8dp) for the top margin as shown below.

  1. Drag a Button to the bottom of the layout, and add constraints to the left and right sides and bottom of the layout, as shown in the figure below.

  1. Change the layout_width attribute in the Attributes pane for the Button to match_constraint.
  2. Change the other attributes in the Attributes pane for the Button as follows:

Attribute field

Enter the following:

ID

color_button

text

"Change Color"

The Button should now appear in the layout as shown below:

  1. In a previous lesson you learned how to extract a string resource from a literal text string. Click the Text tab to switch to XML code, and extract the "Hello Text!" and "Change Color" strings in the TextView and Button, and enter string resource names for them.
  2. Add the following android:onClick attribute to the Button:
android:onClick="changeColor"
  1. To add colors, expand res and values in the Project > Android pane, and open colors.xml.
  2. Add the following color resources to the file:
<color name="red">#F44336</color>
<color name="pink">#E91E63</color>
<color name="purple">#9C27B0</color>
<color name="deep_purple">#673AB7</color>
<color name="indigo">#3F51B5</color>
<color name="blue">#2196F3</color>
<color name="light_blue">#03A9F4</color>
<color name="cyan">#00BCD4</color>
<color name="teal">#009688</color>
<color name="green">#4CAF50</color>
<color name="light_green">#8BC34A</color>
<color name="lime">#CDDC39</color>
<color name="yellow">#FFEB3B</color>
<color name="amber">#FFC107</color>
<color name="orange">#FF9800</color>
<color name="deep_orange">#FF5722</color>
<color name="brown">#795548</color>
<color name="grey">#9E9E9E</color>
<color name="blue_grey">#607D8B</color>
<color name="black">#000000</color>

These color values and names come from the recommended color palettes for Android apps defined at Material Design - Style - Color. The codes indicate color RGB values in hexadecimal.

2.2 Add behavior to MainActivity

In this task you'll finish setting up the project by adding private variables and implementing onCreate() and onSaveInstanceState().

  1. Open MainActivity.
  2. Add a private variable at the top of the class to hold the TextView object.
private TextView mHelloTextView;
  1. Add the following color array just after the private variable:
private String[] mColorArray = {"red", "pink", "purple", "deep_purple",
          "indigo", "blue", "light_blue", "cyan", "teal", "green", 
          "light_green", "lime", "yellow", "amber", "orange", "deep_orange",
          "brown", "grey", "blue_grey", "black" };

Each color name corresponds to the name of a color resource in color.xml.

  1. In the onCreate() method, use findViewById() to get a reference to the TextView instance and assign it to that private variable:
mHelloTextView = findViewById(R.id.hello_textview);
  1. Also in onCreate(), restore the saved instance state, if any:
// restore saved instance state (the text color)
if (savedInstanceState != null) {
    mHelloTextView.setTextColor(savedInstanceState.getInt("color"));
}
  1. Add the onSaveInstanceState() method to MainActivity to save the text color:
@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    // save the current text color
    outState.putInt("color", mHelloTextView.getCurrentTextColor());
}

Task 2 solution code

The following is the solution code for the XML layout and a code snippet in the MainActivity class for the HelloCompat app so far.

XML layout

The XML layout for the activity_main.xml file is shown below. The changeColor click handler for the android:onClick attribute for the Button is underlined in red because it hasn't yet been defined. You define it in the next task.

<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.android.myapplication.MainActivity">

    <TextView
        android:id="@+id/hello_textview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="@string/hello_world"
        android:textAlignment="center"
        android:textSize="100sp"
        android:textStyle="bold"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/color_button"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginBottom="8dp"
        android:text="@string/change_color"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        android:onClick="changeColor"/>

</android.support.constraint.ConstraintLayout>

MainActivity

The MainActivity class includes the following private variables at the top of the class:

// Text view for Hello World.
private TextView mHelloTextView;
// array of color names, these match the color resources in color.xml
private String[] mColorArray = {"red", "pink", "purple", "deep_purple",
            "indigo", "blue", "light_blue", "cyan", "teal", "green",
            "light_green", "lime", "yellow", "amber", "orange", "deep_orange",
            "brown", "grey", "blue_grey", "black" };

The MainActivity class includes the following onCreate() and onSaveInstanceState() methods:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    mHelloTextView = findViewById(R.id.hello_textview);
    // restore saved instance state (the text color)
    if (savedInstanceState != null) {
        mHelloTextView.setTextColor(savedInstanceState.getInt("color"));
    }
}

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    // save the current text color
    outState.putInt("color", mHelloTextView.getCurrentTextColor());
}

The Change Color button in the HelloCompat app picks one of the 20 colors from the color.xml resource file at random and sets the color of the text to that color. In this task you'll implement the behavior for Button click handler.

2.1 Add the changeButton() click handler

  1. Open activity_main.xml, if it is not already open. Click the Text tab to show the XML code.
  2. Click on "changeColor" in the android:onClick attribute inside the Button element.
  3. Press Alt+Enter (Option+Enter on a Mac), and select Create onClick event handler.
  4. Choose MainActivity and click OK.

This creates a placeholder method stub for the changeColor() method in MainActivity:

public void changeColor(View view) {
}

2.2 Implement the Button action

  1. Switch to MainActivity.
  2. In the changeColor() method, create a random number object by using the Random class (a Java class) to generate simple random numbers.
Random random = new Random();
  1. Use the random instance to pick a random color from the mColorArray array:
String colorName = mColorArray[random.nextInt(20)];

The nextInt() method with the argument 20 gets another random integer between 0 and 19. You use that integer as the index of the array to get a color name.

  1. Get the resource identifier (an integer) for the color name from the resources:
int colorResourceName = getResources().getIdentifier(colorName,
          "color", getApplicationContext().getPackageName());

When your app is compiled, the Android system converts the definitions in your XML files into resources with internal integer IDs. There are separate IDs for both the names and the values. This line matches the color strings from the colorName array with the corresponding color name IDs in the XML resource file. The getResources() method gets all the resources for your app. The getIdentifier() method looks up the color name (the string) in the color resources ("color") for the current package name.

  1. Get the integer ID for the actual color from the resources and assign it to a colorRes variable, and use the getTheme() method to get the theme for the current application context.
int colorRes = 
   getResources().getColor(colorResourceName, this.getTheme());

The getResources() method gets the set of resources for your app, and the getColor() method retrieves a specific color from those resources by the ID of the color name. However, getColor() has a red underlined highlight.

If you point at getColor(), Android Studio reports: "Call requires API 23 (current min is 15)". Because your minSdkVersion is 15, you get this message if you try to use any APIs that were introduced after API 15. You can still compile your app, but because this version of getColor() is not available on devices prior to API 23, your app will crash when the user taps the Change Color button.

At this stage you could check for the platform version and use the right version of getColor() depending on where the app is running. A better way to support both older and newer Android APIs without warnings is to use one of the compatibility classes in the support library.

  1. Change the colorRes assignment line to use the ContextCompat class:
int colorRes = ContextCompat.getColor(this, colorResourceName);

ContextCompat provides many compatibility methods to address API differences in the application context and app resources. The getColor() method in ContextCompat takes two arguments: the current context (here, the Activity instance, this), and the name of the color.

The implementation of this method in the support library hides the implementation differences in different versions of the API. You can call this method regardless of your compile SDK or minimum SDK versions with no warnings, errors, or crashes.

  1. Set the color of the TextView to the color resource ID:
mHelloTextView.setTextColor(colorRes);
  1. Run the app on a device or emulator, and click the Change Color Button.

The Change Color Button should now change the color of the text in the app, as shown below.

MainActivity solution

The following is the changeColor() click handler in MainActivity:

/**
* This method handles the click of the Change Color button by
* picking a random color from a color array.
*
* @param view The view that was clicked
*/
public void changeColor(View view) {
    // Get a random color name from the color array (20 colors).
    Random random = new Random();
    String colorName = mColorArray[random.nextInt(20)];

    // Get the color identifier that matches the color name.
    int colorResourceName = getResources().getIdentifier(colorName,
                        "color", getApplicationContext().getPackageName());
    
    // Get the color ID from the resources.
    int colorRes = ContextCompat.getColor(this, colorResourceName);

    // Set the text color.
    mHelloTextView.setTextColor(colorRes);
}

Android Studio project

Android Studio project: HelloCompat

Challenge: Rather than using ContextCompat for to get the color resource, use a test of the values in the Build class to perform a different operation if the app is running on a device that supports a version of Android older than API 23.

Installing the Android Support Library:

Android uses three directives to indicate how your app should behave for different API versions:

To manage dependencies in your project:

The ContextCompat class provides methods for compatibility with context and resource-related methods for both old and new API levels.

The related concept documentation is in 3.3: The Android Support Library.

Android Studio documentation:

Android developer documentation:

Other:

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:

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.

Run an app

Open the HelloCompat app you created in the practical on using support libraries.

  1. Set a debugger breakpoint on the line in the changeColor() method that actually changes the color:
int colorRes = ContextCompat.getColor(this, colorResourceName);
  1. Run the app in debug mode on a device or emulator that's running an API version 23 or newer. Click Step Into to step into the getColor() method and follow the method calls deeper into the stack. Examine how the ContextCompat class determines how to get the color from the resources, and which other framework classes it uses.

Some classes may produce a warning that the "source code does not match the bytecode." Click Step Out to return to a known source file, or keep clicking Step Into until the debugger returns on its own.

  1. Repeat the previous step for a device or emulator running an API version older than 23. Note the different paths that the framework takes to accomplish getting the color.

Answer these questions

Question 1

Which class appears when you first Step Into the ContextCompat.getColor() method? Choose one:

Question 2

In the class that appears, which statement is executed if the build version is API version 23 or newer? Choose one:

Question 3

If you change the ContextCompat.getColor() method back to the getColor() method, what will happen when you run the app? Choose one:

Submit your app for grading

Guidance for graders

No app to submit for this homework assignment.

To find the next practical codelab in the Android Developer Fundamentals (V2) course, see Codelabs for Android Developer Fundamentals (V2).

For an overview of the course, including links to the concept chapters, apps, and slides, see Android Developer Fundamentals (Version 2).