Chapter 24

Defining and Using Styles

Every now and then, you will find some code with a cryptic style attribute in a layout element. For example, in the chapter on threading, the following ProgressBar was presented:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  >
  <ProgressBarandroid:id="@+id/progress"
    style="?android:attr/progressBarStyleHorizontal"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content" />
</LinearLayout>

Something about that magic style attribute changed our ProgressBar from a normal circle to a horizontal bar.

This chapter briefly explores the concept of styles, including how you create them and how you apply them to your own widgets.

Styles: DIY DRY

The purpose of styles is to encapsulate a set of attributes that you intend to use repeatedly, conditionally, or otherwise keep separate from your layouts proper. The primary use case is “don’t repeat yourself” (DRY)—if you have a bunch of widgets that look the same, use a style to use a single definition for “look the same,” rather than copying the look from widget to widget.

That paragraph will make a bit more sense if we look at an example, specifically the Styles/NowStyled sample project. This is the same project we examined in an earlier chapter, with a full-screen button that shows the date and time at which the activity was launched or the button was pushed. In this example, we want to change the appearance of the text on the face of the button, which we will accomplish by using a style.

The res/layout/main.xml file in this project is the same as it was in Chapter 20, but with the addition of a style attribute:

<?xml version="1.0" encoding="utf-8"?>
<Button xmlns:android="http://schemas.android.com/apk/res/android"
  android:id="@+id/button"
  android:text=""
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  style="@style/bigred"
/>

NOTE: Because the style attribute is part of stock XML, and therefore is not in the android namespace, it does not get the android: prefix.

The value, @style/bigred, points to a style resource. Style resources are values resources and can be found in the res/values/ directory in your project, or in other resource sets (e.g., res/values-v11/ for values resources to be used only on API level 11 or higher). The convention is to keep style resources in a styles.xml file, such as the following from the NowStyled project:

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <style name="bigred">
    <item name="android:textSize">30sp</item>
    <item name="android:textColor">#FFFF0000</item>
  </style>
</resources>

The <style> element supplies the name of the style, which is what we use when referring to the style from a layout. The <item> children of the <style> element represent values of attributes to be applied to whatever the style is applied to—in our example, our Button widget. So, our Button will have a comparatively large font (android:textSize set to 30sp) and its text will appear in red (android:textColor set to #FFFF0000).

No changes are needed elsewhere in the project—nothing needs to be adjusted in the manifest, in the Java code of the activity, and so on. Just defining the style and applying it to the widget gives us the result shown in Figure 24–1.

images

Figure 24–1. The Styles/NowStyled sample application

Elements of Style

There are four questions to consider when applying a style:

  • Where do you put the style attributes to say you want to apply a style?
  • Which attributes can you define via a style?
  • How do you inherit from a previously defined style (your own or one from Android)?
  • What values can the attributes have in a style definition?

Where to Apply a Style

The style attribute can be applied to a widget, which affects only that widget.

The style attribute can also be applied to a container, which affects only that container. However, doing this does not automatically style its children. For example, suppose res/layout/main.xml looked instead like this:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    style="@style/bigred"
>
  <Button
    android:id="@+id/button"
    android:text=""
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
  />
</LinearLayout>

The resulting UI would not have the Button text in a big red font, despite the style attribute. The style affects only the container, not the contents of the container.

You can also apply a style to an activity or an application as a whole, in which case it is referred to as a theme, as covered a bit later in this chapter.

The Available Attributes

When styling a widget or container, you can apply any of that widget's or container's attributes in the style itself. So, if it appears in the “XML Attributes” or “Inherited XML Attributes” section of the Android JavaDocs, you can put it in a style.

Note that Android will ignore invalid styles. So, had we applied the bigred style to the LinearLayout as shown above, everything would run fine, just with no visible results. Despite the fact that LinearLayout has no android:textSize or android:textColor attribute, no compile-time failure or runtime exception occurs.

Also, layout directives, such as android:layout_width, can be put in a style.

Inheriting a Style

You can also indicate that you want to inherit style attributes from another style, by specifying a parent attribute on the <style> element. For example, take a look at this style resource (which you will see again in Chapter 28, which covers UI design using the fragment framework):

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <style name="activated" parent="android:Theme.Holo">
    <item name="android:background">?android:attr/activatedBackgroundIndicator</item>
  </style>
</resources>

Here, we are indicating that we want to inherit the Theme.Holo style from within Android. Hence, in addition to specifying all of our own attribute definitions, we are specifying that we want all the attribute definitions from Theme.Holo as well.

In many cases, this will not be necessary. If you do not specify a parent, your attribute definitions will be blended into whatever default style is being applied to the widget or container.

The Possible Values

Typically, the value that you will give the attributes in the style will be some constant, like 30sp or #FFFF0000. Sometimes, though, you may want to perform a bit of indirection, by applying some other attribute value from the theme from which you are inheriting. In that case, you need to use the somewhat cryptic ?android:attr/ syntax, along with a few related magic incantations.

For example, let's look again at this style resource:

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <style name="activated" parent="android:Theme.Holo">
    <item name="android:background">?android:attr/activatedBackgroundIndicator</item>
  </style>
</resources>

Here, we are indicating that the value of android:background is not some constant value, or even a reference to a drawable resource (e.g., @drawable/my_background). Instead, we are referring to the value of some other attribute—activatedBackgroundIndicator—from our inherited theme. Whatever the theme defines as being the activatedBackgroundIndicator is what our background should be.

Sometimes this is applied to a style as a whole. For example, let's look again at the ProgressBar:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  >
  <ProgressBarandroid:id="@+id/progress"
    style="?android:attr/progressBarStyleHorizontal"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content" />
</LinearLayout>

Here, our style attribute—not a style resource—is pointing to a theme-supplied attribute (progressBarStyleHorizontal). If you poke through the Android source code, you will see that this is defined as being a style resource, specifically @android:style/Widget.ProgressBar.Horizontal. Hence, we are saying to Android that we want our ProgressBar styled as @android:style/Widget.ProgressBar.Horizontal, via the indirection of ?android:attr/progressBarStyleHorizontal.

This portion of the Android style system is still very underdocumented, even with the latest release of Android 4.0 Ice Cream Sandwich—the entire inheritance topic is three short paragraphs. Google itself recommends that you look at the Android source code listing the various styles to see what is possible.

This is one place where inheriting a style becomes important. In the first example shown in this section, we inherited from Theme.Holo, because we specifically wanted the activatedBackgroundIndicator value from Theme.Holo. That value might not exist in other styles, or it might not have the value we want.

Themes: A Style by Any Other Name…

Themes are styles applied to an activity or application via an android:theme attribute on the <activity> or <application> element. If the theme you are applying is your own, simply reference it as @style/…, just as you would in a style attribute of a widget. If the theme you are applying comes from Android, though, typically you will use a value with @android:style/ as the prefix, such as @android:style/Theme.Dialog or @android:style/Theme.Light.

In a theme, your focus is not so much on styling widgets, but styling the activity itself. For example, here is the definition of @android:style/Theme.NoTitleBar.Fullscreen:

<!-- Variant of the default (dark) theme that has no title bar and
 fills the entire screen -->
<style name="Theme.NoTitleBar.Fullscreen">
  <item name="android:windowFullscreen">true</item>
  <item name="android:windowContentOverlay">@null</item>
</style>

It specifies that the activity should take over the entire screen, removing the status bar on Android 1.x and 2.x devices (android:windowFullscreen set to true), and the action bar on Android 3.x and 4.x devices. It also specifies that the content overlay—a layout that wraps around your activity's content view—should be set to nothing (android:windowContentOverlay set to @null), having the effect of removing the title bar.

A theme might also specify other styles that are applied to specific widgets. For example, we see the following in the root theme (Theme):

<item name="progressBarStyleHorizontal">@android:style/Widget.ProgressBarimages
.Horizontal</item>

Here, progressBarStyleHorizontal is pointing to @android:style/ Widget.ProgressBar.Horizontal. This is how we are able to reference ?android:attr/progressBarStyleHorizontal in our ProgressBar widget, and we could create our own theme that redefines progressBarStyleHorizontal to point to some other style (e.g., if we want to change the rounded rectangle used for the actual progress bar image itself).

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

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