Chapter 6

Improving Your App

IN THIS CHAPTER

check Improving the look of an app

check Helping people in other countries use your app

check 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 …)

Improving the Layout

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:

  • The button is below the pair of check boxes.
  • The check boxes are side by side.
    A mobile screen depicting the layout of a pair of check boxes centered (side by side) on the screen and the Show button centered below the check boxes.

    FIGURE 6-1: Your mission, should you decide to accept it.

  • The pair of check boxes is centered (side by side) on the screen.
  • The button is centered on the screen.
  • Taken as a group, the check boxes and the button don’t fill the entire screen.
  • Using a different layout will provide better results.

You may wonder why you should make these six observations and not others. If so, read on.

Changing the layout

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: Allows you to position components anywhere within the area defined by the 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).
  • Guideline: Works with a 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: Groups components vertically or horizontally into a single column or row. You combine LinearLayout components to get specific component organizations.
  • FrameLayout: Blocks out an area of the screen for a specific component. The idea is to ensure that the component resides in one specific area and uses a specific amount of screen real estate.
  • TableLayout and TableRow: Creates an environment akin to that used by web pages that rely on tables for organization. The TableLayout is the entire table, while a TableRow is a single row within that table.
  • Space: Defines a space between components. You combine this component with any other layout to provide a view that isn't cramped. The components can breathe. (Imagine that!)
  • Other: There are other layouts in the Legacy group of the Palette. In general, you want to avoid these layouts because they don’t match the current Android design guidelines. In addition, they may not work well with new layouts.

Defining the project

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 LinearLayout 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:

  1. 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.

  2. In the Designer tool's preview screen, select the Hello World! TextView and press Delete.

    The Hello World! TextView is deleted.

  3. 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.

    Remember 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.

  4. Set all four contraints of the 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.
  5. 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.

Screenshot of the Component Tree depicting a LinearLayout in addition to the ConstraintLayout that acts as a container for the LinearLayout.

FIGURE 6-2: This app uses a LinearLayout for component layout, rather than a ConstraintLayout.

Adding project components

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:

  1. 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.

  2. 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.

    Screenshot to drag a LinearLayout (Horizontal) component from the Layouts group of the Palette window directly under the Linear Layout (Vertical) in the Component Tree.

    FIGURE 6-3: Place the various components at the correct levels to obtain the desired layout.

    Tip 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.

  3. 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.

  4. 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.

  5. Drag a Space component from the Layouts group of the Palette window to place it between checkBox and checkBox2.

    Ahhh! Able to breathe again.

Configuring the project components

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:

  1. 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.

  2. 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.

  3. In the Component Tree, select your LinearLayout (Horizontal) component.
  4. 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.

  5. In the Component Tree, select the button.
  6. 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.

  7. 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.

    Technical Stuff 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.

  8. 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.

  9. 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.

Screenshot displaying the layout of the Component Tree at this point after adding the components of the two checkboxes and Button1 text.

FIGURE 6-4: The Component Tree (what you have so far, anyway).

Creating a reusable layout

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.

  1. Open the project that you create in the “Improving the Layout” section, earlier in this chapter.
  2. 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.

    Screenshot displaying a context menu containing a list of Style and Layout options.

    FIGURE 6-5: A list of refactoring options.

  3. In the context menu, select Extract Layout.

    The Extract Android Layout dialog box shown in Figure 6-6 appears.

    Screenshot of the Extract Android Layout dialog box to type the name of your new resource document in the file name field and extracting your style for future use.

    FIGURE 6-6: Extracting your style for future use.

  4. In the dialog box's File Name field, type the name of your new resource document.

    The example uses reusable_layout.xml.

    Warning 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.

  5. 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.

  6. Double-click the reusable_layout.xml branch in the Project tool window.

    Android Studio displays the Designer tool for the reusable_layout.xml file.

  7. Make sure that the Designer tool is in Design mode (as opposed to Text mode).

    In Design mode, you can see the Component Tree.

  8. 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.

    Tip 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.

Screenshot of the saved layout of the Component Tree displaying names like checkBox, checkBox2, and button.

FIGURE 6-7: The saved layout Component Tree.

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.

Reusing a layout

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:

Copying the initial project's files

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:

  1. 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.

  2. In the Project tool window, select the project's reusable_layout.xml file.
  3. 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.

  4. 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.

  5. In the new project's Project tool window, select the app/res/layout branch.
  6. In Android Studio's main menu, choose Edit ⇒   Paste.

    You see the Copy dialog box, shown in Figure 6-8.

    Screenshot of the Copy dialog box to place a copy of a layout or any other file in your current project.

    FIGURE 6-8: Place a copy of a layout or other file in your current project.

  7. 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.

  8. 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.

    Tip 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.

  9. 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!

Adding components to the reusable layout

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.

  1. 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).

  2. Delete the Hello World TextView.

    This project is going to be stacking components, so you need a LinearLayout (Vertical) component to hold the components.

  3. In the Layouts group of the Palette window, drag the LinearLayout (Vertical) component under the  ConstraintLayout in the Component Tree window.
  4. Select the LinearLayout (Vertical) component and set the four constraints to 0.
  5. 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.

  6. 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.

    Screenshot of the Resources dialog box to select your reusable layout — the one
named reusable_layout.xml, and then click OK.

    FIGURE 6-9: The Resources dialog box.

  7. In the Resources dialog box, select your reusable layout — the one you named reusable_layout.xml; then click OK.

    The Resources dialog box closes.

  8. 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.

  9. 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.)

  10. From the Palette window, drag a TextView component to a location under the include component in the Component Tree.
  11. Select textView in the Component Tree.
  12. 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.

  13. Replace the textView placeholder text with a resource string named TextView1Text that has a value of Plain.
  14. (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!

Starting Another Activity

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.

  1. 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.

  2. 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 …

  3. 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.

  4. 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.)

    Screenshot of the Configure Activity dialog box to fill in the Activity Name and Layout Name fields and click Finish to close the dialog box.

    FIGURE 6-10: The Configure Activity dialog box.

  5. 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.

  6. 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.)

  7. (Optional, but worth doing.) In this step, you don't have to do anything. Just look at something! In your project's 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.

    Remember 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.)

  8. 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.

  9. 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.

    Remember 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.

  10. 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.

    Technical Stuff 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.

    Tip 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.

  11. Modify your main activity's code, as shown in Listing 6-1.

    The listing shows the code to be added in boldface type.

    Remember 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:

    • The intent in Listing 6-1 invokes the code in a class named OtherActivity (or whatever you name your app's second activity).
    • The intent in Listing 6-1 has two extra pieces of information. Each “extra piece” of information is a name/value pair. For example, if the user selects the Pepperoni box, checkBox.isChecked() is true, so the intent contains the extra pair "Pepperoni", true.
    • In Listing 6-1, the call 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.

  12. In your new OtherActivity class, add the code in Listing 6-2.

    The listing shows the code to be added in boldface type.

    Warning 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".

  13. Run your app.

    When you click the app's button, you see a new activity like the one pictured in Figure 6-11.

A mobile screen displaying the words “Pepperoni” and “Extra cheese” on the screen, for a new activity.

FIGURE 6-11: A new activity appears on the device's screen.

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)
}
}

Localizing Your App

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:

  1. Right-click (or, on a Mac, Ctrl-click) the strings.xml file in your project's res/values folder in the Project tool window.
  2. In the context menu that appears, select Open Translations Editor.

    The Translations Editor appears in place of the Designer tool. (See Figure 6-12.)

    Screenshot of the Translations Editor that appears in place of the Designer tool.

    FIGURE 6-12: The Translations Editor.

  3. Near the top of the Translations Editor, click the globe icon.

    A list of language locales appears. (See Figure 6-13.)

    Screenshot displaying a list of language locales to select one for a project.

    FIGURE 6-13: Select a language.

  4. 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.)

    Screenshot of the strings.xml branch in the Project tool window with 2 sub-branches that sport the label strings.xml, but the new sub-branch’s icon is a tiny picture of the flag of Italy.

    FIGURE 6-14: Look! You have two strings.xml files.

    Technical Stuff 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.

  5. 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.)

  6. 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>

  7. 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.

  8. 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.

    A mobile screen to add the required language support to your emulator, to be used as the primary language.

    FIGURE 6-15: Add the required language support to your emulator.

  9. Change the language setting of your emulator to Italiano (Italia).

    Suddenly, your app looks like the display in Figure 6-16.

A mobile screen to change the language setting of your emulator to Italiano (Italia).

FIGURE 6-16: Buongiorno!

Responding to Check Box Events

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.

Remember After you have the appearance just right, you need to create onClick (found within the All Attributes group of the Attributes window) string resources for both check boxes. In fact, the second check box uses the same string resource as the first one does — imagine that! Give the single string resource a name of CheckboxClick and a value of onCheckboxClick. To assign the resource string to the onClick field, you must actually type @string/CheckboxClick in the field.

A mobile screen depicting the variation of the app you have been using, having two check boxes and the text box, which is configured the same as the Show button it replaces.

FIGURE 6-17: A reduced number of control versions of the previous examples.

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().

Technical Stuff The only real difference between a standard Button and a check box is that the check box is a CompoundButton, so there are other ways you could track the button state, such as by using a listener. (The technique shown in the example is by far the easiest, and you should use it whenever possible.) A CompoundButton is a widget with checked and unchecked states. The CheckBox class is a subclass of CompoundButton. Other subclasses of CompoundButton are Switch, RadioButton, and ToggleButton. A ToggleButton is that cute little thing that lights when it's in the checked state. The Switch is the slider type button that you see in many Android apps today.

Displaying Images

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:

  1. 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.

  2. Copy the 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.
  3. Include reusable_layout.xml and copied_strings.xml files in your project.

    For details, see the “Reusing a layout” section of the chapter.

  4. 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.

    Remember The names of Android's resource files must not contain capital letters. You can use only lowercase letters and underscores.

    Tip 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).

  5. In your operating system's File Explorer or Finder, select the image files. Then, in the main menu, choose Edit ⇒   Copy.
  6. In Android Studio's Project tool window, select the app/res/drawable branch.
  7. In the main menu, choose Edit ⇒   Paste.

    The Choose Destination Directory dialog box, shown in Figure 6-18, appears.

  8. 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.

    Screenshot of the Choose Destination Directory dialog box to use the drawable-dpi directories, such as drawable-v24, in a real-life application.

    FIGURE 6-18: Be sure to choose the correct destination for the files.

    Technical Stuff 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.

  9. Click OK.
  10. Right-click (on Windows) or Ctrl-click (on a Mac) the app/res/drawable branch.
  11. In the menu that appears, choose New ⇒   File.

    Once again, the Choose Destination Directory dialog box rears its ugly head.

  12. 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.

  13. In the New File dialog box's field, type levels.xml.
  14. Click OK to dismiss the New File dialog box.
  15. 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.

  16. Add an ImageView from the Widgets group to your activity's layout.

    You see the Resources dialog box, shown in Figure 6-19.

    Screenshot of the Resources dialog box to choose levels from the drop-down list, with the plain pizza displayed on the right pane.

    FIGURE 6-19: Use the Resources dialog box to configure the ImageView.

  17. 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.

  18. Click OK.

    You see the plain pizza image added to the ImageView component in the preview area.

  19. Select the ImageView component and set the layout_marginTop field found in the All Attributes group of the Attributes window to 20dp.
  20. 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.

  21. Run the app.

    The results, along with the beautiful drawing of the selected pizza with toppings, are shown in Figure 6-20.

A mobile screen displaying a beautiful drawing of the selected pizza with toppings on it - pepperoni and extra cheese.

FIGURE 6-20: What a lovely drawing!

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)
}
}
}

Sending in Your Order

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).

  1. 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.

  2. Include the reusable_layout.xml and copied_strings.xml files in your project.

    For details, see the “Reusing a layout” section of the chapter.

  3. 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.

  4. Select the WebView in the Component Tree and change the id field in the Attributes window to myWebView.
  5. Code your project's activity file as in Listing 6-7.
  6. Add the following element to your project's 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.

    Remember 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.)

  7. 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.

A mobile screen displaying a Google page through which your app sends stuff to a web server.

FIGURE 6-21: Your app sends stuff to a web server.

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())
}

Warning You should note a few things about this example. First, it won't run on all emulators. For example, it runs fine on the Pixel 3a but not on the Nexus S. (Your results may vary from ours.) The Nexus S will report ERR_ACCESS_DENIED even if you add the <uses-permission android:name="android.permission.INTERNET" /> permission to AndroidManifest.xml. So, if your example reports errors, try a different emulator.

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.

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

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