Chapters

Hide chapters

Android Apprentice

Fourth Edition · Android 11 · Kotlin 1.4 · Android Studio 4.1

Section II: Building a List App

Section 2: 7 chapters
Show chapters Hide chapters

Section III: Creating Map-Based Apps

Section 3: 7 chapters
Show chapters Hide chapters

5. Prettifying the App
Written by Darryl Bayliss

Take a moment to congratulate yourself and recognize what you’ve accomplished so far. You have a working Android app that lets users fight the clock and score as many points as possible.

You also fixed a few undiscovered bugs and added support for portrait and landscape mode, regardless of their device. Your app is ready to entertain people for years to come!

There’s one problem though: It’s not visually exciting.

Nothing special here...
Nothing special here...

An app that looks visually appealing tends to stick out compared to similar apps. While it’s not integral to the functionality of your app, it does give it that “wow!” factor.

In this final chapter for the section, you’ll learn how to:

  1. Adjust your app’s colors and themes.
  2. Add small touches to give your app a polished look and feel.
  3. Add a simple animation to your app to give it some life.

Getting started

If you’ve been following along, open your project and keep using it for this chapter. If not, don’t worry. Locate the projects folder for this chapter and open the Timefighter app inside the starter folder.

The first time you open the project, Android Studio takes a few minutes to set up your environment and update dependencies.

With Timefighter open, run the app and consider some things you can do to improve the way it looks. Perhaps you can change the color of the app bar or the white screen? Maybe the button feels a little lifeless when tapped? And why is the app so silent — maybe it needs sound effects?

The important thing to remember is that you don’t need to do everything. You only need to make changes that add to the essential elements on the screen. If you add too much, you run the risk of confusing the user.

Changing the app bar color

In the Project navigator, on the left side of Android Studio, open colors.xml; it’s located in app > res > values. You’ll see a collection of hexadecimal colors defined:

<resources>
  <color name="purple_200">#FFBB86FC</color>
  <color name="purple_500">#FF6200EE</color>
  <color name="purple_700">#FF3700B3</color>
  <color name="teal_200">#FF03DAC5</color>
  <color name="teal_700">#FF018786</color>
  <color name="black">#FF000000</color>
  <color name="white">#FFFFFFFF</color>
</resources>

colors.xml stores the color values used in your app. Like strings.xml, it’s an excellent place to store color-related values in one location, making it easier to change things later.

To define colors, you use a <color> tag along with a name attribute that you can use as a reference when it’s compiled into R.java. The reference is available for use in your XML Layouts and also at runtime in code.

Within <color>, you assign a hexadecimal representation of the color. You close the tag using </color>.

With the theory out of the way, you’re ready to update the file. In colors.xml, change the values to match the following:

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <color name="colorPrimary">#0C572A</color>
  <color name="colorPrimaryDark">#388E3C</color>
  <color name="colorAccent">#8BC34A</color>
  <color name="colorBackground">#D3D3D3</color>
</resources>

The colors you added are various shades of green, you’ll get to see them used in Timefighter soon. You’re maybe wondering how adding these colors changes the app. The answer lies in themes.xml. These files are located in app > res > values > themes.

You may be wondering why there are two files here. As the picture suggests, one of the themes is used when your device is set to Night Mode. You’ll update both themes to use the colors added.

Open themes.xml and review its contents:

<resources xmlns:tools="http://schemas.android.com/tools">
  <!-- Base application theme. -->
  <style name="Theme.Timefighter" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
    <!-- Primary brand color. -->
    <item name="colorPrimary">@color/purple_500</item>
    <item name="colorPrimaryVariant">@color/purple_700</item>
    <item name="colorOnPrimary">@color/white</item>
    <!-- Secondary brand color. -->
    <item name="colorSecondary">@color/teal_200</item>
    <item name="colorSecondaryVariant">@color/teal_700</item>
    <item name="colorOnSecondary">@color/black</item>
    <!-- Status bar color. -->
    <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
    <!-- Customize your theme here. -->
  </style>
</resources>

Notice the <item> tags. These tags define specific items within your app which adhere to a particular color. In this case, these colors are the colors you just removed from colors.xml. Since these colors no longer exist, you need to update each item to use a new color.

Change themes.xml to use the new colors. It should look something like:

<resources xmlns:tools="http://schemas.android.com/tools">
  <!-- Base application theme. -->
  <style name="Theme.Timefighter" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
    <!-- Primary brand color. -->
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryVariant">@color/colorPrimary</item>
    <item name="colorOnPrimary">@android:color/white</item>
    <!-- Secondary brand color. -->
    <item name="colorSecondary">@color/colorAccent</item>
    <item name="colorSecondaryVariant">@color/colorAccent</item>
    <item name="colorOnSecondary">@android:color/black</item>
    <!-- Status bar color. -->
    <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
    <!-- Customize your theme here. -->
  </style>
</resources>

Do the same to themes.xml (night), updating the items to use the same new colors. Note that usually you’d tweak a few colors for Night mode to account for the changed colors of text elements, but you’ll learn more about that later.

One final tweak before running Timefighter, making sure the MainActivity background is set to a color. Open activity_main.xml, located in app > res > layout, then switch from Design to Text. Update the ConstraintLayout to change the color of the background, like so:

<androidx.constraintlayout.widget.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"
    android:background="@color/colorBackground"
    tools:context=".MainActivity">

With the colorBackground attribute set, the whole background of the ConstraintLayout is set to a gray color. The gray color is taken from colors.xml.

Run the app and see if you can notice the difference.

That’s more interesting!
That’s more interesting!

With a few lines of code, you’ve managed to transform the app and make it more visually appealing.

Animations

Animations in apps give visual emphasis and direct the users’ attention to certain parts. When it comes to animation, the most important rule is to use it where and when it matters — not simply because you can.

The most heavily used component in TimeFighter is the “Hit Me” button — because that’s what earns the user points. So, adding an animation here makes sense!

In the Project navigator, right-click on res. In the drop-down window, navigate to New and click Android resource directory.

In the New Resource Directory window, click the drop-down button next to Resource type and select anim. The name of the directory will automatically change to be called anim too. Finally, click OK.

In the Project navigator, you now have a new folder inside res named anim.

Next, you need to create the file defining the animation for your button. Right-click on anim, navigate to New, and click Animation resource file on the right-most drop-down.

A new window will appear, similar to the one you saw when creating the anim folder. This time, you need to enter the name of the file. For the File name, enter bounce, then click OK.

Android Studio creates the resource file and opens it up for you. The file will look like this:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">

</set>

Notice the set attribute? This is a container to hold all of the transformations that occur throughout your animation. You can bundle more than one transformation in the same animation and have them all run concurrently.

Think of a transformation as something that happens over time. Imagine a dog moving from the left of the screen to the right: As the dog walks along the screen, his position changes; he may even grow larger as he moves.

This dog is performing two transformations. Moving from left to right and also growing in size!
This dog is performing two transformations. Moving from left to right and also growing in size!

For this animation, you only need one transformation. Making the View twice its size, then resizing back to its original size over time.

Edit bounce.xml to match the following:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
  android:fillAfter="true"
  android:interpolator="@android:anim/bounce_interpolator">
  <scale
    android:duration="2000"
    android:fromXScale="2.0"
    android:fromYScale="2.0"
    android:pivotX="50%"
    android:pivotY="50%"
    android:toXScale="1.0"
    android:toYScale="1.0" />
</set>

For a small amount of XML, there’s a lot of going on. Let’s look at the first few lines:

<set xmlns:android="http://schemas.android.com/apk/res/android"
  android:fillAfter="true"
  android:interpolator="@android:anim/bounce_interpolator">

The set to hold the animation is declared and fillAfter is set to true. Setting fillAfter to true means the animation won’t reset the View to its original position once it’s complete. Instead, the View remains wherever it is when the animation ends.

The set also sets the interpolator to bounce_interpolator. Interpolators affect the rate that the entire animation is performed over time, independent of durations set within the transformations.

The bounce_interpolator is built into Android and has many other built-in interpolators. You can also create your own if you don’t find one that suits your needs. For now, the bounce_interpolator included with Android works nicely.

Time for the next few lines:

<scale
  android:duration="2000"
  android:fromXScale="2.0"
  android:fromYScale="2.0"
  android:pivotX="50%"
  android:pivotY="50%"
  android:toXScale="1.0"
  android:toYScale="1.0" />

Within the set, you declare a scale attribute. This informs the animation to resize the View running the animation. You also declare that scaling should occur over 2000 milliseconds (2 seconds), via the duration attribute.

In addition, you set the width and height of the View as 2.0, twice the original size when the animation starts via the fromXScale and fromYScale attributes.

The pivotX and pivotY attributes specify the center point where the animation occurs. In this case, it occurs from the center of the View, expressed in percentages as 50%: halfway across the X-axis and halfway across the Y-axis.

Finally, you set the size of the View at the end of the animation as 1.0, via the toXScale and toYScale attributes. This sets the View back to its original size.

To summarize, the animation will:

  • Scale the animated View to twice its size.
  • Shrink it back to its original size.
  • Do this over the space of two seconds.
  • Use a bouncing interpolator for the rate the animation moves.

Note: If you want to know more about animation resources and interpolators on Android, review the Android Developer documentation (https://developer.android.com/guide/topics/resources/animation-resource.html) for an in-depth review.

With the animation resource created, it’s time to put it to use. Open MainActivity.kt and modify the tapMeButton.setOnClickListener callback in onCreate() to use the animation:

tapMeButton.setOnClickListener { view ->
  val bounceAnimation = AnimationUtils.loadAnimation(this,
      R.anim.bounce)
  view.startAnimation(bounceAnimation)
  incrementScore()
}

Every time you click the button, tapMeButton.setOnClickListener loads the bounce animation inside anim and instructs the button (which is passed into the OnClickListener as view) to use that animation.

Run the app and click the button, you’ll see the button jumping out at you.

Adding a Dialog

Making an app is fun and something to be proud of. It’s natural to want to let your users know you created an app. At the same time, you don’t want to distract your users while they’re playing your game. So what can you do? One option is to use a Dialog.

A Dialog is a way to provide a snippet of information without moving away from the main content on the screen. You’re going to use a Dialog to let your users know about the creator of the app and what version of TimeFighter they’re running.

An easy way to do that is to set up a button in the top bar. But first, you need to define a menu.

A menu is a set of items that sit in the app bar, along the top of the screen. The items allow the user to perform actions, depending on the Activity shown. It’s common for the items to change, depending on the context of what the Activity is doing.

In the project navigator, locate res. Right-click on the folder and select Android resource directory. In the window that appears, click the resource type drop-down and change it to menu. Then, click OK.

In the project navigator, Right-click on the newly created menu resource folder. In the pop-up menu, hover over New, and then click on Menu resource file.

In the New Resource File window, enter the file name as menu and click OK:

Android Studio changes over to the Layout window and shows you a similar setup to what you’ve seen when editing Layout files:

Note: Android Studio may open the Text editor. If this happens, click Design at the bottom of the Layout window.

Here, you have similar windows as when editing a layout. The Palette in the top-left changes to show only menu-specific items, and the Component Tree gives you an overview of the hierarchy for your menu.

You want a single item on your menu. To do that, move your cursor over to the Menu Item button in the Palette window, and click and drag from the Menu Item and onto your Layout.

The menu item will appear on the preview:

So far, so good. Your newly-placed menu item is now highlighted, and the Attributes window is shown on the right side. The next thing to do is to edit the attributes for the item.

First, set the id for the menu item and name it about_item. Next, move on to the title attribute and name your menu item About.

Next, you need to decide what icon to use for the menu item. Android includes plenty of embedded images from which to choose, so you can use one of those.

In the Attributed window, click the small picture in the left of the icon text field:

The resources window will appear.

The resources window displays the resources available for use within your app, whether they are built into Android or your own custom resources. The window shows both images — or drawables, as Android refers to them — or colors.

In the top-left of the Resources window is a search bar. Click in the search bar and type ic_menu_info.

As you type, the list of resources filters down to match any resources that contain the characters you entered. In this case, there’s only one.

Click the resource under the Android drop-down to select it, and then click OK. The Resource window closes and takes you back to the Layout window. The icon text field is populated with the resource you chose.

Finally, to make sure the button is always visible, you need to set the showAsAction attribute. Click the flag next to showAsAction. In the dialog that appears, check Always, and then click Apply.

showAsAction affects how your menu item is presented and can have multiple choices depending on the number of items your menu contains and the screen size of your device. You want the menu item to always show up regardless of the circumstances. To do that, you need to set the value to Always.

Looking good! With the menu created in XML, you need to setup your Activity to use it.

In MainActivity.kt, add the following method below onDestroy():

override fun onCreateOptionsMenu(menu: Menu): Boolean {
  // Inflate the menu; this adds items to the action bar if it is present.
  super.onCreateOptionsMenu(menu)
  menuInflater.inflate(R.menu.menu, menu)
  return true
}

This method overrides the Activity callback when it attempts to create the menu. You make a call to super to give any superclasses of your Activity a chance to set themselves up.

You then use the Activity’s menuInflater to programmatically set up your menu layout for the Activity. Finally, you return true to let the Activity know that the menu is set up.

Now that the menu is setup, you also need to tell your Activity what to do in case the item is clicked. To do that, below onCreateOptionsMenu(menu: Menu), add this method:

override fun onOptionsItemSelected(item: MenuItem): Boolean {
  if (item.itemId == R.id.about_item) {
    showInfo()
  }

  return true
}

onOptionsItemSelected(item: MenuItem) is called when a user selects a menu item. The parameter, item is passed into the method, and can be used to see if the ID of the selected menu item is equal to the ID of the item you set up earlier. If so, you call showInfo(). Don’t worry about the error showInfo() creates, you’ll create the method shortly.

Finally, you return true from onOptionItemSelected(item: MenuItem) to let the Activity know that the event was processed.

You’re almost ready to run the app again. The next step is to create the Dialog to show users who created the app. This is what the showInfo() method is responsible for.

Add the following method to MainActivity.kt, anywhere inside of the class. don’t worry about editor errors; you’ll work on that next.

private fun showInfo() {
  val dialogTitle = getString(R.string.about_title,
      BuildConfig.VERSION_NAME)
  val dialogMessage = getString(R.string.about_message)

  val builder = AlertDialog.Builder(this)
  builder.setTitle(dialogTitle)
  builder.setMessage(dialogMessage)
  builder.create().show()
}

Let’s go through the method. showInfo() handles the setting up of a dialog View for you. It creates two strings to use in the dialog, one for the title and one for the message.

These strings are created using a mixture of the strings stored in strings.xml and strings generated when your app is built. In this case, this is the VERSION_NAME of your app. The version name is already available within your app, you’ll set up the other strings you need in strings.xml in a moment.

Next, you create an AlertDialog.Builder and pass in a Context instance to let the Dialog know what Activity to appear on. You set the Dialog title and message, create the Dialog, and finally display it.

Note: When you add val builder = AlertDialog.Builder(this), Android Studio offers to auto-import a library for you, and it offers several options. Be sure to select the Android Support Library version of AlertDialog: androidx.appcompat.app.AlertDialog.

Open strings.xml and add the string the Dialog expects. Don’t forget to substitute your name in about_message, remember it’s your app :].

<string name="about_title">Timefighter %1$s</string>
<string name="about_message">Created by YOUR NAME HERE</string>

Finally, run the app and check out the new menu item sitting in the top-right of the screen. Tap the info button in the menu, and the dialog shows up in the middle of the screen.

Fantastic! You now have a place for people to find out what version of your app they’re using and who created it. Well done.

Key Points

You’ve just completed your first app! Take a moment to appreciate what you’ve built. By finishing the app, you’ve learned:

  • How to change the colors of your app.

  • How to create an animation and apply it to a View.

  • How to setup a menu for your app and add buttons to it.

  • How to create a dialog and show information using it.

Where to go from here?

Congratulations on completing the first section of the book. You learned a lot over the last few chapters, and you now know how to create a simple game.

In the next section, you’ll stop working on TimeFighter and move on to a different app that builds upon the skills and concepts you’ve learned in this first section.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2024 Kodeco Inc.