Chapter 1

Laying Out Your Stuff

In This Chapter

check1 Organizing the widgets on the device’s screen

check1 Dealing with colors, sizes, and positions

check1 Working with various layouts

Which description do you prefer?

  • In my entire line of sight, I see a polygon with convex vertices at the points (1.5, 0), (0, 1.2), (3, 1.2), (0.6, 3), (2.4, 3), and with concave vertices at the points (1.15, 1.2), (1.85, 1.2), (0.95, 1.8), (2.05, 1.8), (1.5, 2.2). The units are in inches.
  • In my entire line of sight, I see a five-pointed star.

The first description is more precise, but the first is also more brittle. As I type this introduction, I anticipate the email messages from readers: “You got one of the numbers wrong in the first description.”

The second description is also more versatile. The second description makes sense whether you describe an image on a laptop screen or on a highway billboard. In a world with all kinds of mobile devices, all kinds of screen sizes, screen resolutions, display qualities, refresh rates, and who-knows-what-other variations, the big picture is often more useful than the picky details. When you describe your app’s screen, you should avoid measurements in favor of concepts.

Android supports several layout concepts, including linear layout, relative layout, table layout, and frame layout. In many cases, choosing one kind of layout over another is a matter of taste. A table layout with only one row looks like a horizontal linear layout. A set of nested linear layouts may look exactly like a complicated relative layout. The possibilities are endless.

Android Layouts

The game with Android’s layouts is to place visible things into a container in an orderly way. In a typical scenario, one of these “visible things” is a view, and one of these “containers” is a view group.

The formal terminology is a bit hazy. But fortunately, the fine distinctions between the terms aren’t terribly important. Here’s some formal terminology:

  • A view appears on the user’s screen and (either directly or indirectly) involves user interaction. The word view often refers to an instance of the android.view.View class. A broader use of the word view includes any class or interface in the android.view package.
  • A widget appears on the user’s screen and (either directly or indirectly) involves user interaction. (Sounds a lot like a view, doesn’t it?) The word widget commonly refers to a class or interface in the android.widget package.

    Views can be widgets, and widgets can be views. As long as your import declarations work and your method parameter types match, the distinction between widgets and views is unimportant.

    Commonly used widgets and views include buttons, check boxes, text views, toasts, and more exotic things such as digital clocks, sliding drawers, progress bars, and other good junk.

    (Android or no Android, I think widget is a wonderful word. The playwrights George S. Kaufman and Marc Connelly made up the word “widget” for dialogue in their 1924 comedy Beggar on Horseback. I learned about widgets from the fictitious Universal Widgets company — an enterprise featured in The Wheeler Dealers from 1963.)

  • A view group (an instance of android.view.ViewGroup) is a view that contains other views (including other view groups). The views contained in a view group are the view group’s children.

    Examples of view groups are the linear layouts, relative layouts, table layouts, and frame layouts mentioned at the beginning of this chapter, as well as some more special-purpose things such as the list view and the scroll view.

One way or another, I usually write about putting a “view” on a “layout.”

Linear Layout

Linear layouts have either vertical or horizontal orientation. Views in a vertical linear layout line up one beneath the other. Views in a horizontal linear layout line up one beside the other. (See Figure 1-1.)

image

Figure 1-1: Four buttons in a horizontal linear layout.

You can create the layout in Figure 1-1 with the XML code in Listing 1-1.

Listing 1-1: A Horizontal Linear Layout

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android=

      "http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:orientation="horizontal">

    <Button android:text="Button1"

        android:layout_height="wrap_content"

        android:layout_width="wrap_content"

        android:id="@+id/button1">

    </Button>

    <Button android:text="Button2"

        android:layout_height="wrap_content"

        android:layout_width="wrap_content"

        android:id="@+id/button2">

    </Button>

    <Button android:text="Button3"

        android:layout_height="wrap_content"

        android:layout_width="wrap_content"

        android:id="@+id/button3">

    </Button>

    <Button android:text="Button4"

        android:layout_height="wrap_content"

        android:layout_width="wrap_content"

        android:id="@+id/button4">

    </Button>

</LinearLayout>

 

You can also create the layout in Figure 1-1 by dragging and dropping views onto the Preview screen in Android Studio’s Designer tool.

Linear layouts don’t wrap, and they don’t scroll. So, if you add six buttons to a horizontal layout and the user’s screen is wide enough for only five of the buttons, the user sees only five buttons. (See Figure 1-2.)

image

Figure 1-2: Six (yes, six) buttons in a horizontal linear layout.

Attributes (A Detour)

Using XML attributes, you can change a layout’s default behavior. This section has several examples.

android:layout_width and android:layout_height

You can tweak the size of a view using the android:layout_width and android:layout_height attributes. Listing 1-2 has some code, and Figure 1-3 shows the resulting layout.

image

Figure 1-3: Buttons of various widths and heights.

Listing 1-2: Setting a View’s Width and Height

<?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">

  <Button

    android:layout_width="match_parent"

    android:layout_height="wrap_content"

    android:text=

      "1. android:layout_height is wrap_content

      android:layout_width is match_parent ">

    android:id="@+id/button1"

  </Button>

  <Button

    android:layout_width="wrap_content"

    android:layout_height="wrap_content"

    android:text="2. wrap/wrap">

    android:id="@+id/button2"

  </Button>

  <Button

    android:layout_width="60dp"

    android:layout_height="wrap_content"

    android:text="3. width 60dp">

    android:id="@+id/button3"

  </Button>

  <Button

    android:layout_width="160dp"

    android:layout_height="wrap_content"

    android:text="4. android:layout_width is 160dp">

    android:id="@+id/button4"

  </Button>

  <Button

    android:layout_width="wrap_content"

    android:layout_height="match_parent"

    android:text="5. width wrap_content

                  height match_parent"

    android:id="@+id/button5">

  </Button>

</LinearLayout>

 

You can describe a view’s size using general guidelines or numbers of units, as spelled out in the next two sections.

Using general size guidelines

To create general guidelines, use the "wrap_content", "match_parent", or "fill_parent" value. (Refer to Listing 1-2.) With "wrap_content", Android shrinks a view’s width or length so that the view tightly encloses whatever it contains. With "match_parent" and "fill_parent", Android expands a view’s width or length so that the view fits tightly inside its container.

remember What it means to “tightly enclose” something or “fit tightly inside” something depends on the amount of breathing room you specify for the boundaries around things. This breathing room comes in two forms — padding and margins. (If you want, you can look ahead to this chapter’s section on padding and margins.)

technicalstuff The strings "match_parent" and "fill_parent" have the same meaning. Before API Level 8, only "fill_parent" works. Starting with API Level 8, the string "fill_parent" is deprecated in favor of "match_parent". According to Java’s official documents, deprecated features are obsolete and “are supported only for backwards compatibility.” In the Java world, a feature that’s deprecated might be unavailable in future software versions. But Java’s deprecated features tend to linger on for years. Both "fill_parent" and "match_parent" work up to (and possibly beyond) API Level 21 (Lollipop).

In Listing 1-2, the first button has attributes android:layout_width="match_parent" and android:layout_height="wrap_content". So in Figure 1-3, the top button is as wide as it can be and only tall enough to contain the words displayed on the button’s face.

technicalstuff In Listing 1-2, look for inside quoted strings of characters. The business is called an escape sequence. It means “go to the next line.”

Using numbers of units

In Listing 1-2, I describe the third button’s width in units. The value 60dp stands for 60 density-independent pixels. A density-independent pixel is a measurement based on a 160 pixels-per-inch benchmark.

“And what,” you ask, “is a 160 pixels-per-inch benchmark?” A pixel is a single dot on a device’s screen. A pixel can be invisible, glow brightly, or anything in between.

Different devices have different pixel densities. For example, a low-density screen might have 120 pixels per inch, and an extra-extra-high-density screen might have 480 pixels per inch. To adjust for these differences, each Android screen has several metrics. Each metric is a numeric value describing some characteristic of the display:

  • widthPixels (an int value): The number of pixels from the left edge to the right edge of the screen.
  • heightPixels (an int value): The number of pixels from the top to the bottom of the screen.
  • xdpi (an int value): The number of pixels from left to right along one inch of the screen.
  • ydpi (an int value): The number of pixels from top to bottom along one inch of the screen. (In a square inch, some screens stuff more pixels across than up and down, so xdpi isn’t necessarily the same as ydpi.)
  • densityDpi (an int value): A general measure of the number of pixels per inch. For screens with equal xdpi and ydpi values, densityDpi is the same as xdpi and ydpi. For screens with unequal xdpi and ydpi values, somebody figures out what the screen’s densityDpi is (but they don’t tell me how they figure it out).
  • density (a float value): The number of pixels per inch, divided by 160.
  • scaledDensity (a float value): Another take on the density measure, but this time with some extra stretching or squeezing to account for any default font size chosen by the user. (Some versions of Android don’t let the user adjust the default font size, but with some third-party apps, a user can get around the limitation.)

When you specify 160dp (as in Listing 1-2), you’re telling Android to display density × 160 pixels. So, on my tiny screen, a width of 160dp is one inch, and on the Android home theater that you transport through time from the year 2055, a width of 160dp is one inch. Everybody gets the inch that they want.

technicalstuff The letters dpi stand for dots per inch. Your Android project might have folders named res/drawable-xhdpi, res/drawable-hdpi, res/drawable-ldpi, and so on. At runtime, Android senses a device’s or emulator’s screen density and uses the resources in the most appropriate res/drawable-dpi folder. For more info, see this chapter’s “Using configuration qualifiers” section.

Another handy unit of measurement is sp — scale-independent pixels. Like the dp unit, the size of an sp unit adjusts nicely for different screens. But the size of an sp unit changes in two ways. In addition to changing based on the screen’s pixel density, the sp unit changes based on the user’s font size preference settings.

tip The abbreviations dp and dip are interchangeable. Both stand for density-independent pixels. But sp is always sp. Android has no unit named sip.

If you want to be ornery, you can use physical units. For example, value 2in stands for two inches. Other unsavory physical units include mm (for millimeters), pt (for points, with 1 point being 1/1000 of an inch), and px (for pixels — actual dots on the device’s screen). In almost all situations, you should avoid physical units. Use dp to specify a view’s size, and use sp to specify a text font size.

Figures 1-4 and 1-5 illustrate the relationship between pixels and density-independent pixels. The screen in Figure 1-4 has density 0.75. So an inch-wide button consumes 0.75 × 160 = 120 pixels. You can confirm this by comparing the sizes of the 160dp and 160px buttons. The 160dp button is roughly three quarters the width of the 160px button.

image

Figure 1-4: A low-density display.

image

Figure 1-5: A high-density display.

warning This book’s figures might have stretched or shrunk in printing. Objects may be larger than they appear. What measures an inch across in this book’s pages or on your e-reader’s screen isn’t necessarily an inch on an Android device’s screen.

The screen in Figure 1-5 has a density 1.5, so in Figure 1-5, a 160dp button is 1.5 times as wide as a 160px button.

The XML document describing the layout in Figures 1-4 and 1-5 is shown in Listing 1-3.

Listing 1-3: Using Size Units

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android=

      "http://schemas.android.com/apk/res/android"

    android:orientation="vertical"

    android:layout_width="fill_parent"

    android:layout_height="fill_parent">

    <TextView android:textSize="30sp"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:id="@+id/textView1"></TextView>

    <Button android:layout_width="160dp"

        android:text="160dp"

        android:layout_height="wrap_content"

        android:id="@+id/button1"></Button>

    <Button android:layout_width="160px"

        android:text="160px"

        android:layout_height="wrap_content"

        android:id="@+id/button2"></Button>

</LinearLayout>

 

tip Look again at Figures 1-4 and 1-5, and at Listing 1-3. In a real-life app, you probably don’t want things like 160dp to appear on the user’s screen. You can specify text that appears only while you test your app (and not after you’ve prepared your app for publication). To do so, add the xmlns:tools="http://schemas.android.com/tools" attribute to your layout file’s root element. Then replace android:text="160dp" with tools:text="160dp". (Your Button component can even have both attributes — a tools:text attribute for when you develop your app, and an android:text attribute for when you publish the app.)

Notice the metric information in the text view in Figures 1-4 and 1-5. To display this information, I use the android.util.DisplayMetrics class in my app’s main activity:

package com.allmycode.screen;

import android.app.Activity;

import android.os.Bundle;

import android.util.DisplayMetrics;

import android.widget.TextView;

public class ScreenActivity extends Activity {

  TextView textView;

  DisplayMetrics metrics;

  String densityDpiConstant;

  @Override

  public void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.main);

    textView = (TextView) findViewById(R.id.textView1);

    metrics = new DisplayMetrics();

    getWindowManager().getDefaultDisplay().

      getMetrics(metrics);

    switch (metrics.densityDpi) {

    case DisplayMetrics.DENSITY_LOW:

      densityDpiConstant = "DENSITY_LOW";

      break;

    case DisplayMetrics.DENSITY_MEDIUM:

      densityDpiConstant = "DENSITY_MEDIUM";

      break;

    case DisplayMetrics.DENSITY_HIGH:

      densityDpiConstant = "DENSITY_HIGH";

      break;

    case DisplayMetrics.DENSITY_XHIGH:

      densityDpiConstant = "DENSITY_XHIGH";

      break;

    case DisplayMetrics.DENSITY_XXHIGH:

      densityDpiConstant = "DENSITY_XXHIGH";

      break;

    default:

      densityDpiConstant = "densityDpi is " +

                                      metrics.densityDpi;

      break;

    }

    textView.setText(metrics.toString()

       + ", " + densityDpiConstant);

  }

}

android:padding and android:margin

Objects on a screen need room to breathe. You can’t butt one text field right up against another. If you do, the screen looks horribly cluttered. So Android has things called padding and margin:

  • A view’s padding is space between the view’s border and whatever is contained inside the view.
  • A view’s margin is space between the view’s border and whatever is outside the view.

In Figure 1-6, I superimpose labels onto a screen shot from an emulator. The rectangle with eight little squares along its perimeter is the text view’s border. Think of the border as the text view’s clothing. The padding keeps the clothing from being too tight, and the margins determine how much “personal space” the text view wants.

image

Figure 1-6: Padding versus margin.

Listing 1-4 contains the code that generates the layout in Figure 1-6.

Listing 1-4: Using Margin and Padding

<?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"

    android:gravity="center">

    <LinearLayout android:id="@+id/LinearLayout1"

        android:layout_height="wrap_content"

        android:layout_width="wrap_content"

        android:background="@color/opaque_white">

        <TextView android:text="Dummies"

            android:layout_margin="30dip"

            android:padding="30dip"

            android:textSize="30sp"

            android:layout_height="wrap_content"

            android:layout_width="wrap_content"

            android:id="@+id/textView1"

            android:textColor="@color/opaque_black">

        </TextView>

    </LinearLayout>

</LinearLayout>

android:gravity and android:layout_gravity

I once asked a button what it wanted to be when it grows up. The button replied, “I want to be an astronaut.” So I placed the button inside a layout with attribute android:layout_gravity="center". A layout with this attribute is like the International Space Station. Things float in the middle of it. (Well, they don’t actually bob to and fro the way things do in the space-station videos, but that’s beside the point.)

Android has two similarly named attributes, and it’s very easy to confuse them with one another. The android:gravity attribute tells a layout how to position the views within it. The android:layout_gravity attribute tells a view how to position itself within its layout. Figure 1-7 illustrates the idea.

image

Figure 1-7: The gray layout gravitates to the center of the screen; the button gravitates to the top of the gray layout.

The screen in Figure 1-7 contains a shortened linear layout and a button. The linear layout is only 220dip tall, and its android:layout_gravity is center_vertical. (See Listing 1-5.) So the gray linear layout floats downward to the center of the screen. But the layout’s android:gravity attribute is center_horizontal. So the button within the layout shimmies horizontally to the layout’s center. The button hangs along the top edge of the layout because, by default, things rise to the top and hug the left.

Listing 1-5: Using layout_gravity and gravity

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android=

      "http://schemas.android.com/apk/res/android"

    android:layout_gravity="center_vertical"

    android:gravity="center_horizontal"

    android:background="#F999"

    android:orientation="vertical"

    android:layout_width="match_parent"

    android:layout_height="220dip">

    <Button android:text="Button"

        android:id="@+id/button1"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content">

    </Button>

</LinearLayout>

 

Figures 1-8 through 1-11 illustrate some other android:gravity attribute values.

image

Figure 1-8: LinearLayout with android:gravity="center_vertical"

image

Figure 1-9: LinearLayout with android:gravity="center"

image

Figure 1-10: LinearLayout with android:gravity="bottom|right"

image

Figure 1-11: LinearLayout with no explicit android:gravity attribute.

Figure 1-10 shows what you can do by combining gravity values using Java’s bitwise or operator (|).

android:color

You can apply colors to all kinds of things — things such as text, backgrounds, shadows, links, and other stuff. In Listing 1-5, I use the attribute android:background="#F999", making a layout’s background one quiet, dignified shade of gray.

As an Android developer, the most grown-up way to create a color is to declare it in a res/values/colors.xml file. The file looks something like the stuff in Listing 1-6.

Listing 1-6: A colors.xml File

<?xml version="1.0" encoding="utf-8"?>

<resources xmlns:android=

    "http://schemas.android.com/apk/res/android">

  <color name="bright_red">#F00</color>

  <color name="bright_red2">#FF00</color>

  <color name="bright_red3">#FF0000</color>

  <color name="translucent_red">#7F00</color>

  <color name="invisible_good_for_nothing_red">

    #00FF0000

  </color>

  <color name="white">#FFF</color>

  <color name="black">#000</color>

  <color name="puce">#CC8898</color>

</resources>

 

A color value begins with a pound sign (#) and then has three, four, six, or eight hexadecimal digits. With three digits, the leftmost digit is an amount of redness, the middle digit is an amount of greenness, and the rightmost digit is an amount of blueness. (The colors always come in that order — red, then green, and then blue. It’s called RGB color.) So, for example, the color value #F92 stands for a decent-looking orange color — 15 units of red, 9 units of green, and 2 units of blue, each out of a possible 16 units.

With only three hexadecimal digits, you can’t express fine color differences. So Android permits you to express a color as a sequence of six hex digits. For example, the value #FEF200 is a good approximation to the yellow on this book’s cover. It’s 254 units of red, 242 units of green, and no blue, each out of a possible 255 units.

With three RGB digits, you can add a fourth alpha digit immediately after the pound sign. And with six RGB digits, you can add two additional alpha digits immediately after the pound sign. The alpha value is the amount of opaqueness, with 15 being fully opaque and 0 being completely transparent. So the value #7F00 in Listing 1-6 is partially transparent red (“translucent red,” if you will). Against a black background, the value #7F00 is a dull, depressing reddishness.

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android=

      "http://schemas.android.com/apk/res/android"

  android:background="@color/black"

  android:id="@+id/linearLayout1"

  android:layout_height="wrap_content"

  android:layout_width="match_parent"

  android:orientation="vertical">

  <Button android:background="@color/translucent_red"

     android:layout_width="match_parent"

     android:layout_height="wrap_content"

     android:text="Button"

     android:id="@+id/button8"></Button>

</LinearLayout>

 

Against a white background, the value #7F00 looks like elementary-school pink.

technicalstuff A hexadecimal digit is an ordinary decimal digit or one of the letters A, B, C, D, E, or F. (Either uppercase or lowercase letters are okay.) The letter A stands for 10, B stands for 11, and so on up to F, which stands for 15. A two-digit hex number stands for “the right digit, plus 16 times the left digit.” For example, A5 stands for 5 + (16 × 10), which is 165. With only one hex digit, you can represent the int values from 0 to 15, inclusive. With two hex digits, you can represent the int values from 0 to 255, inclusive.

android:visibility

An Android view has one of three visibility values — android.view.View.VISIBLE, android.view.View.INVISIBLE, or android.view.View.GONE. The first value — VISIBLE — is self-explanatory. The difference between INVISIBLE and GONE is as follows: An INVISIBLE view takes up space; a view that’s GONE takes up no space. For example, in Figure 1-12, an INVISIBLE view (Button2) separates Button1 from Button3. If I change Button2's visibility to GONE, Button1 butts up against Button3. (See Figure 1-13.)

image

Figure 1-12: Button2 is INVISIBLE.

image

Figure 1-13: Button2 is GONE.

In Figure 1-13, Button2 is gone but not forgotten. See this chapter’s “Frame Layout” section for more details.

Using Configuration Qualifiers (Another Detour)

You may have noticed folders with names like drawable-xhdpi and layout-sw300dp. In Android Studio’s Project tool window, they appear as items inside the drawable and layout branches. But in your system’s File Explorer or Finder, these are separate folders containing image files, XML files, and other kinds of files. You can also find branches in the Project tool window with names like values-it (values for the Italian language locale). In your File Explorer or Finder, the corresponding folders contain strings.xml files, and other locale-specific files.

crossreference To find out how to use localization folders such as values-it, refer to Chapter 6 in Book I.

In a res subfolder name, characters that start with a dash (characters such as -xhdpi, -sw300dp, and -it) are configuration qualifiers. The general idea is to specify certain folders for particular device configurations. If the device is configured to display text in Italian, Android uses files from the res/values-it folder instead of from the default res/values folder. If the device has an extra-high-density screen (no big deal by today’s standards), Android uses files from the res/layout-xhdpi folder. (That is, Android uses files from the res/layout-xhdpi folder if the project has such a folder.)

When you run an app, Android checks the device screen’s characteristics. A screen with dpi value 240 is considered to be high density. This can be called hdpi or DisplayMetrics.DENSITY_HIGH, depending on where you refer to it in your project. So, if the screen’s dpi value is close to 240 (whatever "close to" means), Android looks for folders named res/layout-hdpi, res/drawable-hdpi, and so on. The res/layout-hdpi folder (if your app has one) contains files named activity_main.xml and other files describing the layout of your app’s screens. The res/drawable-hdpi folder (if your app has such a folder) contains images. For a high-density device or emulator, Android uses files that it finds in these -hdpi folders. If your app doesn’t have such folders, Android uses files in the default folders (the plain old res/layout and res/drawable folders).

Table 1-1 contains some of Android’s dpi names and their corresponding dpi values.

Table 1-1 Android Screen Densities

Name

Acronym

Approximate* Number of Dots per Inch (dpi)

Fraction of the Default Density

DENSITY_LOW

ldpi

120

¾

DENSITY_MEDIUM

mdpi

160

1

DENSITY_HIGH

hdpi

240

DENSITY_XHIGH

xhdpi

320

2

DENSITY_XXHIGH

xxhdpi

480

3

DENSITY_XXXHIGH

xxxhdpi

640

4

* When the screen density of a device doesn’t match a number in Column 3 of Table 1-1 , Android does its best with the existing categories. For example, Android classifies density 265 dpi in the hdpi group.

Fun facts: DENSITY_XHIGH is used for 1080p high-definition televisions in the United States. A seldom-used Android density, DENSITY_TV with 213 dpi, represents 720p television. The ultra-large DENSITY_XXXHIGH might be useful for displaying graphics on the newest 4K television screens.

technicalstuff You can combine configuration qualifiers. For example, the folder with the name drawable-en-land is for the English language when the device is in landscape mode. When you do this, you must list the qualifiers in a certain order. For details, see http://developer.android.com/guide/topics/resources/providing-resources.html.

Specifying exact dpi values

When I was young, products were available in sizes small, medium, and large. But the U.S. economy was doing well, and people were buying more and more stuff. So companies started selling extra-large items. After a while, people were no longer buying small sizes. Eventually, the three choices in supermarket shelves were large, extra-large, and jumbo. Nothing was available in small or medium sizes.

The same kind of thing has happened with Android screen sizes. People don’t write apps for DENSITY_LOW screens anymore. And the trend toward higher and higher densities isn’t stopping. By the time Android 3.2 was released, the stewards of Android were tired of making up new names for higher and higher screen densities. So they came up with a more versatile scheme. They called for folders with names such as layout-sw300dp.

In the Android documentation, such folders are named sw< N >dp folders. The letters sw stand for smallest width (the width of your activity when the activity has its narrowest width — typically the activity’s width in portrait mode). If the screen width available to your activity is ever smaller than whatever number you substitute in place of N, Android doesn’t look inside this sw< N >dp folder. For example, if the screen width available to your activity is ever smaller than 600dp, Android doesn’t look inside the layout-sw600dp folder, the drawable-sw600dp, or any other -sw600dp folders.

technicalstuff An activity’s smallest width doesn’t change when you flip from portrait to landscape mode, because “smallest width” really means “smallest possible width in any mode — landscape, portrait, or whatever other mode there might be.”

Here’s an experiment for you to try:

  1. Create a new Android project.
  2. Using your system’s File Explorer or Finder, create a subfolder of the project’s res folder. Name the new folder layout-sw300dp (for example).
  3. Still using your system’s File Explorer or Finder, copy your app’s activity_main.xml file from the res/layout folder to the new res/layout-sw300dp folder.

    Now your main activity has two layout files — one for emulators and devices that have 300dp or more for an activity and another for all other screens.

  4. Modify one of the activity_main.xml files so that you can determine which is being used during a run of the app.

    For me, the easiest thing to do is to take the text view that comes with every new app. In that text view, change the characters in one of the activity_main.xml files.

  5. Run your app on two emulators — one relatively new and another that’s fairly old.

    I ran this app on a 768 × 1280 Nexus 4, and a 480 × 800 Nexus One. The Nexus 4 displayed the layout from the res/layout-sw300dp folder. But the Nexus One defaulted to the layout from the res/layout folder. That’s how these sw< N >dp folders work.

Specifying screen orientation

In the previous section, you name different layout files for different screen sizes. The screen sizes depend on the device’s dpi (dots per inch) value. This is very useful, but when you’re trying to make an app look nice, dots per inch don’t tell the whole story.

Suppose you want different layouts for the device in portrait and landscape modes. You can make that happen by creating a res/layout-land folder. Here are the details:

  1. Create a new Android project.
  2. Using your system’s File Explorer or Finder, create a subfolder of the project’s res folder. Name the new folder layout-land.
  3. Still using your system’s File Explorer or Finder, copy your app’s activity_main.xml file from the res/layout folder to the new res/layout-land folder.

    Now your main activity has two layout files — one for landscape mode on your emulator or device (res/layout-land/activity_main.xml) and another for portrait mode (res/layout /activity_main.xml).

  4. Modify one of the activity_main.xml files so that you can determine which is being used during a run of the app.

    For me, the easiest thing to do is to take the text view that comes with every new app. In that text view, change the characters in one of the activity_main.xml files.

  5. Run your app.
  6. Change the emulator or device back and forth between portrait and landscape mode.

    Notice how the layout changes automatically from one XML file to the other.

    tip To switch an emulator between portrait and landscape modes, press Ctrl+F11.

Some other configuration qualifiers

This chapter emphasizes the dpi, sw< N >dp, and land configuration qualifiers. But Android has many other configuration qualifiers. Table 1-2 contains a brief list.

Table 1-2 Configuration Qualifiers

Name

Examples

Meaning

Language and region

-en, -it

In what language (and for what country or region) is this device configured?

Layout direction

-ldrtl, -ldltr

In which direction does writing go on this device? Right-to-left (as in Hebrew or Arabic) or left-to-right (as in many other languages)?

Smallest width

-sw300dp

No matter how the device is oriented, what’s the smallest width (in density-independent pixels) that’s available to your activity?

Available width

-w720dp

For the device’s current orientation, what’s the width (in density-independent pixels) that’s available to your activity?

Available height

-h1024dp

For the device’s current orientation, what’s the height (in density-independent pixels) that’s available to your activity?

Pixel density

-hdpi, -xhdpi

How many density-independent pixels does the device’s screen have?

Orientation

-port, -land

How is the device currently oriented? Portrait or landscape?

UI mode

-car, -desk, -watch, -television, -appliance

What kind of a device is running your app?

Night mode

-night, -notnight

Are you (or aren’t you) currently in the Earth’s shadow?

Touchscreen type

-notouch, -finger

Does the screen respond to the user’s touch?

API level

-v4, -v7

Which API level does this device support?

remember I occasionally become sloppy and write that the choice of sw< N >dp, w< N >dp, and h< N >dp folders depends on a device’s screen size. This is mostly true, but sometimes, it’s worth being picky. To be painfully precise, measurements such as sw300dp and h1024dp don’t really refer to the entire screen. They refer to whatever part of the screen is available for use by your activity. For example, the Back, Home and Recents buttons that appear at the bottom of a phone’s screen are not part of an activity’s layout. So the space taken by these elements doesn’t count when Android decides about using an h< N >dp folder. If the screen has 1060dp with these buttons but only 900dp when you don’t count the height of these buttons, then on this device, Android doesn’t use an h1024dp folder’s file. (If there’s a surprise in this story, it concerns the Action bar. The Action bar counts as part of the activity’s screen space. So, if you have 1060dp with the Action bar and only 900dp without the Action bar, Android can use the h1024dp folder’s files.)

Relative Layout

When you create a new Android project, Android Studio fills your main activity’s layout with a relative layout. A relative layout describes the placement of each view compared with other views. For example, in a relative layout, you might place Button2 beneath Button1 and place Button3 to the right of Button1. Listing 1-7 has some code, and Figure 1-14 shows the resulting screen.

image

Figure 1-14: Buttons in a relative layout.

Listing 1-7: Using a Relative Layout

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android=

      "http://schemas.android.com/apk/res/android"

    android:orientation="vertical"

    android:layout_height="match_parent"

    android:layout_width="match_parent">

    <Button android:layout_alignParentTop="true"

            android:layout_alignParentLeft="true"

        android:text="Button1" android:id="@+id/button1"

        android:layout_height="wrap_content"

        android:layout_width="wrap_content"></Button>

    <Button android:layout_alignParentLeft="true"

            android:layout_below="@+id/button1"

        android:text="Button2" android:id="@+id/button2"

        android:layout_height="wrap_content"

        android:layout_width="wrap_content"></Button>

    <Button android:layout_alignParentTop="true"

            android:layout_toRightOf="@+id/button1"

        android:text="Button3" android:id="@+id/button3"

        android:layout_height="wrap_content"

        android:layout_width="wrap_content"></Button>

    <Button android:layout_below="@+id/button2"

            android:layout_alignLeft="@+id/button3"

        android:text="Button4" android:id="@+id/button4"

        android:layout_height="wrap_content"

        android:layout_width="wrap_content"></Button>

</RelativeLayout>

 

Coding Android’s relative layouts can be complicated. I have trouble remembering which android:id goes with which view’s android:layout_ toRightOf. I can easily goof by creating a circular reference. But don’t give up on relative layouts! To avoid relative layouts, people try creating vast nests of linear layouts within other linear layouts. Things go well until someone runs the code. Excessive nesting of linear layouts slows down a processor.

Android has tools to help wean you away from nested linear layouts. One is the hierarchy viewer. The hierarchy viewer’s tree displays the nesting of your layout’s objects. To use the hierarchy viewer, do the following:

  1. Choose Tools  ⇒  Android  ⇒  Android Device Monitor.
  2. In the Android Device Monitor’s menu, choose Window  ⇒  Open Perspective  ⇒  Hierarchy View.
  3. When the hierarchy view opens, look for the Windows tab at the upper-left corner of the viewer. (See Figure 1-15.)
  4. In the viewer’s Windows tab, double-click the item corresponding to your app.
image

Figure 1-15: The hierarchy viewer.

warning If you’re not a total geek, you won’t get the hierarchy viewer to work on a real device. The tool normally works only on an emulator.

Figure 1-15 contains a revealing (okay, okay, a personally embarrassing) hierarchy viewer analysis of one of my recent projects. Look at the Tree Overview in the upper-right panel. Each rounded rectangle represents a view (a layout, for example). The length of the tree (from left to right) shows how deep the nesting goes for this particular project. Some of the tree’s branches are nine levels deep, and the processor can’t draw a view without first calculating the views to its left along the tree’s branches. So the processor chugs slowly as it tries to render the whole scene.

A run of the hierarchy viewer tells you how deeply nested your layouts are. With the viewer’s tree in mind, you can look for ways to eliminate some of the nesting.

Table Layout

A table layout has rows, and each row contains some views. If you do nothing to override the defaults, the views line up to form columns.

For example, the table layout in Figure 1-16 has three table rows. Each table row contains buttons.

<?xml version="1.0" encoding="utf-8"?>

<TableLayout xmlns:android=

  "http://schemas.android.com/apk/res/android"

         android:layout_width="match_parent"

         android:layout_height="match_parent">

  <TableRow>

    <Button android:text="Button"/>

    <Button android:text="Button"/>

    <Button android:text="Button"/>

  </TableRow>

  <TableRow>

    <Button android:text="Button"/>

    <Button android:text="Wide button"/>

  </TableRow>

  <TableRow>

    <Button android:text="Button"/>

    <Button android:text="Btn."/>

    <Button android:text="Button"/>

  </TableRow>

</TableLayout>

image

Figure 1-16: Buttons in a table layout.

In Figure 1-16, notice how each column widens to accommodate the largest item in the column. Notice also how each item expands to fill its entire column.

What? You say you want the leftmost column to be wider? Just add the android:stretchColumns attribute to your <TableLayout> start tag:

<TableLayout xmlns:android=

                 "http://schemas.android.com/apk/res/android"

             android:layout_width="match_parent"

             android:layout_height="match_parent"

             android:stretchColumns="0">

 

The leftmost column has number 0, so when you write stretchColumns="0", you get a layout like the one in Figure 1-17.

image

Figure 1-17: Stretching a column.

Do you want more than one column to be stretched? Then name several column numbers in your stretchColumns attribute:

<TableLayout xmlns:android=

                 "http://schemas.android.com/apk/res/android"

             android:layout_width="match_parent"

             android:layout_height="match_parent"

             android:stretchColumns="0,1,2">

 

When you do, you get the layout shown in Figure 1-18.

image

Figure 1-18: Stretching several columns.

You can move the Wide button back in Figure 1-16 to the rightmost column:

<TableRow>

  <Button android:text="Button"/>

  <Button android:text="Wide button"

          android:layout_column="2"/>

</TableRow>

 

The resulting layout is shown in Figure 1-19.

image

Figure 1-19: Putting a widget in a particular column.

You can make a cell in the table span several columns:

<TableRow>

  <Button android:text="Button"/>

  <Button android:text="Wide button"

          android:layout_span="2"/>

</TableRow>

 

When you do, you get the layout shown in Figure 1-20.

image

Figure 1-20: Spanning across columns.

Grid Layout

Like a table layout, a grid layout arranges things in rows and columns. But some features make grid layouts different from table layouts. For one thing, you don’t use TableRow elements (or anything like TableRow elements) in a grid layout. Instead, you specify the number of rows and columns in the GridLayout start tag:

<GridLayout xmlns:android=

               "http://schemas.android.com/apk/res/android"

            android:layout_width="match_parent"

            android:layout_height="match_parent"

            android:columnCount="3"

            android:rowCount="3">

    <Button android:text="Button"/>

    <Button android:text="Button"/>

    <Button android:text="Button"/>

    <Button android:text="Button"/>

    <Button android:text="Wide button"/>

    <Button android:text="Button"

            android:layout_row="2"

            android:layout_column="0"/>

    <Button android:text="Btn."/>

    <Button android:text="Button"/>

</GridLayout>

 

This code gives you the layout shown in Figure 1-21. Notice how the layout_row and layout_column attributes force all the buttons after the Wide button into the bottommost row.

image

Figure 1-21: Using a grid layout.

Notice also how the buttons in Figure 1-21 don’t expand to fill their respective columns. Android’s layout_width attribute doesn’t play nicely with the grid layout. So, if you want an item in a grid layout to expand to fill its column, use the android:layout_gravity attribute:

<Button android:text="Button"

        android:layout_row="2"

        android:layout_column="0"/>

  <Button android:layout_gravity="fill_horizontal"

          android:text="Btn."/>

  <Button android:text="Button"/>

 

The resulting layout is shown in Figure 1-22.

image

Figure 1-22: Filling a column in a grid layout.

Frame Layout

A frame layout displays one view. What good is that?

Well, to be more precise (and less sensational), a frame layout displays views one in front of another. (Put on your 3D glasses and think of a frame layout as an outward-pointing linear layout.) Because views tend to cover the stuff behind them, a frame layout normally displays only one view — namely, whatever’s in front.

Frame layouts usually serve one of two purposes:

  • A frame layout might display a small view superimposed on a larger view (such as text on an image).
  • A frame layout might store several views, only one of which is visible at any point in time. Using the frame layout, you change what appears in a certain place on the screen.

This section’s example illustrates both ideas. You start with a word superimposed on an image, which is, in turn, superimposed on top of another image. (See Figure 1-23.)

image

Figure 1-23: Three widgets on a frame layout.

When the user touches the screen, two of the three items disappear. (See Figure 1-24.) The screen cycles through the three images, changing the image whenever the user touches the screen. (See Figures 1-25 and 1-26.)

image

Figure 1-24: Meow!

image

Figure 1-25: The largest of three images.

image

Figure 1-26: The midsize image.

(I know what you’re thinking: “The author looks for excuses to show pictures of his cats.” That’s only partly true. I include lots of illustrations to help you visualize the code’s behavior. Anyway, pictures of cats make perfect clip art. Cats don’t complain when you use their least favorite profiles. Pictures of cats are better than pictures of your family members because readers seldom stalk cats. Best of all, no one’s figured out how to patent the domestic cat. I can’t be sued for putting cat pictures in my book. Not yet, anyway.)

Listing 1-8 shows you the XML code for the app in Figures 1-23 to 1-26.

Listing 1-8: Creating a Frame Layout

<?xml version="1.0" encoding="utf-8"?>

<FrameLayout xmlns:android=

        "http://schemas.android.com/apk/res/android"

    android:id="@+id/mainlayout"

    android:layout_height="fill_parent"

    android:layout_width="fill_parent"

    android:orientation="vertical"

    android:onClick="rotate">

    <ImageView android:src="@drawable/calico"

        android:layout_height="wrap_content"

        android:layout_width="wrap_content"

        android:padding="5px"

        android:layout_gravity="center"

        android:id="@+id/imageViewCalico"/>

    <ImageView android:src="@drawable/burmese"

        android:layout_height="wrap_content"

        android:layout_width="wrap_content"

        android:padding="5px"

        android:layout_gravity="center"

        android:id="@+id/imageViewBurmese"/>

    <TextView android:text="@string/meow"

        android:textColor="#FFF"

        android:textSize="15sp"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:layout_gravity="center"

        android:id="@+id/textView"/>

</FrameLayout>

 

The only important business in Listing 1-8 is the order in which I declare the views. The largest image (the Calico) is the FrameLayout element’s first child. So, in Figure 1-23, the Calico appears behind the other images. If the Burmese’s image was as large as the Calico’s image, you wouldn’t see the edges of the Calico’s image in Figure 1-23. The last element in Listing 1-8 is the text view, so in Figure 1-23, the text is superimposed on top of the other elements.

Listing 1-9 has the code that rotates from image to image.

Listing 1-9: Coding Java with a Frame Layout

package com.allmycode.layouts;

import android.app.Activity;

import android.os.Bundle;

import android.view.View;

import android.widget.ImageView;

import android.widget.TextView;

public class LayoutTesterActivity extends Activity {

  ImageView imageCalico, imageBurmese;

  TextView textView;

  @Override

  public void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.frame);

    imageCalico =

        (ImageView) findViewById(R.id.imageViewCalico);

    imageBurmese =

        (ImageView) findViewById(R.id.imageViewBurmese);

    textView = (TextView) findViewById(R.id.textView);

  }

  int count = 0;

  public void rotate(View view) {

    switch (count++ % 3) {

    case 0:

      textView.setVisibility(View.VISIBLE);

      imageCalico.setVisibility(View.INVISIBLE);

      imageBurmese.setVisibility(View.INVISIBLE);

     break;

    case 1:

      textView.setVisibility(View.INVISIBLE);

      imageCalico.setVisibility(View.VISIBLE);

      imageBurmese.setVisibility(View.INVISIBLE);

      break;

    case 2:

      textView.setVisibility(View.INVISIBLE);

      imageCalico.setVisibility(View.INVISIBLE);

      imageBurmese.setVisibility(View.VISIBLE);

      break;

    }

  }

}

 

When the app starts running, all three views (the two images and the text view) are visible. But then Listing 1-9 cycles from one view to another. Each call to the rotate method makes one view visible and makes the other two views invisible.

Using a ScrollView

Sometimes, an activity’s screen takes up more space than is available on a mobile phone. If that’s likely to happen, you can enclose the activity’s screen in a ScrollView. As its name suggests, a ScrollView lets the user slide things onto the screen as other things slide off. Listing 1-10 shows you some code, and Figure 1-27 shows you the results.

image

Figure 1-27: The screen in Listing 1-10.

Listing 1-10: How to Scroll

<?xml version="1.0" encoding="utf-8"?>

<ScrollView xmlns:android=

              "http://schemas.android.com/apk/res/android"

            android:layout_width="match_parent"

            android:layout_height="wrap_content">

  <LinearLayout

    android:layout_width="match_parent"

    android:layout_height="wrap_content"

    android:orientation="vertical">

    <TextView

      android:layout_width="match_parent"

      android:layout_height="wrap_content"

      android:text="0 1 2 3 4 5 6 7 8 9"

      android:textSize="40sp"/>

    <TextView

      android:layout_width="match_parent"

      android:layout_height="wrap_content"

      android:text="10 11 12 13 14"

      android:textSize="40sp"/>

    <TextView

      android:layout_width="match_parent"

      android:layout_height="wrap_content"

      android:text="15 16 17 18 19"

      android:textSize="40sp"/>

  </LinearLayout>

</ScrollView>

 

In truth, Figure 1-27 doesn’t show you the whole story. If the user slides a finger upward along the screen, the numbers in Figure 1-27 move upward. The smaller numbers scroll off the top of the screen, and larger numbers scroll up onto the bottom of the screen.

remember Inside a quoted string of characters, stands for newline. So, in Listing 1-10, "10 11 12 13 14" means 10, go to the next line, 11, go to the next line, and so on.

The big restriction on a ScrollView is that a ScrollView may contain only one direct child. So, in Listing 1-10, you can’t put three TextView elements directly inside a ScrollView element. Instead, you put the three TextView elements inside some other element (in Listing 1-10, a LinearLayout element) and then put that other element inside the ScrollView.

Defining a Layout in Java Code

XML files are good for describing layouts because XML is declarative. In an XML file, you declare, once and for all, a layout’s characteristics. You declare the presence of a text field and a button. You don’t think of these widgets as actions (“Put a text field here and, sometime later, put a button there”), so you don’t say “Do this, and later, do that” the way you say things in a Java program.

But sometimes, a layout’s look has to change. Imagine an activity in which you have one row consisting of two items — a text field and a button. (See Figure 1-28.)

image

Figure 1-28: A row of widgets.

When you click the button, you want an additional row to appear. (See Figure 1-29.)

image

Figure 1-29: One row turns into two rows.

To make this happen, you start with a regular, old XML layout. (See Listing 1-11.)

Listing 1-11: The Starting Layout

<LinearLayout xmlns:android=

    "http://schemas.android.com/apk/res/android"

        android:orientation="vertical"

        android:id="@+id/linear_layout"

        android:layout_width="match_parent"

        android:layout_height="match_parent">

  <LinearLayout

         android:orientation="horizontal"

         android:layout_width="fill_parent"

         android:layout_height="wrap_content">

         <EditText

           android:layout_width="wrap_content"

           android:layout_height="wrap_content"

           android:hint="@string/type_text_here"/>

         <Button

           android:id="@+id/button"

           android:layout_width="wrap_content"

           android:layout_height="wrap_content"

           android:text="@string/add_a_row"/>

</LinearLayout>

</LinearLayout>

 

The layout in Listing 1-11 describes the screen shown earlier in Figure 1-28.

In your main activity, you respond to button clicks. The code is in Listing 1-12.

Listing 1-12: Creating a New Row of Widgets.

package com.allyourcode.dynamiclayout;

import android.app.Activity;

import android.os.Bundle;

import android.view.View;

import android.widget.Button;

import android.widget.EditText;

import android.widget.LinearLayout;

import android.widget.LinearLayout.LayoutParams;

public class MainActivity extends Activity

                      implements View.OnClickListener {

  LinearLayout rootlayout;

  @Override

  protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);

    rootlayout =

       (LinearLayout) findViewById(R.id.linear_layout);

    Button firstButton = (Button) findViewById(R.id.button);

    firstButton.setOnClickListener(this);

  }

  @Override

  public void onClick(View view) {

    LayoutParams editTextLayoutParams =

        new LayoutParams(LayoutParams.WRAP_CONTENT,

                         LayoutParams.WRAP_CONTENT);

    LayoutParams buttonLayoutParams =

        new LayoutParams(LayoutParams.WRAP_CONTENT,

                         LayoutParams.WRAP_CONTENT);

    LayoutParams rowLayoutParams =

        new LayoutParams(LayoutParams.MATCH_PARENT,

                         LayoutParams.WRAP_CONTENT);

    EditText editText = new EditText(this);

    editText.setLayoutParams(editTextLayoutParams);

    editText.setHint(R.string.type_text_here);

    Button button = new Button(this);

    button.setLayoutParams(buttonLayoutParams);

    button.setText(R.string.add_a_row);

    button.setOnClickListener(this);

    LinearLayout newRow = new LinearLayout(this);

    newRow.setLayoutParams(rowLayoutParams);

    newRow.setOrientation(LinearLayout.HORIZONTAL);

    newRow.addView(editText);

    newRow.addView(button);

    LinearLayout currentRow =

                      ((LinearLayout) view.getParent());

    int currentIndex =

      ((LinearLayout) currentRow.getParent())

                              .indexOfChild(currentRow);

    rootlayout.addView(newRow, currentIndex + 1);

  }

}

 

In Listing 1-12, in the onCreate method, you identify the outmost linear layout and the button that’s currently displayed in that layout. You assign these items to the variables rootLayout and firstButton. You make this very activity be the listener for button clicks. Accordingly, this activity implements the OnClickListener interface and has an onClick method. The onClick method responds when the user clicks the button.

crossreference For a review of the use of OnClickListener and setOnClickListener, refer to Chapter 5 in Book I.

In the body of the onClick method, you do five things:

  • You create instances of the LayoutParams class.

    Each instance sets the width and height for one of the items that you’ll be adding to the user’s screen.

  • You create a new EditText and a new Button.

    You set the properties of these two new items. In particular, you make this activity listen for clicks of your new button.

  • You create a new row.

    The row has a horizontal linear layout.

  • You add the EditText and the Button to the new row.
  • Finally, you add the new row to the rootLayout.

    If you call

    rootlayout.addView(newRow);

    then the new row gets added after all other rows in the rootLayout. In some situations, this is okay. But presumably, in this example, a new row goes immediately below whichever button the user pressed.

    Accordingly, before adding a new row, you want to find out which row contains the button that the user pressed. That’s what this bit of Listing 1-12 code does:

    LinearLayout currentRow =

                   ((LinearLayout) view.getParent());

    int currentIndex =

       ((LinearLayout) currentRow.getParent())

                               .indexOfChild(currentRow);

    With currentIndex equal to the position number of the row containing the pressed button, you call

    rootlayout.addView(newRow, currentIndex + 1);

    You add the newRow one position after the current row.

Sometimes, you need both (XML and Java code)

At the end of Chapter 6 in Book I, I present an activity that has a few check boxes and a button. When the user clicks the button, a web page takes over the entire screen. This behavior is surprising because you call

webView.loadUrl("http://blah-blah-blah);

and the webView in question is only one element in the activity’s layout. Why don’t the other elements stay on the screen?

In early versions of Android, the web page didn’t consume the entire screen, and it would be nice if those good old days weren’t gone forever. To keep the web page from consuming all of the screen’s real estate, declare the webView's position in the activity’s relative layout.

<!-- Disclaimer: This code conveys the general idea

     but this code isn’t real XML. (Sorry about that!) -->

<RelativeLayout …>

  <include

    android:id="@+id/checkboxes_and_button"

    … />

  <WebView

    android:layout_below="@id/checkboxes_and_button"

    … />

</RelativeLayout …>

In addition, you have to sneak back to the main activity’s Java file and add code of the following kind:

webView.setWebViewClient(new WebViewClient() {

  @Override

  public boolean shouldOverrideUrlLoading

              (WebView webView, String url) {

   webView.loadUrl(url);

   return true;

  }

});

This extra Java code overrides the default behavior by letting your activity control the loading of the new URL. The webView in your activity’s layout displays the web page, and the good old days are here again.

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

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