Chapter 5
IN THIS CHAPTER
Working with layout elements
Using color in a layout
Adding action to the user interface
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.
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.
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.
ListView
GridView
Spinner
Gallery
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
.
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.
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.
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"/>
You can use a variety of methods to modify the layout at runtime. However, in most cases, the methods employ these techniques:
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.
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.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.
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:
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.
Choose Tools⇒ Layout Inspector in Android Studio.
You see the Choose Process dialog box, shown in Figure 5-4.
Highlight the process you want to inspect and click OK.
You see the window shown in Figure 5-5, which has the following panes:
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.
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.)
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.
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.
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>
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
.
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.
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.
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.
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))
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
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.
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.
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.
You have various means to animate graphics. This example looks at what amounts to a simple slide show.
To begin, you must create an animation list resource using the following steps:
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.
Be sure to right-click the res
folder and not the drawable
folder or you'll create an unusable file.
Type star_animation in the File Name field.
Use a lowercase name or Android Studio will reject it.
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
.
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.
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"
}
}
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.
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.
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.
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.
Android supports several levels of programmatic emoji using Unicode characters. The two basic choices are
https://developers.google.com/fonts/docs/android
).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'
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.
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" >
😸 Grin 😸
</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.
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)
}
3.238.82.77