Chapter 6
IN THIS CHAPTER
Improving the look of an app
Helping people in other countries use your app
Getting your app to communicate with the outside world
Face it — the app in Chapter 5 of this minibook is boring! Really boring! Who wants to click a button to see the words Pepperoni and Extra Cheese on a device's screen?
In this chapter, you improve on the app that you created in Chapter 5. We can't promise instant excitement, but with modest effort, you can add features to make the app more interesting. (We confess: In this chapter, the real reason for making the app interesting is to show you some additional Android developer tricks. Anyway, read on …)
In addition to being boring, the app in Chapter 5 is ugly. You can improve an app's look in two ways: the way it looks to a user and the way it looks to another developer. In this section, you do both. When you're done, you have a layout like the one in Figure 6-1.
Before creating the layout's code, you should make some observations about the layout in Figure 6-1:
You may wonder why you should make these six observations and not others. If so, read on.
You can use the various layouts either singularly or in groups to make designing your app easy. How you use the layouts will determine how your app appears onscreen — how the components align themselves. Think about organizing your closet. If you use a combination of shelves and clothes racks, you get one appearance. However, you might have tons of shoes and other small items, so cubbies might be useful. Drawers might come in handy for tiny items. If you need long-term storage, you might add some covered trays. Just as with a closet, you use Android layouts to provide specific results when working with components:
ConstraintLayout
by defining a constraint for it. This is the default layout and often acts as a container layout for other layouts (as you see in this chapter).ConstraintLayout
to provide horizontal or vertical alignment for a group of components. Think about it as drawing a line on the screen for positioning the items you need. You don't see the Guideline
when viewing the app.LinearLayout
components to get specific component organizations.TableLayout
is the entire table, while a TableRow
is a single row within that table.Understand that the layouts described in this section aren’t exclusive of each other. You might start with a ConstraintLayout
to organize the screen as a whole, use a LinearLayou
t for some overview information, and have a TableLayout
for details. The layouts nest inside each other to give you precise control over your app's appearance on any device. Using layouts means that the organization of your app will change to meet the constraints of a device automatically, so you don’t have to worry about a smartphone being overwhelmed by an app that is usually used on a tablet. Of course, everything has limits, and you still need to test whether a layout is successful in creating a useful app. The following steps help you change the layout for this example:
Launch Android Studio and create a new Android project with an app name of 01-06-01
and a package name of com.allmycode.p01_06_01
.
For details on creating a new project, see Chapter 3 of this minibook.
In the Designer tool's preview screen, select the Hello World! TextView
and press Delete.
The Hello World! TextView
is deleted.
Drag a LinearLayout (Vertical)
from the Layouts group of the Palette window to the location under the ConstraintLayout
in the Component Tree.
The LinearLayout (Vertical)
automatically indents under the ConstraintLayout
, showing that it's nested within the parent layout.
Notice the yellow triangle next to the LinearLayout (Vertical) entry. This triangle currently tells you that the layout is useless because it has no children. Of course, you’ll make it happy by adding children later.
Not noted is that you still have to provide constraints for a layout. If you don’t provide the constraints, the designer will complain later.
LinearLayout (Vertical)
to 0
using the Layout section of the Attributes window, as described in the “Adding constraints” section of Chapter 5 of this minibook.Click the Design button.
Note that the Component Tree now shows a LinearLayout
in addition to the ConstraintLayout
, as shown in Figure 6-2. The ConstraintLayout
is now acting as a container for the LinearLayout
, but the LinearLayout
is actually controlling the appearance of the components onscreen. The yellow triangle is in place because we still haven’t added children.
It's time to add components to the new layout. The most important thing you notice is that the layout process is different from that used for the examples in the previous chapters of this minibook. The results you see onscreen are different as well, and components like buttons suddenly have different attributes — all because you changed layouts!
To add components, follow these steps:
Drag a Button
component from the Buttons group of the Palette window onto the LinearLayout (Vertical)
component in the Component Tree.
Voilà! Your app has a button. However, notice that the button consumes the entire width of the app. You'll fix this problem later so that the button looks like the one in Figure 6-1.
Next, you want something that aligns objects horizontally so that the check boxes are side by side.
Drag a LinearLayout (Horizontal)
component from the Layouts group of the Palette window directly under the Linear Layout (Vertical)
in the Component Tree.
Try to place the horizontal linear layout component above the button. The LinearLayout (Horizontal)
and Button
components should be at the same level as shown in Figure 6-3. The IDE will try to place the button within the LinearLayout (Horizontal)
branch, but you should drag it back to its correct position.
It pays to look at the two yellow triangles currently in the Component Tree. The first tells you that the LinearLayout (Horizontal)
component is useless because it lacks children. The second tells you that you should use a string resource for the Button
component. Later steps address both issues. (Notice also that the yellow triangle next to the LinearLayout (Vertical)
component is gone.)
Oddly enough, the button will seem to have disappeared — it actually appears under the horizontal linear layout. Because the horizontal linear layout is currently using the entire display area, the button appears off screen below it. You'll fix this problem later as well.
Next, you put the check boxes into your horizontal linear layout.
Drag a CheckBox
component from the Palette window’s Buttons group to the horizontal linear layout element in the preview screen.
If you have trouble dragging the component inside the horizontal linear layout, place the check box anywhere on the preview screen. Then, in the Component Tree, drag the checkBox
component so that it's subordinate to the LinearLayout (Horizontal)
branch.
Drag another CheckBox
component from the Buttons group of the Palette window to the horizontal linear layout element in the preview screen.
Like the first check box, this second check box is inside your horizontal linear layout. The two components are side by side. Perhaps they're uncomfortable being that close and need a little space.
Drag a Space
component from the Layouts group of the Palette window to place it between checkBox
and checkBox2
.
Ahhh! Able to breathe again.
As things stand now, you can see the two check boxes with a space between them, but the button seems to have disappeared. Yes, it's still there — just hiding. To make the layout useful, you need to make a few adjustments, including getting rid of those yellow triangles.
To make the needed adjustments, follow these steps:
In the Component Tree, select the LinearLayout (Vertical)
component.
You can try selecting this element in the preview screen but, for an element with nothing but a border, doing the selecting in the Component tree is easier.
In the Attributes window, locate the gravity
entry in the All Attributes group. Expand this entry to reveal the underlying settings and select the center_horizontal
entry.
The value will change to true
. This attribute controls the positioning of components that don't take up the entire width of the parent area. If you don’t select this attribute, the components will appear on the left side of the app.
LinearLayout (Horizontal)
component.In the Attributes window, locate the layout_height
entry in the Declared Attributes group and set it to wrap_content
.
Okay, there's that pesky button. However, it takes up the entire width of the app and is too close to the two check boxes.
In the Attributes window, locate the layout_width
entry in the Declared Attributes group and set it to wrap_content
.
The button is now centered onscreen and the right size. However, it's still too close to the check boxes.
In the Attributes window, locate the layout_margin
entry in the All Attributes group. Expand this entry and type 10dp in the layout_marginTop
entry.
The button is now a respectable distance from those check boxes.
You may wonder what the 10dp
business is all about. It means to set the start of the button to ten density-independent pixels (dp) from the bottom of the previous component. Mobile device designers use the dp because different displays have different densities. For example, the Samsung Galaxy S5, S6, and S7 all have 360 x 640 dp displays, even though the actual resolution of the S5 is 1080 x 1920 pixels and the S6 and S7 both are 1440 x 2560 pixels. Using dp simplifies design and ensures that each display shows whatever you have created in the same manner. If you really want to know more, the article at https://medium.com/mockingbot/why-ui-designers-are-using-dp-instead-of-pixel-as-unit-to-design-mobile-app-2c080f90936b
covers this issue in significantly more detail.
Change the text on the check boxes and the button so that it matches what you see in Figure 6-1 by clicking the box in the text attribute in the Attributes window. Make sure you use a string resource as described in the “Adding text to each component” section of Chapter 5 of this minibook.
Don't worry about the word Plain in Figure 6-1. You work on that in the section “Reusing a layout,” later in this chapter.
When setting the button
text, be sure to add the android:textAllCaps="false"
entry by switching to Text mode as described in Chapter 5.
Set the onClick
property of the button
(found in the Attributes window) to a resource string named Button1Click
with a value of onButtonClick
.
For help setting the onClick
property, see Chapter 5 of this minibook. Figure 6-4 shows how the Component Tree should appear at this point.
The check boxes and the button in Figure 6-1 are useful in more than one situation. You might place these widgets in an app with a confirmation word (such as the word Plain in Figure 6-1). You might use the same widgets in a different app with a picture of a pizza below the widgets. One way or another, it's worth your while to save the layout containing these widgets. You save these widgets in a new layout resource document (a blahblah
.xml
document in the res/layout
folder). This section tells you how to do that.
In the Component Tree, right-click Linear Layout (Vertical)
and choose the Refactor submenu.
Make sure that you see the outline of the layout that contains both check boxes and the button.
A context menu containing the Style and Layout options shown in Figure 6-5 appears. If you don't see this list of options (or if they’re different in some way), you may not have the right item selected.
In the context menu, select Extract Layout.
The Extract Android Layout dialog box shown in Figure 6-6 appears.
In the dialog box's File Name field, type the name of your new resource document.
The example uses reusable_layout.xml.
The names of Android's resource files must not contain capital letters. You can use lowercase letters and underscores. You cannot use Java's or Kotlin’s customary “camelCase” naming convention with names like reUsableLayout.xml
. And, yes, a layout filename must end with the extension .xml
.
Click OK to close the dialog box.
The app/res/layout
branch in the Project tool window now has a new item. If you named the file as described in Step 4, the branch is labeled reusable_layout.xml
.
Double-click the reusable_layout.xml
branch in the Project tool window.
Android Studio displays the Designer tool for the reusable_layout.xml
file.
Make sure that the Designer tool is in Design mode (as opposed to Text mode).
In Design mode, you can see the Component Tree.
Make note of the labels on the branches in the Component Tree.
Look for names like checkBox
, checkBox2
, and button
. (See Figure 6-7.) You use these names (these id values) in the code that you write later in this chapter.
To change an element's id, select the component in the Component Tree. Change whatever is entered in the id
text field in the Attributes window.
Your current app automatically uses the layout you just saved. Look in the Text tab of activity_main.xml
and you see that the code has changed to look like this:
<?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">
<include layout="@layout/reusable_layout"/>
</androidx.constraintlayout.widget.ConstraintLayout>
The <include layout="@layout/reusable_layout" />
element places the layout you just saved in the specified place on the app screen. Congratulations! You have a group of widgets that you can use and reuse.
In the preceding section, “Creating a reusable layout,” you create a layout with check boxes and a button. You can reuse this layout in many of this chapter's examples. Here's how:
The first thing you need to do is copy the required files from the initial project (from the earlier section “Creating a reuseable layout”) to the new project in which you plan to use the reusable layout in a new application. Follow these steps:
Follow the steps in the “Creating a reusable layout” section if you haven’t already done so.
If you're impatient, you can skip a few of that section's steps, but be sure to create a reusable_layout.xml
file and to populate the file with a few widgets.
reusable_layout.xml
file.In Android Studio's main menu, choose Edit ⇒ Copy.
You don't see much happening, but now your Clipboard contains a copy of the reusable_layout.xml
file.
Without closing the existing project, start a new Android project by choosing File⇒ New⇒ New Project.
Give the new project the name 01_06_02
and a package name of com.allmycode.p01_06_02
. Make sure you delete the Hello World TextView
component as well.
app/res/layout
branch.In Android Studio's main menu, choose Edit ⇒ Paste.
You see the Copy dialog box, shown in Figure 6-8.
Click OK.
Now the app/res/layout
branch contains a reusable_layout.xml
file. In addition, Android Studio automatically opens a copy of the file for you.
Perform Steps 5 through 7 for the strings.xml
file located in the app/res/values
branch of the initial project. However, in this case, rename the file copied_strings.xml
in the new project by typing the new name in the New Name field of the Copy dialog box.
The reusable layout you created relies on resource strings you defined. To keep the layout functional, you need to provide a source for those strings in the new application.
Using a separate file keeps the reusable layout string resources separate from those used for the current project. This approach makes it easier to locate specific strings when you need to.
Open copied_strings.xml
and delete <string name="app_name">01_06_01</string>
.
If you leave this particular string resource in place, the compiler complains about a duplicate resource. Plus, the app name will be wrong!
Now that you have the required files, you can use the reusable layout to create the beginnings of an app and then complete the app using other components.
Open your project's res/layout/activity_main.xml
file.
When you do, you see your new project's preview screen (which is mostly empty).
Delete the Hello World TextView
.
This project is going to be stacking components, so you need a LinearLayout (Vertical)
component to hold the components.
LinearLayout (Vertical)
component under the ConstraintLayout
in the Component Tree window.LinearLayout (Vertical)
component and set the four constraints to 0
.In the Attributes window, locate the gravity
entry in the All Attributes group. Expand this entry to reveal the underlying settings and select the center_horizontal
entry.
At this point, your LinearLayout (Vertical)
component is ready to receive the reusable layout and the TextView
you need to show the selections.
In the Containers group of the Palette window, drag the <include>
item to below the LinearLayout (Vertical)
in the Component Tree.
When you do this, a Resources dialog box appears, as shown in Figure 6-9.
In the Resources dialog box, select your reusable layout — the one you named reusable_layout.xml
; then click OK.
The Resources dialog box closes.
Click include
in the Component Tree.
As if by magic, the stuff that you created in the “Creating a reusable layout” section appears on the preview screen. (Well, anyway, it looks like magic to us.) This stuff appears as one group.
In the Declared Attributes group of the Attributes window, choose match_parent
in the layout_width
field and wrap_content
in the layout_height
field.
These settings ensure that the reusable layout takes up the appropriate amount of space in the upper portion of the screen.
If all goes well, your layout looks like the stuff in Figure 6-1.
In the next step, you add the word Plain to your app's screen. (Refer to Figure 6-1.)
TextView
component to a location under the include
component in the Component Tree.textView
in the Component Tree.In the Declared Attributes group of the Attributes window, choose wrap_content
in both the layout_width
and the layout_height
fields.
The textView
should appear in the middle of the screen directly under the reusable layout.
textView
placeholder text with a resource string named TextView1Text
that has a value of Plain
.(Optional) If you're very ambitious, follow the steps in Chapter 5 of this minibook for coding your app's behavior. Then run your app.
Ambitious or not, you have a decent-looking layout with a reusable component. Nice work!
As we mention in Chapter 4 of this minibook, an Android activity is one “screenful” of components. So juggling activities is a major endeavor for Android developers. This section's example does the simplest thing you can do with an activity — namely, make it run.
Launch Android Studio and create a new project.
This example uses an app name of 01_06_03
and a package name of com.allyourcode.p01_06_03
.
Follow the instructions in the earlier section “Reusing a layout” to include reusable_layout
on your new app's screen.
You have two check boxes and a button. When a user clicks the button, you want Android to display a different activity's screen. So you have to create another activity.
Let's get cracking …
In the Project tool window, right-click or (on a Mac) Ctrl-click your project's app/java/
your.package
branch.
In Listing 6-1, the package name is com.allyourcode.p01_06_03
. If you used the same package name, you'd right or Ctrl-click your project's app/java/com.allyourcode.p01_06_03
branch.
In the context menu that appears, choose New ⇒ Activity ⇒ Empty Activity.
The Configure Activity dialog box, shown in Figure 6-10, appears. (You see this dialog box whenever you create a new, blank activity.)
In the dialog box, fill in the Activity Name and Layout Name fields.
Listings 6-1 and 6-2 refer to OtherActivity
and other_layout
. So, if you're following along letter for letter with these instructions, type OtherActivity in the Activity Name field, and type other_layout in the Layout Name field. You can accept the defaults for all the other fields in the dialog box.
Click Finish to close the dialog box.
Your new other_layout
now appears in Android Studio's Designer tool. (You may need to click the other_layout tab to see the layout.)
app/manifests/AndroidManifest.xml
file, notice the following code:
<activity android:name=".OtherActivity">
</activity>
Android Studio adds this <activity>
element when you create OtherActivity
in Steps 3 through 6.
Each activity in your application must have an <activity>
element in the AndroidManifest.xml
file. In an <activity>
element, the android:name
attribute points to the name of the activity's Kotlin class. In this step, the attribute's value is ".OtherActivity"
. The initial dot refers to the application's package name (the name com.allyourcode.p01_06_03
from Step 1). The rest of the attribute refers to the class name in Listing 6-2.
Each activity in your application must have an <activity>
element. If you're missing an <activity>
element, the app can't start that activity. (Instead, the app crashes, and you see an Unable to find explicit activity message in the Logcat panel.)
With other_layout.xml
showing in the preview screen, drag a TextView
component from the Palette onto other_layout
.
Now, other_layout
has a TextView
element.
Configure the TextView
component as you have in the past by setting constraints for it and using a string resource to set the default text to Plain
.
Your project has two activities (two Java files) and a layout for each activity (two XML files in the Project view's app/res/layout
branch). In addition, your project has a reusable_layout
that's included inside your main activity's layout. You can switch back and forth between the two activities and their layout files, but try to be mindful of the switching. Try not to become confused by editing the wrong Java code or the wrong layout file.
Look for your new TextView
element in the Component tree.
Make note of the label on that element's branch of the tree. In the example's version of the app, the label is textView
.
If you switch momentarily to the Designer tool's Text mode, you see the attribute android:id="@+id/textView"
inside the TextView
tag. The id
of this element is textView
.
To change an element's id
, select its entry in the Component Tree. Locate the id
field in the Attributes window. Change whatever is entered in that text field.
Modify your main activity's code, as shown in Listing 6-1.
The listing shows the code to be added in boldface type.
Double-check the expressions R.id.checkBox
and R.id.checkBox2
in Listing 6-1 against the names in the Component tree at the end of the “Creating a reusable layout” section. If the Component tree's labels aren't checkBox
and checkBox2
, change your Listing 6-1 code appropriately.
In the MainActivity
(Listing 6-1), you have code that starts up the OtherActivity
. You don't start an activity by calling the activity's methods. Instead, you create an intent. An intent is like an open-ended method call. In Listing 6-1, you create an explicit intent — an intent that invokes a specific class's code, as follows:
OtherActivity
(or whatever you name your app's second activity).checkBox.isChecked()
is true
, so the intent contains the extra pair "Pepperoni", true
.startActivity(intent)
invokes the OtherActivity
class's code.Next up, your OtherActivity
should have some code that responds to the fact that OtherActivity
was started.
In your new OtherActivity
class, add the code in Listing 6-2.
The listing shows the code to be added in boldface type.
Listing 6-2 assumes that the TextView
in the other_layout.xml
file is textView
. (That is, the code assumes that, in the XML file itself, the TextView
element has an attribute that reads android:id="@+id/textView"
.) If this TextView
element has an id
other than textView
, change the code in Listing 6-2 accordingly.
In Listing 6-2, by calling intent.getBooleanExtra()
, the OtherActivity
discovers the values of checkBox.isChecked()
and checkBox2.isChecked()
from Listing 6-1. For example, the call
intent.getBooleanExtra("Pepperoni", false)
returns true
if the value of checkBox.isChecked()
in Listing 6-1 is true
. The call returns false
if the value of checkBox.isChecked()
in Listing 6-1 is false
. The call's second argument is a default value. So, in Listing 6-2, the call to intent.getBooleanExtra("Pepperoni", false)
returns false
if the intent created in Listing 6-1 doesn't have an extra named "Pepperoni"
.
Run your app.
When you click the app's button, you see a new activity like the one pictured in Figure 6-11.
LISTING 6-1: Starting OtherActivity from the MainActivity
package com.allmycode.p01_06_03
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import kotlinx.android.synthetic.main.reusable_layout.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
fun onButtonClick(view: View){
val intent = Intent(this,
OtherActivity::class.java)
intent.putExtra("Pepperoni",
checkBox.isChecked())
intent.putExtra("Extra cheese",
checkBox2.isChecked())
startActivity(intent)
}
}
LISTING 6-2: The OtherActivity
package com.allmycode.p01_06_03
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.other_layout.*
class OtherActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.other_layout)
val builder = StringBuilder()
if (intent.getBooleanExtra("Pepperoni",
false)){
builder.append("Pepperoni")
}
if (intent.getBooleanExtra("Extra cheese",
false)){
if (builder.length == 0) {
builder.append("Extra Cheese")
} else {
builder.append(" and Extra Cheese")
}
}
if (builder.length == 0) {
builder.append("Plain")
}
textView.setText(builder)
}
}
Words, words, words. The apps in this chapter have so many words. "Pepperoni"
here; "Extra cheese"
there! It's a wonder a developer can keep this stuff straight. It's too easy to type a word one way in one part of the code and misspell the word in a different part.
The example application currently supports just one language, likely English. In a world where the United States ranks third of all countries where cellphones are used (see https://www.infoplease.com/science-health/cellphone-use/cell-phone-usage-worldwide-country
or https://en.wikipedia.org/wiki/List_of_countries_by_number_of_mobile_phones_in_use
for details), you don't want to make assumptions about your audience.
Fortunately, you already have a good start on solving this problem because the example application relies on string resources rather than hard-coded text. Even so, you still need to add the text for the languages you want to support in your app. Here's what you do:
strings.xml
file in your project's res/values
folder in the Project tool window.In the context menu that appears, select Open Translations Editor.
The Translations Editor appears in place of the Designer tool. (See Figure 6-12.)
Near the top of the Translations Editor, click the globe icon.
A list of language locales appears. (See Figure 6-13.)
Select a language locale from the list.
For this exercise, we select Italian (it). (We'd be disloyal to our buddies Steve and Luca if we did otherwise.) For the full scoop on language locales, visit https://www.iso.org/iso-3166-country-codes.html
.
As a result, the strings.xml
branch in the Project tool window now has two sub-branches. Both sub-branches sport the label strings.xml
, but the new sub-branch's icon is a tiny picture of the flag of Italy. (See Figure 6-14.)
Temporarily change the Project tool window from Android view to Project view. Your project's app/src/main/res
folder now has a values
subfolder and a values-it
subfolder. The values-it
subfolder contains its own strings.xml
file. (Okay, you can go back to the Android view now!)
In the Translations Editor, the term extra_cheese
is in red because you haven't yet translated extra_cheese
into Italian. The same is true for other terms that you haven't yet translated.
Click the Italian (it) column in the Checkbox2Text
row. In that column, type Con più formaggio
and then press Enter.
(Sorry. The Translations Editor doesn't do any translating for you. The Translations Editor only adds code to your project when you type in the translations of words and phrases.)
Repeat Step 4 for the app_name
, Checkbox1Text
, Button1Text
, and TextView1Text
rows.
If your Italian is a bit rusty, copy the text from the res/values-it/strings.xml
file in Listing 6-3.
ELENCO 6-3 Benvenuto in Italia!
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Il mio secondo progetto Android</string>
<string name="Checkbox2Text">Con più formaggio</string>
<string name="Checkbox1Text">Merguez</string>
<string name="TextView1Text">Semplice</string>
<string name="Button1Text">Mostra</string>
<string name="Button1Click">onButtonClick</string>
</resources>
Copy the Button1Click
value row to the Italian (it) column.
The viewer will never see the native language value of onButtonClick
in this row, but Android won't react well if you change this particular string to Italian.
Test your app.
As with most devices, the emulator has a setting for Language & Input. How you access this setting depends on the particular emulator, but in some cases you must add the language you want to use before you can use it, as shown in Figure 6-15. In addition, you usually have to set the language you want to use as the primary language.
Change the language setting of your emulator to Italiano (Italia).
Suddenly, your app looks like the display in Figure 6-16.
Why click twice when you can do the same thing by clicking only once? Think about the example in the previous section. Your app responds to the contents of check boxes when the user clicks a button. In a streamlined scenario, your app might respond as soon as the user selects a box. Figure 6-17 shows a variation of the app you’ve been using, but this one is missing the Show button. It does, however, have the two check boxes and the text box, which is configured the same as the Show button it replaces. The setup for this app is the same as the one used for the reusable layout (see the “Changing the layout” section, earlier in this chapter, for details), just without the Show button.
Listing 6-4 shows you how to make this happen. Just add onCheckboxClick()
to your MainActivity.kt
file. You can also find this example in the 01_06_04
folder of the downloadable source.
LISTING 6-4: Responding to CheckBox Events
fun onCheckboxClick (view: View)
{
val builder = StringBuilder()
if (checkBox.isChecked()){
builder.append("Pepperoni")
}
if (checkBox2.isChecked()){
if (builder.length == 0){
builder.append("Extra Cheese")
} else {
builder.append(" and Extra Cheese")
}
}
if (builder.length == 0){
builder.append("Plain")
}
textView.setText(builder)
}
Like a button, each check box listens for onClick
events. So you can write this section's code very much like the code in Listing 5-3 in Chapter 5. As with that example, you must declare a single input argument, view: View
. The output text appears within a StringBuilder()
.
After designing an app and its variations in the previous sections, you may decide that your app needs some flair. How about designing your app so that when a user clicks a button, your app displays a picture of the pizza being ordered? The Show button in Figure 6-1 is perfect for this.
Android has all kinds of features for drawing images and displaying bitmap files. Here is one possible approach:
Launch Android Studio and create a new project.
The example in this section uses a project name of 01_06_05
, and a package name of com.allmycode.p01_06_05
.
reusable_layout.xml
file from “Creating a reusable layout” section, earlier in this chapter, to your new project's app/res/layout
branch in the Project tool window.Include reusable_layout.xml
and copied_strings.xml
files in your project.
For details, see the “Reusing a layout” section of the chapter.
Find four images — one for plain, one for pepperoni, one for extra cheese, and one for pepperoni with extra cheese.
Android's official documentation recommends the .png
format for images. If you don't have .png
images, Android's docs call the .jpg
format “acceptable.” If you don't have .png
or .jpg
, the docs tell you to hold your nose and use .gif
. But remember that in this section, you're creating a practice application, not a work of art. Your images don't have to look good. They don't even have to look like pizzas. Besides, you can download our silly-looking drawings of pizzas from this book's website at www.allmycode.com/Android
. (You can also get there by visiting www.dummies.com
and searching this book's title.)
In creating this project, the example uses the filenames plain.png
, pepperoni.png
, extracheese.png
, and pep_extracheese.png
.
The names of Android's resource files must not contain capital letters. You can use only lowercase letters and underscores.
For working with image formats, the program IrfanView works well. You can get this Windows program at https://www.irfanview.com/
. The program is free for noncommercial use. Free alternatives to IrfanView for the Mac are Picasa (https://picasa-mac.en.softonic.com/mac
) or Preview (https://support.apple.com/guide/preview/welcome/mac
).
app/res/drawable
branch.In the main menu, choose Edit ⇒ Paste.
The Choose Destination Directory dialog box, shown in Figure 6-18, appears.
In the Choose Destination Directory dialog box, select the drawable
branch, as shown in Figure 6-18, and then click OK.
You see a Copy dialog box that contains a verification of the directory to use in the To Directory field.
In a real-life app, you use the drawable-dpi
directories (such as drawable-v24
, shown in Figure 6-18) as alternatives for devices with high, medium, extra-high, and extra-extra-high screen densities. But in this practice app, a default drawable
folder is the easiest to use.
The letters dpi stand for dots per inch. Android senses a device's screen density and uses the resources in the most appropriate drawable-?dpi
folder. To find out what Android considers “most appropriate,” visit https://developer.android.com/guide/practices/screens_support.html
.
app/res/drawable
branch.In the menu that appears, choose New ⇒ File.
Once again, the Choose Destination Directory dialog box rears its ugly head.
Select the drawable
branch, and then click OK.
A New File dialog box appears. This dialog box has only one field — a field for the name of your new file.
Use Android Studio's editor to populate your levels.xml
file with the code in Listing 6-5.
A level-list is a list of alternative drawables for a single image component to display. At any moment during an app's run, the image component has an integer level. You set the component's level using the setImageLevel
method.
When your app calls setImageLevel
, Android starts at the top of the level-list and looks for the first item whose android:maxLevel
is greater than or equal to the new image level. You can also assign an android:minLevel
attribute to an item. But in most situations, android:maxLevel
is all you need.
Add an ImageView
from the Widgets group to your activity's layout.
You see the Resources dialog box, shown in Figure 6-19.
Open the Project drop-down list and choose levels from the list.
You see the plain pizza displayed in the right pane, as shown in Figure 6-19.
Click OK.
You see the plain pizza image added to the ImageView
component in the preview area.
ImageView
component and set the layout_marginTop
field found in the All Attributes group of the Attributes window to 20dp
.Add the onButtonClick()
method shown in Listing 6-6 to your MainActivity.kt
file.
In Listing 6-6, the onButtonClick()
method calls the setImageLevel()
method. The method parameter's value depends on the states of the activity's check boxes.
Run the app.
The results, along with the beautiful drawing of the selected pizza with toppings, are shown in Figure 6-20.
LISTING 6-5: A Level-List Document
<?xml version="1.0" encoding="utf-8"?>
<level-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/plain" android:maxLevel="0"/>
<item android:drawable="@drawable/pepperoni" android:maxLevel="1"/>
<item android:drawable="@drawable/extracheese" android:maxLevel="2"/>
<item android:drawable="@drawable/pep_extracheese" android:maxLevel="3"/>
</level-list>
LISTING 6-6: Changing Images
fun onButtonClick(view: View) {
if (checkBox.isChecked()) {
if (checkBox2.isChecked()){
imageView.setImageLevel(3)
} else {
imageView.setImageLevel(1)
}
} else {
if (checkBox2.isChecked()){
imageView.setImageLevel(2)
} else {
imageView.setImageLevel(0)
}
}
}
If you've read any of this chapter's previous sections, you're probably very hungry. An app with nothing but pictures and the names of pizza toppings is a real tease.
So you'd better add some purchasing power to this chapter's example. Real e-commerce functionality is the subject of several other books. But in this book, you can get a small taste of the online pizza-ordering process (pun intended). You can submit your choice of toppings to an existing web server — Google's search engine, to be precise. It's not as good as biting into a tasty pizza, but the example shows you one way to send information from a mobile device.
In a real application, you might program your own server to respond intelligently to users' requests. For passing money back and forth, you might use the Google Play Store's in-app billing facilities.
Programming web servers isn't an Android-specific topic. To read all about servers, check out PHP & MySQL: Server-side Web Development, by Jon Duckett (Wiley).
Launch Android Studio and create a new project.
In this section's listing, we call the project 01_06_06
, and we use the package com.allmycode.p01_06_06
.
Include the reusable_layout.xml
and copied_strings.xml
files in your project.
For details, see the “Reusing a layout” section of the chapter.
Drag a WebView
component from the Widgets group of the Palette to your app's preview screen.
A WebView is a mini web browser that you can add to an existing activity. It doesn't set an id
value when you drag it to the screen, so you must perform this task separately.
WebView
in the Component Tree and change the id
field in the Attributes window to myWebView
.AndroidManifest.xml
file:
<uses-permission android:name="android.permission.INTERNET"/>
Make this uses-permission
element a direct sub-element of the document's manifest
element by pressing Enter and typing the code.
The uses-permission
element grants your app permission to access the Internet. Access to the Internet will appear in the list the user sees before installing your app.
When you create an app, don't forget to add the appropriate permissions to the app's AndroidManifest.xml
file. In a recent survey of For Dummies book authors, all respondents reported that they frequently forget to add permissions to their apps' manifest files. (Survey sample size: one.)
Run your app. Click Show.
You might have to wait for the web page to load. When the page loads, your app looks something like the screen in Figure 6-21.
LISTING 6-7: Sending Info to a Server
fun onButtonClick(view: View){
val builder = StringBuilder()
if (checkBox.isChecked()){
builder.append("Pepperoni")
}
if (checkBox2.isChecked()){
if (builder.length == 0){
builder.append("Extra Cheese")
} else {
builder.append(" and Extra Cheese")
}
}
if (builder.length == 0){
builder.append("Plain")
}
val thisView: WebView = findViewById(R.id.myWebView)
val mySettings: WebSettings = thisView.settings
mySettings.setAppCacheEnabled(true)
mySettings.cacheMode = WebSettings.LOAD_CACHE_ELSE_NETWORK
mySettings.javaScriptEnabled = true
mySettings.loadWithOverviewMode = true
thisView.loadUrl("https://www.google.com/search?q="
+ builder.toString())
}
The second item to note is that the settings used for this example appear to work on most emulators and most devices. However, you may have to locate any special settings for your particular device on vendor sites. For example, some devices may require an addition config.xml
file that isn't covered as part of this example because it’s a special file.
Of the settings shown, javaScriptEnabled
and loadWithOverviewMode
are the most important. Many sites won’t load properly, or sometimes not at all, unless you have JavaScript support enabled. The overview mode zooms the content out so that it fits on the device you’re using. Otherwise, you might not see anything, even though the page is loaded, because you’ve zoomed in on a particular page component.
35.170.81.33