Chapter 5

Interacting with the Users

IN THIS CHAPTER

check Working with layout elements

check Using color in a layout

check Adding action to the user interface

check Switching elements in and out

Ultimately, apps perform two tasks: manage data and make it accessible to the user. Even with all the odd sound effects, visual eye candy, and interesting features, the bottom line is that the user is depending on the app to manage and present some sort of data. Just think about all the ways in which an app stores data and then presents it to the user. A personal contact can appear as part of an email, text, phone call, data destination, data sender, meeting participant, photo annotation, and much more. The same can be said of almost every other sort of data you can imagine. Even playing games involves the manipulation of data and presenting it in ways that the user will understand.

This chapter helps you consider the user end of the data pipeline. You discover various techniques of creating engaging user interfaces that will make the user’s app experience a lot more pleasant.

Creating a Great Layout

The layout of your app determines how the user interacts with the data — the order in which data elements receive attention. A layout can also create focal points so that more important data receives more attention than less important data. The goal is to create a workflow that the user finds useful in performing tasks quickly. If a user has to stop doing work in order to find a setting or to understand the data, the layout isn’t performing well and the user will be less efficient. The most successful layout is the one that the user doesn’t notice at all — it simply works.

This section relies on an app named 03_05_01 to provide glimpses into working with layouts in Android. You see how the various elements combine to create an interesting composite user interface to the user. So, begin with the Empty Activity without any controls or components added. Set the minimum API level to 19. In the following sections, you read about layout elements to use with Android.

Defining the View and ViewGroup elements

A view is a rectangular area of a layout that contains a single widget. Examples of widgets are

  • TextView
  • Button
  • CheckBox

Every widget you use in your apps is a member of the View class. The members you see in the Designer Palette are those that come with Android. Adding libraries in the module build.gradle file often adds new controls to the Palette, and each of these new controls is a member of the View class as well. You can also create custom controls by creating a class that inherits from the View class, or you can save time by inheriting from one of the existing control classes, such as Button. Just keep in mind that a view is always a single element of your layout.

A view group is usually a collection of one or more views and possibly other view groups. The ViewGroup class provides the basis for the containers and layouts found in the Designer Palette. The view group generally acts as a kind of layout and container for the app interface, whether it appears as part of an activity or a fragment.

Remember A special kind of view group extends the AdapterView class rather than the ViewGroup class. This kind of view group will accept only controls that derive from the Adapter class as children. Examples of Adapter class controls are

  • ListView
  • GridView
  • Spinner
  • Gallery

Tip You can create a <merge> element to contain a layout that you won't use in a stand-alone configuration. The reusable layout examples in Book 1, Chapter 6 (01_06_03 through 01_06_06) could benefit from this approach. The <merge> element acts as a different kind of layout root than one of the layout elements, such as the ConstraintLayout. When using a <merge> layout, you must combine it with an <include> element in another file to see the components in the <merge> layout. In addition, you position an <include> that uses a <merge> onscreen using any of the various layouts instead of trying to position the widgets within the <merge> itself.

The <include> element enables you to combine multiple layouts into a cohesive whole. You use this element in Book 1, Chapter 6 to combine two independent layouts (reusable_layout.xml becomes part of activity_main.xml layout).

All the various views and view groups so far create a user interface that appears the moment you inflate it onscreen using setContentView(). Android also supports a lazy view through <ViewStub>. A <ViewStub> remains hidden until you specifically inflate it by calling code like this:

ViewStub.inflate()

You can't use a <ViewStub> with a <merge>. Instead, you must use it with a ViewGroup or an AdapterView.

Creating a layout using XML

Most of the time, you use the Designer to create layouts because moving components and controls from the Palette to the design area or the Component Tree is a lot easier and faster than performing a lot of typing. However, as you see in the “Using the AndroidX approach” section, at the end of this chapter, some design elements don't just appear in the Palette — you must access them by typing the XML directly into the Text tab.

Some containers are also inaccessible from the Palette. For example, the <merge> falls into the category. However, you don’t actually type the <merge> element into the Text tab. Rather, you enter it into the Root Element field of the New Resource File dialog box that you access by right-clicking the layout folder and choosing New⇒  Layout Resource File. As shown in Figure 5-1, merge shows as one of the items you can type into the field.

Screenshot of the New Resource
File dialog box where merge shows as one of the items you can type into the field.

FIGURE 5-1: Defining a <merge> element as the root element.

The resulting <merge> layout has the required namespaces defined, so you simply start adding controls to it. Here is the single control used for an example in this case:

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

<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/MergeText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/MergeTextText"/>
</merge>

To use the merge in your main layout, you add an <include>, not something like a <ViewStub>. However, to ensure that you can position the items in the <merge>, you want to place the <include> within a layout, such as a LinearLayout(horizontal). When you move the <include> from the Containers folder of the Palette to the design area or the Component Tree, you see the Resources dialog box shown in Figure 5-2, where you select the <merge> you want to use.

Screenshot of the Resources dialog box, where you select the <merge> you want to use,

FIGURE 5-2: Selecting the <merge> to provide in the <include>.

After you have a layout and an <include> in place, you can configure the <merge> as you would any other control. In reality, however, you're configuring an entire group of controls — the entire <merge>. Here’s what a <merge> might look like in activity_main.xml when accessed through an <include>:

<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="8dp"
android:gravity="center_horizontal"
android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">

<include
android:id="@+id/Merge"
layout="@layout/merge_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"/>
</LinearLayout>

You must use a full layout with a <ViewStub> element. The layout doesn't appear at runtime; you have to inflate it later. Here’s what a simple layout used with a <ViewStub> might look like:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">

<TextView
android:id="@+id/StubText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="@string/StubTextText"/>

All positioning is done within the layout rather than the host in most cases. What you’re really doing is merging two layouts in this case. The <ViewStub> code in activity_main.xml looks something like this:

<ViewStub
android:id="@+id/ViewStub"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:layout="@layout/stub_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>

Modifying a layout at runtime

You can use a variety of methods to modify the layout at runtime. However, in most cases, the methods employ these techniques:

  • Inflate an element
  • Deflate an element
  • Hide an element
  • Show an element
  • Rearrange elements
  • Change element characteristics (such as title)
  • Use a different layout

Tip In general, you don’t want to modify the layout without good reason. Perhaps a particular option becomes unavailable (such as you’re out of cheese pizza and you don’t want the option to appear on your menu) or you need the user to supply one piece of information before asking for another (depending on country of origin, you provide different address forms). You might also provide different layouts that depend on user preference or move elements around to accommodate special needs (such as larger fonts for those who have viewing difficulties). When the need does arrive, you want to plan ahead for it and use the most appropriate technique. For example, you use a <ViewStub> for additional information after the user has provided a necessary input, or you hide the menu item that isn't available. Here’s some code that inflates the <ViewStub> and then hides the two buttons on the current layout:

fun onShowViewStubClick(view: View) {
if (ViewStub != null) {
ViewStub.inflate()
ShowViewStub.visibility = View.GONE
Layout2.visibility = View.GONE
}
}

When you decide to have multiple layouts for your app, you can switch between them simply as long as each layout has a different id value. The example app has a Main_Layout that uses one order of controls (the buttons appear at the bottom) and an Aux_Layout that uses a different order of controls (the buttons appear at the top). The controls are the same in each layout. Using different layouts doesn't mean that the layouts must have entirely different controls. Clicking the Other Layout button in one layout calls the same click handler as clicking the Other Layout button in the other layout. Here’s the click handler for the Layout2 button:

private var CurrentView = "Main"
fun onLayout2Click(view: View) {
if (CurrentView == "Main") {

setContentView(R.layout.activity_main2)
CurrentView = "Aux"
} else {
setContentView((R.layout.activity_main))
CurrentView = "Main"
}
}

You can discover the current layout through various means, but sometimes clever programming is error prone, and simple is better. This is one of those cases. Simply create a variable such as CurrentView to track the current view and set it appropriately. You can move back and forth between views as needed without problem using this approach.

Considering the common layouts

Android provides you with a number of common layouts that will work with non-adapter widgets. The layouts are

  • ContraintLayout: Places the controls relative to the parent and sometimes each other based on the limits of the current device. The controls appear in the same order and in about the same place within the limits of a constraint on each device that displays the user interface.
  • LinearLayout: Creates a horizontal or vertical arrangement of controls with one control in each horizontal or vertical slot. The size of the largest control determines the size of the slot. This layout relies on scroll bars when the layout doesn't fit on the user’s screen.
  • FrameLayout: Defines an area onscreen to place a control, usually graphical, such as a picture.
  • TableLayout: Relies on a tabular format similar to the tables used for web pages. Control size can vary to accommodate various device sizes. The overall layout remains the same.
  • RelativeLayout (legacy): Makes it possible to position the controls relative to each other, which means that devices of different sizes will have the controls in the same order, but not necessarily in the same place.

Working with adapters

Adapters enable you to create views that contain a number of items. You might have a group of pictures, list of employees, or any other sort of collection, fixed or dynamic. The purpose of an adapter is to hold items. Different adapters hold different items better than. For example, when creating a list of pictures, an ImageView works best. This example, however, uses a ScrollView, which allows you to display any number of simple or complex items. You use a LinearLayout(vertical) to provide structure for the items, and the items appear within a TextView. Here is the initial design for this part of the example:

<ScrollView
android:id="@+id/scrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="160dp"
android:layout_marginBottom="20dp"
android:scrollbarSize="10dp"
android:scrollbarStyle="insideInset"
android:scrollbars="vertical"
app:layout_constraintBottom:toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">

<LinearLayout
android:id="@+id/ScrollItems"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"></LinearLayout>
</ScrollView>

The designer will complain that the LinearLayout is useless, but you can safely ignore that complaint. You fill the LinearLayout with TextView items in onCreate(), as shown here in bold:

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

for (value in 0..30) {
var valueTV: TextView = TextView(this)
valueTV.setText(value.toString())
valueTV.gravity = Gravity.CENTER_HORIZONTAL
ScrollItems.addView(valueTV)
}
}

All the code does is to create a TextView with a number in the range from 0 through 30. The number is centered in the TextView by adding Gravity.CENTER_HORIZONTAL. You then add it to the LinearLayout, ScrollItems, using addView(). The result is the scrollable list shown in Figure 5-3.

Debugging your layout

The previous sections point only to what is possible, because Android provides a lot of flexibility in creating a layout. Unfortunately, the flexibility you have also complicates the task of determining where problems exist in the layout, which is where the Layout Inspector comes into play. These steps get you started with the Layout Inspector:

  1. Start your app.

    It doesn't matter whether you place it in run or debug mode, but the Layout Inspector will work faster if you’re not also debugging the app.

  2. Choose Tools⇒  Layout Inspector in Android Studio.

    You see the Choose Process dialog box, shown in Figure 5-4.

    A mobile screen displaying the scrollable list of items for determining where problems exist in the layout, with the help of the Layout Inspector.

    FIGURE 5-3: Viewing the scrollable list.

    Screenshot of the Choose Process dialog box to select the process in the Android Studio that you want to inspect.

    FIGURE 5-4: Select the process you want to inspect.

  3. Highlight the process you want to inspect and click OK.

    You see the window shown in Figure 5-5, which has the following panes:

    • View Tree: Displays every element of the layout so that you can drill down and choose specific items.
    • Load Overlay: Shows a graphical representation of the layout with the individual controls highlighted. The selected item from View Tree has a border of a different color so that you can pick it out easily.
    • Properties Table: Contains the properties for the item selected in View Tree. You can drill down into the properties to see how they are set to determine why the display isn’t working as expected.
Screenshot of a window in which the Layout Inspector gives a graphical representation of the layout with the individual controls highlighted.

FIGURE 5-5: The Layout Inspector gives you significant detail about your layout.

By comparing what the Layout Inspector shows with what you expected to see, you can locate potential problems in your layout or associated code. More important, drilling down into the properties often tells you which properties to set to obtain a desired result. Unfortunately, you can’t modify the settings to try them out while the app is running, but getting an idea of what to try still gives you a significant advantage.

Employing Color and Texture

Some user interfaces focus on shades of gray, or perhaps gray, white, and black. Such user interfaces are boring. Unless your goal is to put your user to sleep, you really do need more than gray, and the addition of white and black won’t fulfill the need. Of course, you also don’t want to resort to garish displays of colliding colors that will bedazzle the use to the point of unconsciousness. A nice middle ground of color works best. Fortunately, Android Studio offers help in this area, especially if you tend toward the two extremes.

Texture gives your app a feel. The hard outlines of the default controls work best when every control is important. However, if a control becomes less important or simply unavailable because of the task the user is performing, a somewhat fuzzy outline can be helpful because it provides a visual cue. That’s what texture is all about — making your app interesting while also providing cues about what to do next with it.

This section relies on an app named 03_05_02 to show how to work with both color and texture using a variety of methods, including styles. The app doesn’t do anything special except show off what’s available to you as the developer. Begin with the Empty Activity without any controls or components added. Set the minimum API level to 14. (If you want to work with advanced features, such as Palette, you must set the minimum API level to 28.)

Remember The following sections aren’t meant to turn you into a color and texture guru; they’re designed to help you make great use of the functionality that Android Studio offers. Going this route won’t provide your users with a unique experience, but most users are really only interested in something motivating.

Working with styles and themes

A style is simply a kind of template for defining how a particular view looks. You might want to display buttons with particular characteristics, for example. In fact, the Designer often makes style suggestions to you. When you place a series of buttons in a LinearLayout, the Designer suggests using one of several styles that make the buttons borderless so that they provide a smooth appearance across the screen. You use a style when the overall appearance of the app works well, but an individual view needs a little extra work.

A theme is a style that you apply across an entire app, activity, or view hierarchy. Using a theme saves time because you can apply the styles to the app as a whole, giving every element of the app a consistent appearance. Often, a theme establishes most of the style for an app, and you may tweak that appearance for individual elements using a style.

Tip In the following sections, you see how to work with styles and themes, but these sections don’t necessarily dictate good design choices. You’d do well to combine your development skills with those of someone with design knowledge so that you can create a presentation that looks great.

Working with themes

Your app already has one built-in theme attached to it. Look in the app esvalues folder to find styles.xml. When you open this file, you see the base application theme shown here:

<resources>

<!-- Base application theme. -->
<style name="AppTheme"
parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">
@color/colorPrimary
</item>
<item name="colorPrimaryDark">
@color/colorPrimaryDark
</item>
<item name="colorAccent">
@color/colorAccent
</item>
</style>

</resources>

A theme consists of the <style> element and one or more <item> elements. The theme must have a name attribute so that you can access it in your code. The parent attribute determines what the theme affects, which is the dark action bar used for light activities in this case. Each of the <item> entries defines a particular aspect of the theme appearance; in this case, only colors are affected. However, when you add a new <item> entry and type name, you see a selection of stylistic choices that you can modify, as shown in Figure 5-6. Each <item> can modify only one style.

Tip You can find a code listing of the themes online by using the Google search term Android "Theme.AppCompat.Light.DarkActionBar" site:android.googlesource.com. The themes.xml file contains a number of versions, so make sure you use the newest possible version to access the latest theme definitions.

Screenshot displaying a list of stylistic choices that can be modified when you add a new <item> entry and type name. Each <item> can modify only one style.

FIGURE 5-6: A list of styles that an <item> can modify.

The colors in the styles.xml file determine the appearance of your app when you run it. However, the color definitions don't actually appear in this file; they instead appear in colors.xml, shown here:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#008577</color>
<color name="colorPrimaryDark">#00574B</color>
<color name="colorAccent">#D81B60</color>
</resources>

Remember Unlike the <item> entries, the <color> entries don't have specific names, but you should choose something descriptive, as shown in the example. The default theme for your app appears in the AndroidManifest.xml file as the android:theme="@style/AppTheme" attribute.

You can create as many themes for your app as you like. All you need is a separate theme entry for each theme. The theme must have a unique name, like this:

<style name="AppTheme2"
parent="Theme.AppCompat.Light.DarkActionBar">
<item name="colorPrimary">
@color/colorPrimary2</item>
<item name="colorPrimaryDark">
@color/colorPrimaryDark2</item>
<item name="colorAccent">
@color/colorAccent2</item>
<item name="colorButtonNormal">
@color/colorButtonNormal</item>
</style>

You must then define any new colors in the colors.xml file. Where you apply themes is important. In working with app-wide themes, you must place the code for changing the theme before the app draws the app window. For the initial screen, this means placing the code in onCreate(), like this:

override fun onCreate(savedInstanceState: Bundle?) {
if (savedInstanceState != null) {
var selectedTheme = savedInstanceState
.getInt("SavedTheme", -1)
when (selectedTheme) {
R.style.AppTheme -> setTheme(R.style.AppTheme)
R.style.AppTheme2 -> setTheme(R.style.AppTheme2)
else -> setTheme(R.style.AppTheme)
}
}
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}

For this solution to work, you must provide user preference code to save the desired style. The “Setting preferences using the Preference Library” section of Chapter 4 of this minibook tells you that you have to create and use a user preference setup. Notice that the call to setTheme() must appear before super.onCreate() or the call won't work. There are theoretically low-level techniques for bypassing this issue, but they’re definitely for an experienced Android developer and outside the scope of this book. We’ll just say that such techniques are extremely error prone.

You can also set a theme for each new activity or fragment that you create by setting the theme before you create the element. Setting a theme at this level is significantly easier than working with MainActivity.

Using the built-in styles

Styles are more easily changed than themes because styles generally apply to individual views or view groups and affect specific aspects of the element in question. During design time, you sometimes see a drop-down list containing choices for a particular view, such as a button. Figure 5-7 shows the selections for setting the style of a button:

When working with other styles, you see other sorts of dialog boxes to assist you in changing the view’s style. For example, when working with colors, you see a color chooser of the sort shown in Figure 5-8. You click the color block on the left side of the attribute entry value.

Screenshot displaying the selections for setting the style of a button during design time.

FIGURE 5-7: Choosing a button style during design time.

Screenshot for using the color chooser to change a color style directly by clicking the color block on the left side of the attribute entry value.

FIGURE 5-8: Use a color chooser to change a color style directly.

As with many other parts of Android design, you can also use a predefined resource by clicking the square on the right side of the attribute value to display the Pick a Resource dialog box, shown in Figure 5-9. You can select from Project, Android, and Theme Attribute colors, or you can create an entirely new resource if you want.

Creating your own styles

As with app-wide themes, you can also create styles that affect individual elements within the colors.xml or styles.xml files. The important thing is to create the style in the correct file and make it of the correct type. For example, if you want to create a custom color style, it must appear in the colors.xml file and be within a <color> element. The process, however, is the same as working with a theme; it's just less complex.

Screenshot of Pick a Resource dialog box, where you can select  custom colors from Project, Android, and Theme Attributes for your project.

FIGURE 5-9: Rely on standard or custom color selections for your project.

Changing styles programmatically

When you use standard or custom styles, you can change the style of a view any time you want while the app is running. This flexibility differs from working with themes that are applied before you draw the window and are hard to change later. To change the style of a view, you must first obtain the style information and then apply it to the correct object attribute, as shown here:

fun onChangeStyleClick(view: View){
var textColor = ContextCompat.getColor(
this, R.color.colorWhite)
var backColor = ContextCompat.getColor(
this, R.color.colorDarkBlue)
ChangeStyle.setTextColor(textColor)
ChangeStyle.setBackgroundColor(backColor)
}

In this case, you retrieve the color you need using ContextCompat.getColor() from colors.xml. You then apply it to the correct color style using either setTextColor() or setBackgroundColor(). Note that you can also manually create one-off color changes by using the rgb() or argb() functions, like this:

ChangeStyle.setBackgroundColor(
argb(0xFF, 0x00, 0x00, 0xA0))

Technical Stuff Most changeable styles for a view work use the same two-step process. However, some styles aren't changeable through programming, such as the style attribute for a Button. In this case, you must choose a drawing style during design time and stick with it, or resort to programming tricks like creating two buttons, showing one, and hiding the other as needed.

Creating a palette

The Palette API enables developers to create color schemes based on the graphics used by your application. Making your app color coordinate with the graphics helps create a pleasing view for the user. For example, graphics used for a school will likely contain the school colors. A palette based on the graphics will also use the school colors natively, along with any other elements in the graphics to create a pleasing app for school needs. To begin using the Palette API, add the following implementation entry to the dependencies section of the build.gradle(Module: app) file:

implementation 'androidx.palette:palette-ktx:1.0.0'

To create a palette, you begin with a bitmap. However, most apps save bitmaps as a drawable resource, which means performing a conversion. The following code shows how to obtain a palette from the pep_extracheese.png file used in Book 1, Chapter 6.

val myPalette = Palette.from(
BitmapFactory.decodeResource(
resources,
R.drawable.pep_extracheese)).generate()

That's all there is to it! You now have access to a palette that you can use within your app to create a pleasing appearance. To assign a color to one of the controls, you use code like this:

SomeText.setTextColor(
myPalette.getDarkVibrantColor(1))
SomeText.setBackgroundColor(
myPalette.getLightVibrantColor(1))

The IDE guides you through which functions to use. The value you provide is simply a selection of one of the colors. You can try different numbers to see which colors appeal to you. You can view the palette colors as being of these types based on the bitmap you provide:

  • LightVibrant
  • Vibrant
  • DarkVibrant
  • LightMuted
  • Muted
  • DarkMuted

Using swatches to create color schemes

The Palette object you created in the previous section also has swatches, which are essentially color profiles based on the bitmap you provide. Like the individual color selections, these profiles come in various groups, such as DarkMuted. Consequently, you access a swatch like this:

val mySwatch = myPalette.darkMutedSwatch

The essential use for swatches is to create themes. You also gain access to new methods that let you choose a color based on how the algorithm sees it being used, such as getBodyTextColor() and getTitleTextColor(). Finally, swatches help you perform analysis on your Palette in preparation for tweaks you may want to make.

Using Animations and Transitions

Playing games can be time consuming — a real waste of resources that most businesses try to avoid. However, games are actually quite good at teaching developers about the use of animations and transitions. For example, a card game can use an animation to suggest a next move when the user hasn't made a move in a while. An action game might use a transition to provide different moves as the user navigates a maze. Business software could take a page from games and use animation to demonstrate what to do next when the user seems stuck. Transitions can give the user a feeling of accomplishment when data is accepted.

Fortunately, the controls and components you use to create Android apps sometimes have the animations and transitions built right into them so that you don’t even have to do anything special. For example, a progress bar might have an animation feature to make the movement from one level to the next smoother and more likely to be seen by the user.

This section relies on an app named 03_05_03 to get you started with animations and transitions. Begin with the Empty Activity without any controls or components added. Set the minimum API level to 21. The following sections offer a few ideas on how to use animations and transitions to your advantage in developing apps.

Understanding the need for animations

When most people think about animation, they think about cartoons or games, but those aren’t the only place where animation is commonly used, and the other ways can be quite interesting. For example, in pointing where to go, a sign can employ animation to attract attention and make the direction clearer. Animations can also smooth out visual activities so that the activity is easier to follow and easier on the eyes. An animation can help make a process clearer, and you sometimes see them used for step-by-step procedures, such as for putting something together. So, it shouldn’t come as a surprise that you can use animations in your app as well and for the same reasons you see them used everywhere else.

Some of the Android controls use animation by default, such as the ProgressBar. As shown in the “Creating a test app” section in Chapter 2 of this minibook, you can use coding techniques to make the ProgressBar movement smoother. You can also set the animationResolution attribute to make the movement smoother. The point is that with a proper setup, a ProgressBar comes ready to provide some level of animation for the user. The SeekBar provides this same functionality so that when you use it for a music app, the thumb (the selector you use to modify the time index) moves smoothly as the music plays.

Android also supplies you with some widgets that handle animation natively, such as the VideoView and the ImageView. Both controls offer a variety of animation techniques through careful programming. Animating these controls lets you put on an unattended video presentation, such as at a kiosk. Animation relieves the user from having to click from picture to picture, ensuring that the user will tend to view more of the presentation.

Animating graphics

You have various means to animate graphics. This example looks at what amounts to a simple slide show.

Creating the animation list

To begin, you must create an animation list resource using the following steps:

  1. Right-click the res folder and choose New⇒  Android Resource File.

    You see a New Resource File dialog box, similar to the one shown previously in Figure 5-1.

    Remember Be sure to right-click the res folder and not the drawable folder or you'll create an unusable file.

  2. Type star_animation in the File Name field.

    Use a lowercase name or Android Studio will reject it.

  3. Choose Drawable in the Resource Type field.
  4. Type animation-list in the Root Element field and click OK.

    This entry replaces selector or some other entry as the root. The dialog box automatically shows a list of options based on what you type. Android Studio creates the required resource file for you.

This example uses some built-in graphics so that no one has to draw anything. These built-in graphics come in handy for testing purposes when you aren’t an artist and stick figures hold little interest. With this in mind, here’s the code for the resource file:

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

<item
android:drawable="@android:drawable/btn_star"
android:duration="300"/>
<item
android:drawable="@android:drawable/btn_star_big_off"
android:duration="300"/>
<item
android:drawable="@android:drawable/btn_star_big_on"
android:duration="300"/>
</animation-list>

The <animation-list> element includes one essential attribute, android:oneshot. Setting this attribute to true means that you see the animation only once. Because the example will keep the animation going until told to stop, you set this attribute to false.

Tip Each item in the <animation-list> is a separate bitmap found in the @android:drawable resource. You can find a lot of useful bitmaps this way, and this resource helps when your artistic skills are lacking. In addition to the android:drawable attribute, which points to the bitmap, you must also provide an android:duration attribute that tells how long to display each bitmap.

Defining a user interface

This example has a simple user interface consisting of an ImageView and a Button. The ImageView displays the animation and the Button provides the means for starting and stopping the animation. Here's the setup you need for the example:

<?xml version="1.0" encoding="utf-8"?>
<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"
tools:context=".MainActivity">

<ImageView
android:id="@+id/imageView"
android:layout_width="350dp"
android:layout_height="350dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>

<Button
android:id="@+id/StartStop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="400dp"
android:layout_marginEnd="8dp"
android:onClick="@string/StartStopClick"
android:text="@string/StartStopText"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>

Note that the ImageView is sized to 350 x 350 pixels. This size is for demonstration purposes only. Normally, you’d configure the app to work with the target device.

Starting the animation

You can create an animation using very little code. All you really need is to tell the ImageView what resource to use, create an AnimationDrawable, and tell the resulting object to start() or stop() the animation, as shown in the following code:

fun onStartStopClick(view: View) {
imageView.setBackgroundResource(
R.drawable.star_animation)
val frameAnimation: AnimationDrawable =
imageView.background as AnimationDrawable

if (StartStop.text == "Start") {
frameAnimation.start()
StartStop.text = "Stop"
} else {
frameAnimation.stop()
StartStop.text = "Start"
}

}

Communicating with Emoji

Emoji communicate complex emotions, events, ideas, and even words using simple symbols. Because humans have a vast range of emotions, a large number of emoji exist. Of course, you're most interested in the emoji that Android apps support, and you can find them at https://emojipedia.org/google/ (3,066 at the time of this writing). This list is updated regularly, so if you don’t see the emoji you want now, you’ll likely see it later.

This section relies on an app named 03_05_04 to provide glimpses into working with emoji in Android. The app doesn’t do anything special except show off what’s available to you as the developer. So, begin with the Empty Activity without any controls or components added. Set the minimum API level to 26. The following sections tell you about various forms of emoji support in Android.

Keyboard emoji support

Many Android components and controls support emoji natively. You access them using the keyboard, as shown in Figure 5-10 using the 03_04_02 app from Chapter 4 of this minibook. This app doesn’t do anything special to provide emoji support; it comes as part of the package. The user just clicks the smiley face at the bottom of the keyboard.

A mobile screen displaying a list of emojis for selecting a particular emoji using the keyboard in an Android app.

FIGURE 5-10: Accessing emoji using the keyboard in an Android app.

The emoji automatically appear in any control where you can type them, as shown in Figure 5-11. As you can see in the figure, the emoji automatically resize themselves as needed to provide a good presentation onscreen. The emoji tend to be a little larger than the text, so you need to give controls that use them a little more space onscreen.

A mobile screen displaying a Happy emoji in a TextView on the blank screen.

FIGURE 5-11: Displaying emoji in a TextView.

Using the cut-and-paste method on standard controls

If you want to use standard emoji in a static way in your app, you can copy and paste emoji into your app using a standard control, such as a button, using the strings.xml file. One site that makes this practice incredibly easy is https://www.emojicopy.com/. You simply select the emoji you want to use, click Copy, and then paste the emoji into your strings.xml file, as shown in Figure 5-12.

Tip The appearance of the emoji in the strings.xml file isn't all that impressive, but Figure 5-13 shows the results onscreen. This is a standard button — nothing fancy added. The textAppearance property is set to Display2 in this case. So, you get a large button that has all the standard functionality with a friendly grinning cat simply by cutting and pasting. Simple solutions are often the best.

Illustration of the <resources> section for copying and pasting emoji into your strings.xml file.

FIGURE 5-12: Copying and pasting emoji into your strings.xml file.

A mobile screen displaying the result of cutting and pasting in a standard button.

FIGURE 5-13: Viewing the result of cutting and pasting in a standard button.

Using the AndroidX approach

Android supports several levels of programmatic emoji using Unicode characters. The two basic choices are

Remember The problem with using the bundled fonts is that you may not have access to the latest set of emoji. Here are problems with using the downloaded support:

  • You must have an Internet connection to use this approach.
  • Downloading the fonts can delay app appearance onscreen.
  • It's more complicated.

Because using bundled fonts is straightforward and will meet your needs most of the time, the example in this section relies on bundled fonts. However, the techniques shown also provide a good starting point for using downloaded fonts. Adding full emoji support to your app begins with an addition to the dependencies section of the build.gradle (Module: app) file, shown here:

implementation 'androidx.emoji:emoji-bundled:1.0.0'

If you wanted to use downloaded fonts instead, you’d include this entry in the dependencies section of the build.gradle (Module: app) file:

implementation 'androidx.emoji:emoji:1.0.0'

Warning You can't use both entries; you must decide on using bundled fonts or downloaded fonts for the application as a whole.

After you sync your app with the new addition, you have access to a number of emoji-specific features, including a series of controls. You must type the controls in the XML because they don’t appear in the Palette. Figure 5-14 shows the controls in question.

Screenshot providing access to a number of emoji-specific features, including a series of controls in the Android Studio.

FIGURE 5-14: Accessing the special emoji controls.

The example uses an EmojiButton widget so that you can compare the cut-and-paste method to the Unicode approach. Here are the configuration options for the EmojiButton:

<androidx.emoji.widget.EmojiButton
android:id="@+id/GrinButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="120dp"
android:text="@string/GrinButtonText"
android:textAllCaps="false"
android:textSize="44sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>

The GrinButtonText entry in strings.xml looks like this:

<string name="GrinButtonText" >
&#x1F638; Grin &#x1F638;
</string>

Notice how the Unicode character appears. If you don't include the #, you get an unresolved entity reference error. If you don’t include the x, you see an unescaped & character or nonterminated character error.

Tip You can obtain a full list of emoji, along with their hexadecimal Unicode values, at https://unicode.org/emoji/charts/full-emoji-list.html.

Before you can use the fonts, you must load them, even if they're included with your app. This means adding the following code in bold to the onCreate() for your app:

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

val config: EmojiCompat.Config =
BundledEmojiCompatConfig(this)
.setReplaceAll(true)
.setEmojiSpanIndicatorEnabled(true)
.setEmojiSpanIndicatorColor(Color.BLUE)
EmojiCompat.init(config)

setContentView(R.layout.activity_main)
}

Remember Notice that you must load and configure the fonts before you call setContentView(). Otherwise, the app won't load at all and you continuously see EmojiCompat.init() error, with little explanation of why it occurs. Now that you have all the code in place, you can run your app and compare the two versions of the emoji button, as shown in Figure 5-15.

A mobile screen for comparing pasted emoji to Unicode emoji, the two versions of the emoji button.

FIGURE 5-15: Comparing pasted emoji to Unicode emoji.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.238.82.77