M7350v1_en_gpl

This commit is contained in:
T
2024-09-09 08:52:07 +00:00
commit f9cc65cfda
65988 changed files with 26357421 additions and 0 deletions
@@ -0,0 +1,598 @@
page.title=Hello, World
@jd:body
<div id="qv-wrapper">
<div id="qv">
<h2>In this document</h2>
<ol>
<li><a href="#platform">Install a Platform</a></li>
<li><a href="#avd">Create an AVD</a></li>
<li><a href="#create">Create the Project</a></li>
<li><a href="#ui">Construct the UI</a></li>
<li><a href="#run">Run the Code</a></li>
<li><a href="#upgrading">Upgrade the UI to an XML Layout</a></li>
<li><a href="#debugging">Debug Your Project</a></li>
<li><a href="#noeclipse">Creating the Project Without Eclipse</a></li>
</ol>
</div>
</div>
<p>As a developer, you know that the first impression
of a development framework is how easy it is to write "Hello,
World." Well, on Android, it's pretty easy.
It's particularly easy if you're using Eclipse as your IDE, because we've provided a
great plugin that handles your project creation and management to greatly speed-up your
development cycles.</p>
<p>This tutorial assumes that you're using Eclipse. If you're not, see
<a href="{@docRoot}guide/developing/other-ide.html">Developing in Other IDEs</a>.
You can then return to this tutorial and ignore anything about Eclipse.</p>
<p>Before you start, you should already have the SDK installed, and if you're
using Eclipse, you should have installed the ADT plugin as well. If you have not
installed these, see <a href="{@docRoot}sdk/installing.html">Installing the
Android SDK</a> and return here when you've completed the installation.</p>
<h2 id="platform">Install a Platform</h2>
<p>To run the Hello World application, you need to install at least one Android
platform in your SDK environment. If you have not already performed this step,
you need to do it now.</p>
<p>To install a platform in Eclipse:</p>
<ol>
<li>In the Android SDK and AVD Manager, choose <strong>Available
Packages</strong> in the left panel.</li>
<li>Click the repository site checkbox to display the components
available for installation.</li>
<li>Select at least one platform to install, and click <strong>Install
Selected</strong>. If you aren't sure which platform to install, use the latest
version.</li>
</ol>
<h2 id="avd">Create an AVD</h2>
<div class="sidebox-wrapper">
<div class="sidebox">
<p>To learn more about how to use AVDs and the options
available to you, refer to the
<a href="{@docRoot}guide/developing/tools/avd.html">Android
Virtual Devices</a> document.</p>
</div>
</div>
<p>In this tutorial, you will run your application in the Android Emulator.
Before you can launch the emulator, you must create an
Android Virtual Device (AVD). An AVD defines the system image and
device settings used by the emulator.</p>
<p>To create an AVD:</p>
<ol>
<li>In Eclipse, choose <strong>Window &gt; Android SDK and AVD Manager</strong>.
<li>Select <strong>Virtual Devices</strong> in the left panel.</li>
<li>Click <strong>New</strong>. </li>
<p>The <strong>Create New AVD</strong> dialog appears.</p>
<li>Type the name of the AVD, such as "my_avd".</li>
<li>Choose a target. The target is the platform (that is, the version of the Android
SDK, such as 2.1) you want to run on the emulator. </li>
<p>You can ignore the rest of the fields for now. </p>
<li>Click <strong>Create AVD</strong>.</li>
</ol>
<h2 id="create">Create a New Android Project</h2>
<p>After you've created an AVD, the next step is to start a new
Android project in Eclipse.</p>
<ol>
<li>From Eclipse, select <strong>File &gt; New &gt; Project</strong>.
<p>If the ADT
Plugin for Eclipse has been successfully installed, the resulting dialog
should have a folder labeled "Android" which should contain
"Android Project". (After you create one or more Android projects, an entry for
"Android XML File" will also be available.)</p>
</li>
<li>Select "Android Project" and click <strong>Next</strong>.<br/>
<a href="images/hello_world_0.png"><img src="images/hello_world_0.png" style="height:230px" alt="" /></a>
</li>
<li>Fill in the project details with the following values:
<ul>
<li><em>Project name:</em> HelloAndroid</li>
<li><em>Application name:</em> Hello, Android</li>
<li><em>Package name:</em> com.example.helloandroid (or your own private namespace)</li>
<li><em>Create Activity:</em> HelloAndroid</li>
</ul>
<p>Click <strong>Finish</strong>.</p>
<a href="images/hello_world_1.png"><img src="images/hello_world_1.png" style="height:400px" alt="" /></a>
<p>Here is a description of each field:</p>
<dl>
<dt><em>Project Name</em></dt>
<dd>This is the Eclipse Project name &mdash; the name of the directory
that will contain the project files.</dd>
<dt><em>Application Name</em></dt>
<dd>This is the human-readable title for your application &mdash; the name that
will appear on the Android device.</dd>
<dt><em>Package Name</em></dt>
<dd>This is the package namespace (following the same rules as for
packages in the Java programming language) that you want all your source code to
reside under. This also sets the package name under which the stub
Activity will be generated.
<p>Your package name must be unique across
all packages installed on the Android system; for this reason, it's
important to use a standard domain-style package for your
applications. The example above uses the "com.example" namespace, which is
a namespace reserved for example documentation &mdash;
when you develop your own applications, you should use a namespace that's
appropriate to your organization or entity.</p></dd>
<dt><em>Create Activity</em></dt>
<dd>This is the name for the class stub that will be generated by the plugin.
This will be a subclass of Android's {@link android.app.Activity} class. An
Activity is simply a class that can run and do work. It can create a UI if it
chooses, but it doesn't need to. As the checkbox suggests, this is optional, but an
Activity is almost always used as the basis for an application.</dd>
<dt><em>Min SDK Version</em></dt>
<dd>This value specifies the minimum API Level required by your application. For
more information, see <a href="{@docRoot}guide/appendix/api-levels.html">Android API Levels</a>.
</dd>
</dl>
<p><em>Other fields</em>: The checkbox for "Use default location" allows you to change
the location on disk where the project's files will be generated and stored. "Build Target"
is the platform target that your application will be compiled against
(this should be selected automatically, based on your Min SDK Version).</p>
<p class="note">Notice that the "Build Target" you've selected uses the Android 1.1
platform. This means that your application will be compiled against the Android 1.1
platform library. If you recall, the AVD created above runs on the Android 1.5 platform.
These don't have to match; Android applications are forward-compatible, so an application
built against the 1.1 platform library will run normally on the 1.5 platform. The reverse
is not true.</p>
</li>
</ol>
<p>Your Android project is now ready. It should be visible in the Package
Explorer on the left.
Open the <code>HelloAndroid.java</code> file, located inside <em>HelloAndroid > src >
com.example.helloandroid</em>). It should look like this:</p>
<pre>
package com.example.helloandroid;
import android.app.Activity;
import android.os.Bundle;
public class HelloAndroid extends Activity {
/** Called when the activity is first created. */
&#64;Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}</pre>
<p>Notice that the class is based on the {@link android.app.Activity} class. An Activity is a
single application entity that is used to perform actions. An application may have many separate
activities, but the user interacts with them one at a time. The
{@link android.app.Activity#onCreate(Bundle) onCreate()} method
will be called by the Android system when your Activity starts &mdash;
it is where you should perform all initialization and UI setup. An activity is not required to
have a user interface, but usually will.</p>
<p>Now let's modify some code! </p>
<h2 id="ui">Construct the UI</h2>
<p>Take a look at the revised code below and then make the same changes to your HelloAndroid class.
The bold items are lines that have been added.</p>
<pre>
package com.example.helloandroid;
import android.app.Activity;
import android.os.Bundle;
<strong>import android.widget.TextView;</strong>
public class HelloAndroid extends Activity {
/** Called when the activity is first created. */
&#64;Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
<strong>TextView tv = new TextView(this);
tv.setText(&quot;Hello, Android&quot;);
setContentView(tv);</strong>
}
}</pre>
<p class="note"><strong>Tip:</strong> An easy way to add import packages to your project is
to press <strong>Ctrl-Shift-O</strong> (<strong>Cmd-Shift-O</strong>, on Mac). This is an Eclipse
shortcut that identifies missing packages based on your code and adds them for you.</p>
<p>An Android user interface is composed of hierarchies of objects called
Views. A {@link android.view.View} is a drawable object used as an element in your UI layout,
such as a button, image, or (in this case) a text label. Each of these objects is a subclass
of the View class and the subclass that handles text is {@link android.widget.TextView}.</p>
<p>In this change, you create a TextView with the class constructor, which accepts
an Android {@link android.content.Context} instance as its parameter. A
Context is a handle to the system; it provides services like
resolving resources, obtaining access to databases and preferences, and so
on. The Activity class inherits from Context, and because your
HelloAndroid class is a subclass of Activity, it is also a Context. So, you can
pass <code>this</code> as your Context reference to the TextView.</p>
<p>Next, you define the text content with
{@link android.widget.TextView#setText(CharSequence) setText()}.</p>
<p>Finally, you pass the TextView to
{@link android.app.Activity#setContentView(View) setContentView()} in order to
display it as the content for the Activity UI. If your Activity doesn't
call this method, then no UI is present and the system will display a blank
screen.</p>
<p>There it is &mdash; "Hello, World" in Android! The next step, of course, is
to see it running.</p>
<h2 id="run">Run the Application</h2>
<p>The Eclipse plugin makes it easy to run your applications:</p>
<ol>
<li>Select <strong>Run > Run</strong>.</li>
<li>Select "Android Application".</li>
</ol>
<div class="sidebox-wrapper">
<div class="sidebox">
<p>To learn more about creating and editing run configurations in Eclipse, refer to
<a href="{@docRoot}guide/developing/eclipse-adt.html#RunConfig">Developing In Eclipse,
with ADT</a>.</p>
</div>
</div>
<p>The Eclipse plugin automatically creates a new run configuration for your project
and then launches the Android Emulator. Depending on your environment, the Android
emulator might take several minutes to boot fully, so please be patient. When the
emulator is booted, the Eclipse plugin installs your application
and launches the default Activity. You should now see something like this:</p>
<a href="images/hello_world_5.png"><img src="images/hello_world_5.png" style="height:230px" alt="" /></a>
<p>The "Hello, Android" you see in the grey bar is actually the application title. The Eclipse plugin
creates this automatically (the string is defined in the <code>res/values/strings.xml</code> file and referenced
by your <code>AndroidManifest.xml</code> file). The text below the title is the actual text that you have
created in the TextView object.</p>
<p>That concludes the basic "Hello World" tutorial, but you should continue reading for some more
valuable information about developing Android applications.</p>
<h2 id="upgrading">Upgrade the UI to an XML Layout</h2>
<p>The "Hello, World" example you just completed uses what is called a "programmatic"
UI layout. This means that you constructed and built your application's UI
directly in source code. If you've done much UI programming, you're
probably familiar with how brittle that approach can sometimes be: small
changes in layout can result in big source-code headaches. It's also
easy to forget to properly connect Views together, which can result in errors in
your layout and wasted time debugging your code.</p>
<p>That's why Android provides an alternate UI construction model: XML-based
layout files. The easiest way to explain this concept is to show an
example. Here's an XML layout file that is identical in behavior to the
programmatically-constructed example:</p>
<pre>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;TextView xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
android:id=&quot;@+id/textview&quot;
android:layout_width=&quot;fill_parent&quot;
android:layout_height=&quot;fill_parent&quot;
android:text=&quot;@string/hello&quot;/&gt;</pre>
<p>The general structure of an Android XML layout file is simple: it's a tree
of XML elements, wherein each node is the name of a View class
(this example, however, is just one View element). You can use the
name of any class that extends {@link android.view.View} as an element in your XML layouts,
including custom View classes you define in your own code. This
structure makes it easy to quickly build up UIs, using a more simple
structure and syntax than you would use in a programmatic layout. This model is inspired
by the web development model, wherein you can separate the presentation of your
application (its UI) from the application logic used to fetch and fill in data.</p>
<p>In the above XML example, there's just one View element: the <code>TextView</code>,
which has five XML attributes. Here's a summary of what they mean:</p>
<table>
<tbody>
<tr>
<th>
Attribute
</th>
<th>
Meaning
</th>
</tr>
<tr>
<td>
<code>xmlns:android</code>
</td>
<td>
This is an XML namespace declaration that tells the Android tools that you are going to refer to common attributes defined in the Android namespace. The outermost tag in every Android layout file must have this attribute.<br>
</td>
</tr>
<tr>
<td>
<code>android:id</code>
</td>
<td>
This attribute assigns a unique identifier to the <code>TextView</code> element.
You can use the assigned ID to reference this View from your source code or from other
XML resource declarations.
</td>
</tr>
<tr>
<td>
<code>android:layout_width</code>
</td>
<td>
This attribute defines how much of the available width on the screen this View should consume.
In this case, it's the only View so you want it to take up the entire screen, which is what a value of "fill_parent" means.<br>
</td>
</tr>
<tr>
<td>
<code>android:layout_height</code>
</td>
<td>
This is just like android:layout_width, except that it refers to available screen height.
</td>
</tr>
<tr>
<td>
<code>android:text</code>
</td>
<td>
This sets the text that the TextView should display. In this example, you use a string
resource instead of a hard-coded string value.
The <em>hello</em> string is defined in the <em>res/values/strings.xml</em> file. This is the
recommended practice for inserting strings to your application, because it makes the localization
of your application to other languages graceful, without need to hard-code changes to the layout file.
For more information, see <a href="{@docRoot}guide/topics/resources/resources-i18n.html">Resources
and Internationalization</a>.
</td>
</tr>
</tbody>
</table>
<p>These XML layout files belong in the <code>res/layout/</code> directory of your project. The "res" is
short for "resources" and the directory contains all the non-code assets that
your application requires. In addition to layout files, resources also include assets
such as images, sounds, and localized strings.</p>
<div class="sidebox-wrapper">
<div class="sidebox">
<h2>Landscape layout</h2>
<p>When you want a different design for landscape, put your layout XML file
inside /res/layout-land. Android will automatically look here when the layout changes.
Without this special landscape layout defined, Android will stretch the default layout.</p>
</div>
</div>
<p>The Eclipse plugin automatically creates one of these layout files for you: main.xml.
In the "Hello World" application you just completed, this file was ignored and you created a
layout programmatically. This was meant to teach you more
about the Android framework, but you should almost always define your layout
in an XML file instead of in your code.
The following procedures will instruct you how to change your
existing application to use an XML layout.</p>
<ol>
<li>In the Eclipse Package Explorer, expand the
<code>/res/layout/</code> folder and open <code>main.xml</code> (once opened, you might need to click
the "main.xml" tab at the bottom of the window to see the XML source). Replace the contents with
the following XML:
<pre>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;TextView xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
android:id=&quot;@+id/textview&quot;
android:layout_width=&quot;fill_parent&quot;
android:layout_height=&quot;fill_parent&quot;
android:text=&quot;@string/hello&quot;/&gt;</pre>
<p>Save the file.</p>
</li>
<li>Inside the <code>res/values/</code> folder, open <code>strings.xml</code>.
This is where you should save all default text strings for your user interface. If you're using Eclipse, then
ADT will have started you with two strings, <em>hello</em> and <em>app_name</em>.
Revise <em>hello</em> to something else. Perhaps "Hello, Android! I am a string resource!"
The entire file should now look like this:
<pre>
&lt;?xml version="1.0" encoding="utf-8"?>
&lt;resources>
&lt;string name="hello">Hello, Android! I am a string resource!&lt;/string>
&lt;string name="app_name">Hello, Android&lt;/string>
&lt;/resources>
</pre>
</li>
<li>Now open and modify your <code>HelloAndroid</code> class use the
XML layout. Edit the file to look like this:
<pre>
package com.example.helloandroid;
import android.app.Activity;
import android.os.Bundle;
public class HelloAndroid extends Activity {
/** Called when the activity is first created. */
&#64;Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}</pre>
<p>When you make this change, type it by hand to try the
code-completion feature. As you begin typing "R.layout.main" the plugin will offer you
suggestions. You'll find that it helps in a lot of situations.</p>
<p>Instead of passing <code>setContentView()</code> a View object, you give it a reference
to the layout resource.
The resource is identified as <code>R.layout.main</code>, which is actually a compiled object representation of
the layout defined in <code>/res/layout/main.xml</code>. The Eclipse plugin automatically creates this reference for
you inside the project's R.java class. If you're not using Eclipse, then the R.java class will be generated for you
when you run Ant to build the application. (More about the R class in a moment.)</p>
</li>
</ol>
<p>Now re-run your application &mdash; because you've created a launch configuration, all
you need to do is click the green arrow icon to run, or select
<strong>Run &gt; Run History &gt; Android Activity</strong>. Other than the change to the TextView
string, the application looks the same. After all, the point was to show that the two different
layout approaches produce identical results.</p>
<p class="note"><strong>Tip:</strong> Use the shortcut <strong>Ctrl-F11</strong>
(<strong>Cmd-Shift-F11</strong>, on Mac) to run your currently visible application.</p>
<p>Continue reading for an introduction
to debugging and a little more information on using other IDEs. When you're ready to learn more,
read <a href="{@docRoot}guide/topics/fundamentals.html">Application
Fundamentals</a> for an introduction to all the elements that make Android applications work.
Also refer to the <a href="{@docRoot}guide/index.html">Developer's Guide</a>
introduction page for an overview of the <em>Dev Guide</em> documentation.</p>
<div class="special">
<h3>R class</h3>
<p>In Eclipse, open the file named <code>R.java</code> (in the <code>gen/</code> [Generated Java Files] folder).
It should look something like this:</p>
<pre>
package com.example.helloandroid;
public final class R {
public static final class attr {
}
public static final class drawable {
public static final int icon=0x7f020000;
}
public static final class id {
public static final int textview=0x7f050000;
}
public static final class layout {
public static final int main=0x7f030000;
}
public static final class string {
public static final int app_name=0x7f040001;
public static final int hello=0x7f040000;
}
}
</pre>
<p>A project's <code>R.java</code> file is an index into all the resources defined in the
file. You use this class in your source code as a sort of short-hand
way to refer to resources you've included in your project. This is
particularly powerful with the code-completion features of IDEs like Eclipse
because it lets you quickly and interactively locate the specific reference
you're looking for.</p>
<p>It's possible yours looks slighly different than this (perhaps the hexadecimal values are different).
For now, notice the inner class named "layout", and its
member field "main". The Eclipse plugin noticed the XML
layout file named main.xml and generated a class for it here. As you add other
resources to your project (such as strings in the <code>res/values/string.xml</code> file or drawables inside
the <code>res/drawable/</code> direcory) you'll see <code>R.java</code> change to keep up.</p>
<p>When not using Eclipse, this class file will be generated for you at build time (with the Ant tool).</p>
<p><em>You should never edit this file by hand.</em></p>
</div>
<h2 id="debugging">Debug Your Project</h2>
<p>The Android Plugin for Eclipse also has excellent integration with the Eclipse
debugger. To demonstrate this, introduce a bug into
your code. Change your HelloAndroid source code to look like this:</p>
<pre>
package com.example.helloandroid;
import android.app.Activity;
import android.os.Bundle;
public class HelloAndroid extends Activity {
/** Called when the activity is first created. */
&#64;Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Object o = null;
o.toString();
setContentView(R.layout.main);
}
}</pre>
<p>This change simply introduces a NullPointerException into your code. If
you run your application again, you'll eventually see this:</p>
<a href="images/hello_world_8.png"><img src="images/hello_world_8.png" style="height:230px" alt="" /></a>
<p>Press "Force Quit" to terminate the application and close the emulator window.</p>
<p>To find out more about the error, set a breakpoint in your source code
on the line <code>Object o = null;</code> (double-click on the marker bar next to the source code line). Then select <strong>Run &gt; Debug History &gt; Hello,
Android</strong> from the menu to enter debug mode. Your app will restart in the
emulator, but this time it will suspend when it reaches the breakpoint you
set. You can then step through the code in Eclipse's Debug Perspective,
just as you would for any other application.</p>
<a href="images/hello_world_9.png"><img src="images/hello_world_9.png" style="height:230px" alt="" /></a>
<h2 id="noeclipse">Creating the Project without Eclipse</h2>
<p>If you don't use Eclipse (such as if you prefer another IDE, or simply use text
editors and command line tools) then the Eclipse plugin can't help you.
Don't worry though &mdash; you don't lose any functionality just because you don't
use Eclipse.</p>
<p>The Android Plugin for Eclipse is really just a wrapper around a set of tools
included with the Android SDK. (These tools, like the emulator, aapt, adb,
ddms, and others are <a href="{@docRoot}guide/developing/tools/index.html">documented elsewhere.</a>)
Thus, it's possible to
wrap those tools with another tool, such as an 'ant' build file.</p>
<p>The Android SDK includes a tool named "android" that can be
used to create all the source code and directory stubs for your project, as well
as an ant-compatible <code>build.xml</code> file. This allows you to build your project
from the command line, or integrate it with the IDE of your choice.</p>
<p>For example, to create a HelloAndroid project similar to the one created
in Eclipse, use this command:</p>
<pre>
android create project \
--package com.example.helloandroid \
--activity HelloAndroid \
--target 2 \
--path <em>&lt;path-to-your-project></em>/HelloAndroid
</pre>
<p>This creates the required folders and files for the project at the location
defined by the <em>path</em>.</p>
<p>For more information on how to use the SDK tools to create and build projects, please read
<a href="{@docRoot}guide/developing/other-ide.html">Developing in Other IDEs</a>.</p>
Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

@@ -0,0 +1,8 @@
<html>
<head>
<meta http-equiv="refresh" content="0;url=../index.html">
</head>
<body>
<a href="../index.html">click here</a> if you are not redirected.
</body>
</html>
+593
View File
@@ -0,0 +1,593 @@
page.title=Hello, L10N
@jd:body
<div id="qv-wrapper">
<div id="qv">
<h2>In this document</h2>
<ol>
<li><a href="#unlocalized">Create an Unlocalized App</a>
<ol>
<li><a href="#create">Create the Project and Layout</a></li>
<li><a href="#default">Create Default Resources</a></li>
</ol>
</li>
<li><a href="#run">Run the Unlocalized App</a></li>
<li><a href="#plan">Plan the Localization</a></li>
<li><a href="#localize">Localize the App</a>
<ol>
<li><a href="#localize_strings">Localize the Strings</a></li>
<li><a href="#localize_images">Localize the Images</a></li>
</ol>
</li>
<li><a href="#test_localized">Run and Test the Localized App</a></li>
</ol>
<h2>See also</h2>
<ol>
<li>{@link android.widget.Button}</li>
<li>{@link android.widget.TextView}</li>
<li>{@link android.app.AlertDialog}</li>
</ol>
</div>
</div>
<p>In this tutorial, we will create a Hello, L10N application that uses the
Android framework to selectively load resources. Then we will localize the
application by adding resources to the <code>res/</code> directory. </p>
<p>This tutorial uses the practices described in the <a
href="{@docRoot}guide/topics/resources/localization.html">Localization</a>
document. </p>
<h2 id="unlocalized">Create an Unlocalized Application</h2>
<p>The first version of the Hello, L10N application will use only the default
resource directories (<code>res/drawable</code>, <code>res/layout</code>, and
<code>res/values</code>). These resources are not localized &#8212; they are the
graphics, layout, and strings that we expect the application to use most often.
When a user runs the application in the default locale, or in a locale that the
application does not specifically support, the application will load resources
from these default directories.</p>
<p>The application consists of a simple user interface that displays two
{@link android.widget.TextView} objects and a {@link android.widget.Button} image with a
background image of a national flag. When clicked, the button displays an
{@link android.app.AlertDialog} object that shows additional text. </p>
<h3 id="create">Create the Project and Layout</h3>
<p>For this application, the default language will be British English and the
default location the United Kingdom. </p>
<ol>
<li>Start a new project and Activity called &quot;HelloL10N.&quot; If you are
using Eclipse, fill out these values in the New Android Project wizard:
<ul>
<li><em>Project name:</em> HelloL10N</li>
<li><em>Application name:</em> Hello, L10N</li>
<li><em>Package name:</em> com.example.hellol10n (or your own private
namespace)</li>
<li><em>Create Activity:</em> HelloL10N</li>
<li><em>Min SDK Version:</em> 3</li>
</ul>
<p>The basic project contains a <code>res/</code> directory with
subdirectories for the three most common types of resources: graphics
(<code>res/drawable/</code>), layouts (<code>res/layout/</code>) and strings
(<code>res/values/</code>). Most of the localization work you do later in this
tutorial will involve adding more subdirectories to the <code>res/</code>
directory.</p>
<img src="{@docRoot}images/hello_l10n/plain_project.png" alt="plain project" width="194"
height="229">
</li>
<li>Open the <code>res/layout/main.xml</code> file and replace it with the
following code:
<pre>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;LinearLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
android:orientation=&quot;vertical&quot;
android:layout_width=&quot;fill_parent&quot;
android:layout_height=&quot;fill_parent&quot;
&gt;
&lt;TextView
android:layout_width=&quot;fill_parent&quot;
android:layout_height=&quot;wrap_content&quot;
android:gravity=&quot;center_horizontal&quot;
android:text=&quot;@string/text_a&quot;
/&gt;
&lt;TextView
android:layout_width=&quot;fill_parent&quot;
android:layout_height=&quot;wrap_content&quot;
android:gravity=&quot;center_horizontal&quot;
android:text=&quot;@string/text_b&quot;
/&gt;
&lt;Button
android:id=&quot;@+id/flag_button&quot;
android:layout_width=&quot;wrap_content&quot;
android:layout_height=&quot;wrap_content&quot;
android:layout_gravity=&quot;center&quot;
/&gt;
&lt;/LinearLayout&gt;
</pre>
<p>The LinearLayout has two {@link android.widget.TextView} objects that will
display localized text and one {@link android.widget.Button} that shows a flag.
</p>
</li>
</ol>
<h3 id="default">Create Default Resources</h3>
<p>The layout refers to resources that need to be defined. </p>
<ol>
<li>Create default text strings. To do this, open the <code>res/values/strings.xml</code> file and replace it with the following code:<br>
<pre>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;resources&gt;
&lt;string name=&quot;app_name&quot;&gt;Hello, L10N&lt;/string&gt;
&lt;string name=&quot;text_a&quot;&gt;Shall I compare thee to a summer&quot;'&quot;s day?&lt;/string&gt;
&lt;string name=&quot;text_b&quot;&gt;Thou art more lovely and more temperate.&lt;/string&gt;
&lt;string name=&quot;dialog_title&quot;&gt;No Localisation&lt;/string&gt;
&lt;string name=&quot;dialog_text&quot;&gt;This dialog box&quot;'&quot;s strings are not localised. For every locale, the text here will come from values/strings.xml.&lt;/string&gt;
&lt;/resources&gt;</pre>
<p>This code provides British English text for each string that the application
will use. When we localize this application, we will provide alternate text in
German, French, and Japanese for some of the strings.</p>
</li>
<li>Add a default flag graphic to the <code>res/drawable</code> folder by
saving <a href="../../../images/hello_l10n/flag.png">flag.png</a> as
<code>res/drawable/flag.png</code>. When the application is not localized, it
will show a British flag.<br>
</li>
<li>Open HelloL10N.java (in the <code>src/</code> directory) and add the
following code inside the <code>onCreate()</code> method (after
<code>setContentView</code>).
<pre>// assign flag.png to the button, loading correct flag image for current locale
Button b;
(b = (Button)findViewById(R.id.flag_button)).setBackgroundDrawable(this.getResources().getDrawable(R.drawable.flag));
// build dialog box to display when user clicks the flag
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(R.string.dialog_text)
.setCancelable(false)
.setTitle(R.string.dialog_title)
.setPositiveButton("Done", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.dismiss();
}
});
final AlertDialog alert = builder.create();
// set click listener on the flag to show the dialog box
b.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
alert.show();
}
});</pre>
<p class="note"><strong>Tip:</strong> In Eclipse, use
<strong>Ctrl-Shift-O</strong> (<strong>Cmd-Shift-O</strong>, on Mac) to find and
add missing import packages to your project, then save the HelloL10N.java
file.</p>
<p>The code that you added does the following:</p>
<ul>
<li>It assigns the correct flag icon to the button.
For now, no resources are defined other than the default, so this code
will always assign the contents of <code>res/drawable/flag.png</code> (the
British flag) as the flag icon, no matter what the locale. Once we add more
flags for different locales, this code will sometimes assign a different flag.
</li>
<li>It creates an {@link android.app.AlertDialog} object and sets a click listener so that when the
user clicks the button, the AlertDialog will display.
We will not localize the dialog text;
the AlertDialog will always display the <code>dialog_text</code> that is located
within <code>res/values/strings.xml</code>. </li>
</ul>
</li>
</ol>
<p>The project structure now looks like this:</p>
<img src="{@docRoot}images/hello_l10n/nonlocalized_project.png" alt="nonlocalized" width="394"
height="320">
<p class="note"><strong>Tip:</strong> If you will want to run the application on
a device and not just on an emulator, open <code>AndroidManifest.xml</code> and
add <code>android:debuggable="true"</code> inside the
<code>&lt;application&gt;</code> element. For information about setting up the
device itself so it can run applications from your system, see <a
href="{@docRoot}guide/developing/device.html">Developing on a Device</a>.</p>
<h2 id="run">Run the Unlocalized Application</h2>
<p>Save the project and run the application to see how it works. No matter what
locale your device or emulator is set to, the application runs the same way. It
should look something like this:</p>
<table border="0" cellspacing="0" cellpadding="30">
<tr>
<th scope="col">The unlocalized application, running in any locale:</th>
<th scope="col">After clicking the flag, in any locale:</th>
</tr>
<tr>
<td valign="top"><img src="{@docRoot}images/hello_l10n/nonlocalized_screenshot1.png"
alt="nonlocalized" width="321" height="366"></td>
<td><img src="{@docRoot}images/hello_l10n/nonlocalized_screenshot2.png" alt="nonlocalized2"
width="321" height="366"></td>
</tr>
</table>
<h2 id="plan">Plan the Localization</h2>
<p>The first step in localizing an application is to plan how the application
will render differently in different locales. In this application, the default
locale will be the United Kingdom. We will add some locale-specific information
for Germany, France, Canada, Japan, and the United States. Table 1 shows the
plan for how the application will appear in different locales.</p>
<p class="caption">Table 1</p>
<table border="0" cellspacing="0" cellpadding="10">
<tr>
<th scope="col" valign="bottom">Region /<br />
Language</th>
<th scope="col">United Kingdom</th>
<th scope="col">Germany</th>
<th scope="col">France</th>
<th scope="col">Canada</th>
<th scope="col">Japan</th>
<th scope="col">United States</th>
<th scope="col">Other Location</th>
</tr>
<tr>
<th scope="row"><br>
English</th>
<td> British English text; British flag <em>(default)</em></td>
<td><em>-</em></td>
<td><em>-</em></td>
<td> British English text; Canadian flag</td>
<td>-</td>
<td> British English text; U.S. flag</td>
<td> British English text; British flag <em>(default)</em></td>
</tr>
<tr>
<th scope="row">German</th>
<td>-</td>
<td>German text for <code>app_name</code>, <code>text_a</code> and
<code>text_b</code>; German flag</td>
<td>-</td>
<td>-</td>
<td>-</td>
<td>-</td>
<td>German text for <code>app_name</code>, <code>text_a</code> and
<code>text_b</code>; British flag</td>
</tr>
<tr>
<th scope="row">French</th>
<td>-</td>
<td>-</td>
<td>French text for <code>app_name</code>, <code>text_a</code> and
<code>text_b</code>; French flag</td>
<td>French text for <code>app_name</code>, <code>text_a</code> and
<code>text_b</code>; Canadian flag</td>
<td>-</td>
<td>-</td>
<td>French text for <code>app_name</code>, <code>text_a</code> and
<code>text_b</code>; British flag</td>
</tr>
<tr>
<th scope="row">Japanese</th>
<td>-</td>
<td>-</td>
<td>-</td>
<td>-</td>
<td>Japanese text for <code>text_a</code> and <code>text_b</code>; Japanese
flag</td>
<td>-</td>
<td>Japanese text for <code>text_a</code> and <code>text_b</code>; British
flag</td>
</tr>
<tr>
<th scope="row">Other Language</th>
<td>-</td>
<td>-</td>
<td>-</td>
<td>-</td>
<td>-</td>
<td>-</td>
<td> British English text; British flag <em>(default)</em></td>
</tr>
</table>
<p class="note"> Note that other behaviors are possible; for example, the
application could support Canadian English or U.S. English text. But given the
small amount of text involved, adding more versions of English would not make
this application more useful.</p>
<p>As shown in the table above, the plan calls for five flag icons in addition
to the British flag that is already in the <code>res/drawable/</code> folder. It
also calls for three sets of text strings other than the text that is in
<code>res/values/strings.xml</code>.</p>
<p>Table 2 shows where the needed text strings and flag icons will go, and
specifies which ones will be loaded for which locales. (For more about the
locale codes, <em></em>see <a
href="{@docRoot}guide/topics/resources/resources-i18n.html#AlternateResources">
Alternate Resources</a>.)</p>
<p class="caption" id="table2">Table 2</p>
<table border="1" cellspacing="0" cellpadding="5">
<tr>
<th scope="col">Locale Code</th>
<th scope="col">Language / Country</th>
<th scope="col">Location of strings.xml</th>
<th scope="col">Location of flag.png</th>
</tr>
<tr>
<td><em>Default</em></td>
<td>English / United Kingdom</td>
<td>res/values/</td>
<td>res/drawable/</td>
</tr>
<tr>
<td>de-rDE</td>
<td>German / Germany</td>
<td>res/values-de/</td>
<td>res/drawable-de-rDE/</td>
</tr>
<tr>
<td>fr-rFR</td>
<td>French / France</td>
<td>res/values-fr/</td>
<td>res/drawable-fr-rFR/</td>
</tr>
<tr>
<td>fr-rCA</td>
<td>French / Canada</td>
<td>res/values-fr/</td>
<td>res/drawable-fr-rCA/</td>
</tr>
<tr>
<td>en-rCA</td>
<td>English / Canada</td>
<td><em>(res/values/)</em></td>
<td>res/drawable-en-rCA/</td>
</tr>
<tr>
<td>ja-rJP</td>
<td>Japanese / Japan</td>
<td>res/values-ja/</td>
<td>res/drawable-ja-rJP/</td>
</tr>
<tr>
<td>en-rUS</td>
<td>English / United States</td>
<td><em>(res/values/)</em></td>
<td>res/drawable-en-rUS/</td>
</tr>
</table>
<p class="note"><strong>Tip: </strong>A folder qualifer cannot specify a region
without a language. Having a folder named <code>res/drawable-rCA/</code>,
for example, will prevent the application from compiling. </p>
<p>At run time, the application will select a set of resources to load based on the locale
that is set in the user's device. In cases where no locale-specific resources
are available, the application will fall back on the defaults. </p>
<p>For example, assume that the device's language is set to German and its
location to Switzerland. Because this application does not have a
<code>res/drawable-de-rCH/</code> directory with a <code>flag.png</code> file in it, the system
will fall back on the default, which is the UK flag located in
<code>res/drawable/flag.png</code>. The language used will be German. Showing a
British flag to German speakers in Switzerland is not ideal, but for now we will
just leave the behavior as it is. There are several ways you could improve this
application's behavior if you wanted to:</p>
<ul>
<li>Use a generic default icon. In this application, it might be something
that represents Shakespeare. </li>
<li>Create a <code>res/drawable-de/</code> folder that includes an icon that
the application will use whenever the language is set to German but the location
is not Germany. </li>
</ul>
<h2 id="localize">Localize the Application</h2>
<h3 id="localize_strings">Localize the Strings</h3>
<p>The application requires three more <code>strings.xml</code> files, one
each for German, French, and Japanese. To create these resource files within
Eclipse:</p>
<ol>
<li>Select <strong>File</strong> &gt; <strong>New</strong> &gt; <strong>Android
XML File</strong> to open the New Android XML File wizard. You can also open
the wizard by clicking its icon in the toolbar:<br />
<img src="{@docRoot}images/hello_l10n/xml_file_wizard_shortcut.png"
alt="file_wizard_shortcut" width="297"
height="90" style="margin:15px"></li>
<li>Select L10N for the Project field, and type <code>strings.xml</code> into
the File field. In the left-hand list, select Language, then click the right arrow.<br>
<img src="{@docRoot}images/hello_l10n/xml_wizard1.png" alt="res_file_copy" width="335"
height="406" style="margin:15px"></li>
<li>Type <code>de</code> in the Language box and click Finish.<br>
<img src="{@docRoot}images/hello_l10n/xml_wizard2.png" alt="res_file_copy" width="306"
height="179">
<p>A new file, <code>res/values-de/strings.xml</code>, now appears among the project
files.</p></li>
<li>Repeat the steps twice more, for the language codes <code>fr</code> and
<code>ja</code>.
Now the project includes these new skeleton files: <br />
<code>res/<strong>values-de</strong>/strings.xml</code><br />
<code>res/<strong>values-fr</strong>/strings.xml</code><br />
<code>res/<strong>values-ja</strong>/strings.xml</code><br />
</li>
<li>Add localized text to the new files. To do
this, open the <code>res/values-<em>&lt;qualifier&gt;</em>/strings.xml</code> files and
replace the code as follows:</li>
</ol>
<table border="0" cellspacing="0" cellpadding="0">
<tr>
<th scope="col">File</th>
<th scope="col">Replace the contents with the following code:</th>
</tr>
<tr>
<td><code>res/values-de/strings.xml</code></td>
<td><pre>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;resources&gt;
&lt;string name=&quot;app_name&quot;&gt;Hallo, Lokalisierung&lt;/string&gt;
&lt;string name=&quot;text_a&quot;&gt;Soll ich dich einem Sommertag vergleichen,&lt;/string&gt;
&lt;string name=&quot;text_b&quot;&gt;Der du viel lieblicher und sanfter bist?&lt;/string&gt;
&lt;/resources&gt;</pre></td>
</tr>
<tr>
<td><code>res/values-fr/strings.xml</code></td>
<td><pre>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;resources&gt;
&lt;string name=&quot;app_name&quot;&gt;Bonjour, Localisation&lt;/string&gt;
&lt;string name=&quot;text_a&quot;&gt;Irai-je te comparer au jour d'été?&lt;/string&gt;
&lt;string name=&quot;text_b&quot;&gt;Tu es plus tendre et bien plus tempéré.&lt;/string&gt;
&lt;/resources&gt; </pre></td>
</tr>
<tr>
<td><code>res/values-ja/strings.xml</code></td>
<td>
<pre>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;resources&gt;
&lt;string name=&quot;text_a&quot;&gt;あなたをなにかにたとえるとしたら夏の一日でしょうか?&lt;/string&gt;
&lt;string name=&quot;text_b&quot;&gt;だがあなたはもっと美しく、もっとおだやかです。&lt;/string&gt;
&lt;/resources&gt;</pre></td>
</tr>
</table>
<p class="note"><b>Tip:</b> In the
<code>values-<em>&lt;qualifier&gt;</em>/strings.xml</code> files, you only need to
include text for strings that are different from the default strings. For
example, when the application runs on a device that is configured for Japanese,
the plan is for <code>text_a</code> and <code>text_b</code> to be in Japanese
while all the other text is in English, so
<code>res/values-ja/strings.xml</code> only needs to include <code>text_a</code>
and <code>text_b</code>.</p>
<h3 id="localize_images">Localize the Images</h3>
<p>As shown in <a href="#table2">Table 2</a>, the application needs six more
drawable folders, each containing a <code>flag.png</code> icon. Add the needed
icons and folders to your project:</p>
<ol>
<li>Save this <a href="../../../images/hello_l10n/drawable-de-rDE/flag.png">German flag icon</a>
as <code>res/drawable-de-rDE/flag.png</code> in the application's project
workspace.
<p>For example:</p>
<ol>
<li>Click the link to open the flag image.</li>
<li>Save the image in
<code><em>your-workspace</em>/HelloL10N/res/drawable-de-rDE/</code> .</li>
</ol>
</li>
<li>Save this <a href="../../../images/hello_l10n/drawable-fr-rFR/flag.png">French flag icon</a>
as <code>res/drawable-fr-rFR/flag.png</code> in the application's project
workspace. </li>
<li>Save this <a href="../../../images/hello_l10n/drawable-fr-rCA/flag.png">Canadian flag icon</a>
as <code>res/drawable-fr-rCA/flag.png</code> in the project workspace. </li>
<li>Save the <a href="../../../images/hello_l10n/drawable-en-rCA/flag.png">Canadian flag icon</a>
again, this time as <code>res/drawable-en-rCA/flag.png</code> in the project
workspace. (Why not have just <em>one</em> folder that contains the Canadian
flag? Because a folder qualifer cannot specify a region without a language.
You cannot have a folder named <code>drawable-rCA/</code>; instead you must
create two separate folders, one for each of the Canadian languages represented
in the application.)</li>
<li>Save this <a href="../../../images/hello_l10n/drawable-ja-rJP/flag.png">Japanese flag icon</a>
as <code>res/drawable-ja-rJP/flag.png</code> in the project workspace. </li>
<li>Save this <a href="../../../images/hello_l10n/drawable-en-rUS/flag.png">United States flag
icon</a> as <code>res/drawable-en-rUS/flag.png</code> in the project workspace.
</li>
</ol>
<p>If you are using Eclipse, refresh the project (F5). The new
<code>res/drawable-<em>&lt;qualifier&gt;</em>/</code> folders should appear in the
project view. </p>
<h2 id="test_localized">Run and Test the Localized Application</h2>
<p>Once you've added the localized string and image resources, you are ready to
run the application and test its handling of them. To change the locale
on a device or in the emulator, use the Settings
application (Home &gt; Menu &gt; Settings &gt; Locale &amp; text &gt; Select
locale). Depending on how a device was configured, it might not offer any
alternate locales via the Settings application, or might offer only a few. The
emulator, on the other hand, will offer a selection of all the locales that are
available in the Android system image. </p>
<p>To set the emulator to a locale that is not available in the system image,
use the Custom Locale application, which is available in the Application
tab:</p>
<p><img src="{@docRoot}images/hello_l10n/custom_locale_app.png" alt="custom locale app" width="163"
height="158" style="margin-left:15px"></p>
<p>To switch to a new locale, long-press a locale name:</p>
<p><img src="{@docRoot}images/hello_l10n/using_custom_locale.png" alt="using custom locale"
width="512" height="299" style="margin-left:15px"></p>
<p>For a list of locales available on different versions of the Android platform,
refer to the platform notes documents, listed under "Downloadable SDK Components"
in the "SDK" tab. For example, <a
href="{@docRoot}sdk/android-2.0.html#locs">Android 2.0 locales</a>.</p>
<p>Run the application for each of the expected locales, plus one unexpected
locale. Here are some of the results you should see:</p>
<table border="0" cellspacing="0" cellpadding="05">
<tr>
<th scope="col">Locale</th>
<th scope="col">Opening screen of application</th>
</tr>
<tr>
<td>German / Germany
<br />Specifically supported by the Hello, L10N application.</td>
<td><img src="{@docRoot}images/hello_l10n/german_screenshot.png" alt="custom locale app"
width="321" height="175" align="right"
style="margin-left:10px;margin-right:20px"></td>
</tr>
<tr>
<td>French / Canada
<br />Specifically supported by the Hello, L10N application.</td>
<td><img src="{@docRoot}images/hello_l10n/frenchCA_screenshot.png" alt="custom locale app"
width="321" height="175" align="right"
style="margin-left:10px;margin-right:20px"></td>
</tr>
<tr>
<td>German / Switzerland
<br />Only the language is specifically supported by
the Hello, L10N application.</td>
<td><img src="{@docRoot}images/hello_l10n/germanCH_screenshot.png" alt="custom locale app"
width="321" height="175" align="right"
style="margin-left:10px;margin-right:20px">`</td>
</tr>
<tr>
<td>Japanese
<br />Specifically supported by the Hello, L10N application.
</td>
<td><img src="{@docRoot}images/hello_l10n/japanese_screenshot.png" alt="custom locale app"
width="321" height="220" align="right"
style="margin-left:10px;margin-right:20px">`</td>
</tr>
<tr>
<td>Romansh / Switzerland (custom locale <code>rm_CH</code>)
<br />Not specifically supported by the Hello, L10N
application, so the application uses the default resources.</td>
<td><img src="{@docRoot}images/hello_l10n/romanshCH_screenshot.png" alt="custom locale app"
width="321" height="175" align="right"
style="margin-left:10px;margin-right:20px"></td>
</tr>
</table>
@@ -0,0 +1,142 @@
page.title=Notepad Tutorial
@jd:body
<p>This tutorial on writing a notepad application gives you a &quot;hands-on&quot; introduction
to the Android framework and the tools you use to build applications on it.
Starting from a preconfigured project file, it guides you through the process of
developing a simple notepad application and provides concrete examples of how to
set up the project, develop the application logic and user interface, and then
compile and run the application. </p>
<p>The tutorial presents the application development as a set of
exercises (see below), each consisting of several steps. You should follow
the steps in each exercise to gradually build and refine your
application. The exercises explain each step in detail and provide all the
sample code you need to complete the application. </p>
<p>When you are finished with the tutorial, you will have created a functioning
Android application and will have learned many of the most important
concepts in Android development. If you want to add more complex features to
your application, you can examine the code in an alternative implementation
of a Note Pad application, in the
<a href="{@docRoot}resources/samples/index.html">Sample Code</a> section. </p>
<a name="who"></a>
<h2>Who Should Use this Tutorial</h2>
<p>This tutorial is designed for experienced developers, especially those with
knowledge of the Java programming language. If you haven't written Java
applications before, you can still use the tutorial, but you might need to work
at a slower pace. </p>
<p>Also note that this tutorial uses
the Eclipse development environment, with the Android plugin installed. If you
are not using Eclipse, you can follow the exercises and build the application,
but you will need to determine how to accomplish the Eclipse-specific
steps in your environment. </p>
<a name="preparing"></a>
<h2>Preparing for the Exercises</h2>
<p>The tutorial assumes that you have some familiarity with basic Android
application concepts and terminology. If you are not, you
should read <a href="{@docRoot}guide/topics/fundamentals.html">Application
Fundamentals</a> before continuing. </p>
<p>This tutorial also builds on the introductory information provided in the
<a href="{@docRoot}resources/tutorials/hello-world.html">Hello World</a>
tutorial, which explains how to set up your Eclipse environment
for building Android applications. We recommend you complete the Hello World
tutorial before starting this one.</p>
<p>To prepare for this lesson:</p>
<ol>
<li>Download the <a href="codelab/NotepadCodeLab.zip">project
exercises archive (.zip)</a>.</li>
<li>Unpack the archive file to a suitable location on your machine.</li>
<li>Open the <code>NotepadCodeLab</code> folder.</li>
</ol>
<p>Inside the <code>NotepadCodeLab</code> folder, you should see six project
files: <code>Notepadv1</code>,
<code>Notepadv2</code>, <code>Notepadv3</code>,
<code>Notepadv1Solution</code>, <code>Notepadv2Solution</code>
and <code>Notepadv3Solution</code>. The <code>Notepadv#</code> projects are
the starting points for each of the exercises, while the
<code>Notepadv#Solution</code> projects are the exercise
solutions. If you are having trouble with a particular exercise, you
can compare your current work against the exercise solution.</p>
<a name="exercises"></a>
<h2> Exercises</h2>
<p>The table below lists the tutorial exercises and describes the development
areas that each covers. Each exercise assumes that you have completed any
previous exercises.</p>
<table border="0" style="padding:4px;spacing:2px;" summary="This
table lists the
tutorial examples and describes what each covers. ">
<tr>
<th width="120"><a href="{@docRoot}resources/tutorials/notepad/notepad-ex1.html">Exercise
1</a></th>
<td>Start here. Construct a simple notes list that lets the user add new notes but not
edit them. Demonstrates the basics of <code>ListActivity</code> and creating
and handling
menu options. Uses a SQLite database to store the notes.</td>
</tr>
<tr>
<th><a href="{@docRoot}resources/tutorials/notepad/notepad-ex2.html">Exercise 2</a></th>
<td>Add a second Activity to the
application. Demonstrates constructing a
new Activity, adding it to the Android manifest, passing data between the
activities, and using more advanced screen layout. Also shows how to
invoke another Activity to return a result, using
<code>startActivityForResult()</code>.</td>
</tr>
<tr>
<th><a href="{@docRoot}resources/tutorials/notepad/notepad-ex3.html">Exercise 3</a></th>
<td>Add handling of life-cycle events to
the application, to let it
maintain application state across the life cycle. </td>
</tr>
<tr>
<th><a href="{@docRoot}resources/tutorials/notepad/notepad-extra-credit.html">Extra
Credit</a></th>
<td>Demonstrates how to use the Eclipse
debugger and how you can use it to
view life-cycle events as they are generated. This section is optional but
highly recommended.</td>
</tr>
</table>
<a name="other"></a>
<h2>Other Resources and Further Learning</h2>
<ul>
<li>For a lighter but broader introduction to concepts not covered in the
tutorial,
take a look at <a href="{@docRoot}resources/faq/commontasks.html">Common Android Tasks</a>.</li>
<li>The Android SDK includes a variety of fully functioning sample applications
that make excellent opportunities for further learning. You can find the sample
applications in the <code>samples/</code> directory of your downloaded SDK, or browser them
here, in the <a href="{@docRoot}resources/samples/index.html">Sample Code</a> section.</li>
<li>This tutorial draws from the full Notepad application included in the
<code>samples/</code> directory of the SDK, though it does not match it exactly.
When you are done with the tutorial,
it is highly recommended that you take a closer look at this version of the Notepad
application,
as it demonstrates a variety of interesting additions for your application,
such as:</li>
<ul>
<li>Setting up a custom striped list for the list of notes.</li>
<li>Creating a custom text edit view that overrides the <code>draw()</code>
method to make it look like a lined notepad.</li>
<li>Implementing a full <code>ContentProvider</code> for notes.</li>
<li>Reverting and discarding edits instead of just automatically saving
them.</li>
</ul>
</ul>
@@ -0,0 +1,582 @@
page.title=Notepad Exercise 1
parent.title=Notepad Tutorial
parent.link=index.html
@jd:body
<p><em>In this exercise, you will construct a simple notes list that lets the
user add new notes but not edit them. The exercise demonstrates:</em></p>
<ul>
<li><em>The basics of <code>ListActivities</code> and creating and handling menu
options. </em></li>
<li><em>How to use a SQLite database to store the notes.</em></li>
<li><em>How to bind data from a database cursor into a ListView using a
SimpleCursorAdapter.</em></li>
<li><em>The basics of screen layouts, including how to lay out a list view, how
you can add items to the activity menu, and how the activity handles those menu
selections. </em></li>
</ul>
<div style="float:right;white-space:nowrap">
<span style="color:#BBB;">
[<a href="notepad-ex1.html" style="color:#BBB;">Exercise 1</a>]</span>
[<a href="notepad-ex2.html">Exercise 2</a>]
[<a href="notepad-ex3.html">Exercise 3</a>]
[<a href="notepad-extra-credit.html">Extra Credit</a>]
</div>
<h2>Step 1</h2>
<p>Open up the <code>Notepadv1</code> project in Eclipse.</p>
<p><code>Notepadv1</code> is a project that is provided as a starting point. It
takes care of some of the boilerplate work that you have already seen if you
followed the <a href="{@docRoot}resources/tutorials/hello-world.html">Hello,
World</a> tutorial.</p>
<ol>
<li>
Start a new Android Project by clicking <strong>File</strong> >
<strong>New</strong> > <strong>Android Project</strong>.</li>
<li>
In the New Android Project dialog, select <strong>Create project from existing source</strong>.</li>
<li>
Click <strong>Browse</strong> and navigate to where you copied the <code>NotepadCodeLab</code>
(downloaded during <a href="{@docRoot}resources/tutorials/notepad/index.html#preparing">setup</a>)
and select <code>Notepadv1</code>.</li>
<li>
The Project Name and other properties should be automatically filled for you.
You must select the Build Target&mdash;we recommend selecting a target with the
lowest platform version available. Also add an integer to the Min SDK Version field
that matches the API Level of the selected Build Target.</li>
<li>
Click <strong>Finish</strong>. The <code>Notepadv1</code> project should open and be
visible in your Eclipse package explorer.</li>
</ol>
<p>If you see an error about <code>AndroidManifest.xml</code>, or some
problems related to an Android zip file, right click on the project and
select <strong>Android Tools</strong> > <strong>Fix Project Properties</strong>.
(The project is looking in the wrong location for the library file,
this will fix it for you.)</p>
<h2>Step 2</h2>
<div class="sidebox-wrapper">
<div class="sidebox">
<h2>Accessing and modifying data</h2>
<p>For this
exercise, we are using a SQLite database to store our data. This is useful
if only <em>your</em> application will need to access or modify the data. If you wish for
other activities to access or modify the data, you have to expose the data using a
{@link android.content.ContentProvider ContentProvider}.</p>
<p>If you are interested, you can find out more about
<a href="{@docRoot}guide/topics/providers/content-providers.html">content providers</a> or the
whole
subject of <a href="{@docRoot}guide/topics/data/data-storage.html">Data Storage</a>.
The NotePad sample in the <code>samples/</code> folder of the SDK also has an example of how
to create a ContentProvider.</p>
</div>
</div>
<p>Take a look at the <code>NotesDbAdapter</code> class &mdash; this class is provided to
encapsulate data access to a SQLite database that will hold our notes data
and allow us to update it.</p>
<p>At the top of the class are some constant definitions that will be used in the application
to look up data from the proper field names in the database. There is also a database creation
string defined, which is used to create a new database schema if one doesn't exist already.</p>
<p>Our database will have the name <code>data</code>, and have a single table called
<code>notes</code>, which in turn has three fields: <code>_id</code>, <code>title</code> and
<code>body</code>. The <code>_id</code> is named with an underscore convention used in a number of
places inside the Android SDK and helps keep a track of state. The <code>_id</code>
usually has to be specified when querying or updating the database (in the column projections
and so on). The other two fields are simple text fields that will store data.
</p>
<p>The constructor for <code>NotesDbAdapter</code> takes a Context, which allows it to communicate with aspects
of the Android operating system. This is quite common for classes that need to touch the
Android system in some way. The Activity class implements the Context class, so usually you will just pass
<code>this</code> from your Activity, when needing a Context.</p>
<p>The <code>open()</code> method calls up an instance of DatabaseHelper, which is our local
implementation of the SQLiteOpenHelper class. It calls <code>getWritableDatabase()</code>,
which handles creating/opening a database for us.</p>
<p><code>close()</code> just closes the database, releasing resources related to the
connection.</p>
<p><code>createNote()</code> takes strings for the title and body of a new note,
then creates that note in the database. Assuming the new note is created successfully, the
method also returns the row <code>_id</code> value for the newly created note.</p>
<p><code>deleteNote()</code> takes a <var>rowId</var> for a particular note, and deletes that note from
the database.</p>
<p><code>fetchAllNotes()</code> issues a query to return a {@link android.database.Cursor} over all notes in the
database. The <code>query()</code> call is worth examination and understanding. The first field is the
name of the database table to query (in this case <code>DATABASE_TABLE</code> is "notes").
The next is the list of columns we want returned, in this case we want the <code>_id</code>,
<code>title</code> and <code>body</code> columns so these are specified in the String array.
The remaining fields are, in order: <code>selection</code>,
<code>selectionArgs</code>, <code>groupBy</code>, <code>having</code> and <code>orderBy</code>.
Having these all <code>null</code> means we want all data, need no grouping, and will take the default
order. See {@link android.database.sqlite.SQLiteDatabase SQLiteDatabase} for more details.</p>
<p class="note"><b>Note:</b> A Cursor is returned rather than a collection of rows. This allows
Android to use resources efficiently -- instead of putting lots of data straight into memory
the cursor will retrieve and release data as it is needed, which is much more efficient for
tables with lots of rows.</p>
<p><code>fetchNote()</code> is similar to <code>fetchAllNotes()</code> but just gets one note
with the <var>rowId</var> we specify. It uses a slightly different version of the
{@link android.database.sqlite.SQLiteDatabase} <code>query()</code> method.
The first parameter (set <em>true</em>) indicates that we are interested
in one distinct result. The <var>selection</var> parameter (the fourth parameter) has been specified to search
only for the row "where _id =" the <var>rowId</var> we passed in. So we are returned a Cursor on
the one row.</p>
<p>And finally, <code>updateNote()</code> takes a <var>rowId</var>, <var>title</var> and <var>body</var>, and uses a
{@link android.content.ContentValues ContentValues} instance to update the note of the given
<var>rowId</var>.</p>
<h2 style="clear:right;">Step 3</h2>
<div class="sidebox-wrapper">
<div class="sidebox">
<h2>Layouts and activities</h2>
<p>Most Activity classes will have a layout associated with them. The layout
will be the "face" of the Activity to the user. In this case our layout will
take over the whole screen and provide a list of notes.</p>
<p>Full screen layouts are not the only option for an Activity however. You
might also want to use a <a
href="{@docRoot}resources/faq/commontasks.html#floatingorfull">floating
layout</a> (for example, a <a
href="{@docRoot}resources/faq/commontasks.html#dialogsandalerts">dialog
or alert</a>),
or perhaps you don't need a layout at all (the Activity will be invisible
to the user unless you specify some kind of layout for it to use).</p>
</div>
</div>
<p>Open the <code>notepad_list.xml</code> file in <code>res/layout</code>
and
take a look at it. (You may have to
hit the <em>xml</em> tab, at the bottom, in order to view the XML markup.)</p>
<p>This is a mostly-empty layout definition file. Here are some
things you should know about a layout file:</p>
<ul>
<li>
All Android layout files must start with the XML header line:
<code>&lt;?xml version="1.0" encoding="utf-8"?&gt;</code>. </li>
<li>
The next definition will often (but not always) be a layout
definition of some kind, in this case a <code>LinearLayout</code>. </li>
<li>
The XML namespace of Android should always be defined in
the top level component or layout in the XML so that <code>android:</code> tags can
be used through the rest of the file:
<p><code>xmlns:android="http://schemas.android.com/apk/res/android"</code></p>
</li>
</ul>
<h2 style="clear:right;">Step 4</h2>
<p>We need to create the layout to hold our list. Add code inside
of the <code>LinearLayout</code> element so the whole file looks like this: </p>
<pre>
&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;LinearLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
android:layout_width=&quot;wrap_content&quot;
android:layout_height=&quot;wrap_content&quot;&gt;
&lt;ListView android:id=&quot;@android:id/list&quot;
android:layout_width=&quot;wrap_content&quot;
android:layout_height=&quot;wrap_content&quot;/&gt;
&lt;TextView android:id=&quot;@android:id/empty&quot;
android:layout_width=&quot;wrap_content&quot;
android:layout_height=&quot;wrap_content&quot;
android:text=&quot;@string/no_notes&quot;/&gt;
&lt;/LinearLayout&gt;
</pre>
<ul>
<li>
The <strong>&#64;</strong> symbol in the id strings of the <code>ListView</code> and
<code>TextView</code> tags means
that the XML parser should parse and expand the rest of
the id string and use an ID resource.</li>
<li>
The <code>ListView</code> and <code>TextView</code> can be
thought as two alternative views, only one of which will be displayed at once.
ListView will be used when there are notes to be shown, while the TextView
(which has a default value of "No Notes Yet!" defined as a string
resource in <code>res/values/strings.xml</code>) will be displayed if there
aren't any notes to display.</li>
<li>The <code>list</code> and <code>empty</code> IDs are
provided for us by the Android platform, so, we must
prefix the <code>id</code> with <code>android:</code> (e.g., <code>@android:id/list</code>).</li>
<li>The View with the <code>empty</code> id is used
automatically when the {@link android.widget.ListAdapter} has no data for the ListView. The
ListAdapter knows to look for this name by default. Alternatively, you could change the
default empty view by using {@link android.widget.AdapterView#setEmptyView(View)}
on the ListView.
<p>
More broadly, the <code>android.R</code> class is a set of predefined
resources provided for you by the platform, while your project's
<code>R</code> class is the set of resources your project has defined.
Resources found in the <code>android.R</code> resource class can be
used in the XML files by using the <code>android:</code> name space prefix
(as we see here).</p>
</li>
</ul>
<h2 style="clear:right;">Step 5</h2>
<div class="sidebox-wrapper">
<div class="sidebox">
<h2>Resources and the R class</h2>
<p>The folders under res/ in the Eclipse project are for resources.
There is a <a href="{@docRoot}resources/faq/commontasks.html#filelist">specific structure</a>
to the
folders and files under res/.</p>
<p>Resources defined in these folders and files will have
corresponding entries in the R class allowing them to be easily accessed
and used from your application. The R class is automatically generated using the contents
of the res/ folder by the eclipse plugin (or by aapt if you use the command line tools).
Furthermore, they will be bundled and deployed for you as part of the application.</p>
</p>
</div>
</div>
<p>To make the list of notes in the ListView, we also need to define a View for each row:</p>
<ol>
<li>
Create a new file under <code>res/layout</code> called
<code>notes_row.xml</code>. </li>
<li>
Add the following contents (note: again the XML header is used, and the
first node defines the Android XML namespace)<br>
<pre style="overflow:auto">
&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;TextView android:id=&quot;&#64;+id/text1&quot;
xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
android:layout_width=&quot;wrap_content&quot;
android:layout_height=&quot;wrap_content&quot;/&gt;</pre>
<p>
This is the View that will be used for each notes title row &mdash; it has only
one text field in it. </p>
<p>In this case we create a new id called <code>text1</code>. The
<strong>+</strong> after the <strong>@</strong> in the id string indicates that the id should
be automatically created as a resource if it does not already exist, so we are defining
<code>text1</code> on the fly and then using it.</p>
</li>
<li>Save the file.</li>
</ol>
<p>Open the <code>R.java</code> class in the
project and look at it, you should see new definitions for
<code>notes_row</code> and <code>text1</code> (our new definitions)
meaning we can now gain access to these from the our code. </p>
<h2 style="clear:right;">Step 6</h2>
<p>Next, open the <code>Notepadv1</code> class in the source. In the following steps, we are going to
alter this class to become a list adapter and display our notes, and also
allow us to add new notes.</p>
<p><code>Notepadv1</code> will inherit from a subclass
of <code>Activity</code> called a <code>ListActivity</code>,
which has extra functionality to accommodate the kinds of
things you might want to do with a list, for
example: displaying an arbitrary number of list items in rows on the screen,
moving through the list items, and allowing them to be selected.</p>
<p>Take a look through the existing code in <code>Notepadv1</code> class.
There is a currently an unused private field called <code>mNoteNumber</code> that
we will use to create numbered note titles.</p>
<p>There are also three override methods defined:
<code>onCreate</code>, <code>onCreateOptionsMenu</code> and
<code>onOptionsItemSelected</code>; we need to fill these
out:</p>
<ul>
<li><code>onCreate()</code> is called when the activity is
started &mdash; it is a little like the "main" method for an Activity. We use
this to set up resources and state for the activity when it is
running.</li>
<li><code>onCreateOptionsMenu()</code> is used to populate the
menu for the Activity. This is shown when the user hits the menu button,
and
has a list of options they can select (like "Create
Note"). </li>
<li><code>onOptionsItemSelected()</code> is the other half of the
menu equation, it is used to handle events generated from the menu (e.g.,
when the user selects the "Create Note" item).
</li>
</ul>
<h2>Step 7</h2>
<p>Change the inheritance of <code>Notepadv1</code> from
<code>Activity</code>
to <code>ListActivity</code>:</p>
<pre>public class Notepadv1 extends ListActivity</pre>
<p>Note: you will have to import <code>ListActivity</code> into the
Notepadv1
class using Eclipse, <strong>ctrl-shift-O</strong> on Windows or Linux, or
<strong>cmd-shift-O</strong> on the Mac (organize imports) will do this for you
after you've written the above change.</p>
<h2>Step 8</h2>
<p>Fill out the body of the <code>onCreate()</code> method.</p>
<p>Here we will set the title for the Activity (shown at the top of the
screen), use the <code>notepad_list</code> layout we created in XML,
set up the <code>NotesDbAdapter</code> instance that will
access notes data, and populate the list with the available note
titles:</p>
<ol>
<li>
In the <code>onCreate</code> method, call <code>super.onCreate()</code> with the
<code>savedInstanceState</code> parameter that's passed in.</li>
<li>
Call <code>setContentView()</code> and pass <code>R.layout.notepad_list</code>.</li>
<li>
At the top of the class, create a new private class field called <code>mDbHelper</code> of class
<code>NotesDbAdapter</code>.
</li>
<li>
Back in the <code>onCreate</code> method, construct a new
<code>NotesDbAdapter</code>
instance and assign it to the <code>mDbHelper</code> field (pass
<code>this</code> into the constructor for <code>DBHelper</code>)
</li>
<li>
Call the <code>open()</code> method on <code>mDbHelper</code> to open (or create) the
database.
</li>
<li>
Finally, call a new method <code>fillData()</code>, which will get the data and
populate the ListView using the helper &mdash; we haven't defined this method yet. </li>
</ol>
<p>
<code>onCreate()</code> should now look like this:</p>
<pre>
&#64;Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.notepad_list);
mDbHelper = new NotesDbAdapter(this);
mDbHelper.open();
fillData();
}</pre>
<p>And be sure you have the <code>mDbHelper</code> field definition (right
under the mNoteNumber definition): </p>
<pre> private NotesDbAdapter mDbHelper;</pre>
<h2>Step 9</h2>
<div class="sidebox-wrapper">
<div class="sidebox">
<h2>More about menus</h2>
<p>The notepad application we are constructing only scratches the
surface with <a href="{@docRoot}resources/faq/commontasks.html#addmenuitems">menus</a>. </p>
<p>You can also <a href="{@docRoot}resources/faq/commontasks.html#menukeyshortcuts">add
shortcut keys for menu items</a>, <a
href="{@docRoot}resources/faq/commontasks.html#menukeyshortcuts">create
submenus</a> and even <a href="{@docRoot}resources/faq/commontasks.html#addingtoothermenus">add
menu items to other applications!</a>. </p>
</div>
</div>
<p>Fill out the body of the <code>onCreateOptionsMenu()</code> method.</p>
<p>We will now create the "Add Item" button that can be accessed by pressing the menu
button on the device. We'll specify that it occupy the first position in the menu.</p>
<ol>
<li>
In <code>strings.xml</code> resource (under <code>res/values</code>), add
a new string named "menu_insert" with its value set to <code>Add Item</code>:
<pre>&lt;string name="menu_insert"&gt;Add Item&lt;/string&gt;</pre>
<p>Then save the file and return to <code>Notepadv1</code>.</p>
</li>
<li>Create a menu position constant at the top of the class:
<pre>public static final int INSERT_ID = Menu.FIRST;</pre>
</li>
<li>In the <code>onCreateOptionsMenu()</code> method, change the
<code>super</code> call so we capture the boolean return as <code>result</code>. We'll return this value at the end.</li>
<li>Then add the menu item with <code>menu.add()</code>.</li>
</ol>
<p>The whole method should now look like this:
<pre>
&#64;Override
public boolean onCreateOptionsMenu(Menu menu) {
boolean result = super.onCreateOptionsMenu(menu);
menu.add(0, INSERT_ID, 0, R.string.menu_insert);
return result;
}</pre>
<p>The arguments passed to <code>add()</code> indicate: a group identifier for this menu (none,
in this case), a unique ID (defined above), the order of the item (zero indicates no preference),
and the resource of the string to use for the item.</p>
<h2 style="clear:right;">Step 10</h2>
<p>Fill out the body of the <code>onOptionsItemSelected()</code> method:</p>
<p>This is going
to handle our new "Add Note" menu item. When this is selected, the
<code>onOptionsItemSelected()</code> method will be called with the
<code>item.getId()</code> set to <code>INSERT_ID</code> (the constant we
used to identify the menu item). We can detect this, and take the
appropriate actions:</p>
<ol>
<li>
The <code>super.onOptionsItemSelected(item)</code> method call goes at the
end of this method &mdash; we want to catch our events first! </li>
<li>
Write a switch statement on <code>item.getItemId()</code>.
<p>In the case of <var>INSERT_ID</var>, call a new method, <code>createNote()</code>,
and return true, because we have handled this event and do not want to
propagate it through the system.</p>
</li>
<li>Return the result of the superclass' <code>onOptionsItemSelected()</code>
method at the end.</li>
</ol>
<p>
The whole <code>onOptionsItemSelect()</code> method should now look like
this:</p>
<pre>
&#64;Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case INSERT_ID:
createNote();
return true;
}
return super.onOptionsItemSelected(item);
}</pre>
<h2>Step 11</h2>
<p>Add a new <code>createNote()</code> method:</p>
<p>In this first version of
our application, <code>createNote()</code> is not going to be very useful.
We will simply
create a new note with a title assigned to it based on a counter ("Note 1",
"Note 2"...) and with an empty body. At present we have no way of editing
the contents of a note, so for now we will have to be content making one
with some default values:</p>
<ol>
<li>Construct the name using "Note" and the counter we defined in the class: <code>
String noteName = "Note " + mNoteNumber++</code></li>
<li>
Call <code>mDbHelper.createNote()</code> using <code>noteName</code> as the
title and <code>""</code> for the body
</li>
<li>
Call <code>fillData()</code> to populate the list of notes (inefficient but
simple) &mdash; we'll create this method next.</li>
</ol>
<p>
The whole <code>createNote()</code> method should look like this: </p>
<pre>
private void createNote() {
String noteName = &quot;Note &quot; + mNoteNumber++;
mDbHelper.createNote(noteName, &quot;&quot;);
fillData();
}</pre>
<h2>Step 12</h2>
<div class="sidebox-wrapper">
<div class="sidebox">
<h2>List adapters</h2>
<p>Our example uses a {@link android.widget.SimpleCursorAdapter
SimpleCursorAdapter} to bind a database {@link android.database.Cursor Cursor}
into a ListView, and this is a common way to use a {@link android.widget.ListAdapter
ListAdapter}. Other options exist like {@link android.widget.ArrayAdapter ArrayAdapter} which
can be used to take a List or Array of in-memory data and bind it in to
a list as well.</p>
</div>
</div>
<p>Define the <code>fillData()</code> method:</p>
<p>This
method uses <code>SimpleCursorAdapter,</code> which takes a database <code>Cursor</code>
and binds it to fields provided in the layout. These fields define the row elements of our list
(in this case we use the <code>text1</code> field in our
<code>notes_row.xml</code> layout), so this allows us to easily populate the list with
entries from our database.</p>
<p>To do this we have to provide a mapping from the <code>title</code> field in the returned Cursor, to
our <code>text1</code> TextView, which is done by defining two arrays: the first a string array
with the list of columns to map <em>from</em> (just "title" in this case, from the constant
<code>NotesDbAdapter.KEY_TITLE</code>) and, the second, an int array
containing references to the views that we'll bind the data <em>into</em>
(the <code>R.id.text1</code> TextView).</p>
<p>This is a bigger chunk of code, so let's first take a look at it:</p>
<pre>
private void fillData() {
// Get all of the notes from the database and create the item list
Cursor c = mDbHelper.fetchAllNotes();
startManagingCursor(c);
String[] from = new String[] { NotesDbAdapter.KEY_TITLE };
int[] to = new int[] { R.id.text1 };
// Now create an array adapter and set it to display using our row
SimpleCursorAdapter notes =
new SimpleCursorAdapter(this, R.layout.notes_row, c, from, to);
setListAdapter(notes);
}</pre>
<p>Here's what we've done:</p>
<ol>
<li>
After obtaining the Cursor from <code>mDbHelper.fetchAllNotes()</code>, we
use an Activity method called
<code>startManagingCursor()</code> that allows Android to take care of the
Cursor lifecycle instead of us needing to worry about it. (We will cover the implications
of the lifecycle in exercise 3, but for now just know that this allows Android to do some
of our resource management work for us.)</li>
<li>
Then we create a string array in which we declare the column(s) we want
(just the title, in this case), and an int array that defines the View(s)
to which we'd like to bind the columns (these should be in order, respective to
the string array, but here we only have one for each).</li>
<li>
Next is the SimpleCursorAdapter instantiation.
Like many classes in Android, the SimpleCursorAdapter needs a Context in order to do its
work, so we pass in <code>this</code> for the context (since subclasses of Activity
implement Context). We pass the <code>notes_row</code> View we created as the receptacle
for the data, the Cursor we just created, and then our arrays.</li>
</ol>
<p>
In the future, remember that the mapping between the <strong>from</strong> columns and <strong>to</strong> resources
is done using the respective ordering of the two arrays. If we had more columns we wanted
to bind, and more Views to bind them in to, we would specify them in order, for example we
might use <code>{ NotesDbAdapter.KEY_TITLE, NotesDbAdapter.KEY_BODY }</code> and
<code>{ R.id.text1, R.id.text2 }</code> to bind two fields into the row (and we would also need
to define text2 in the notes_row.xml, for the body text). This is how you can bind multiple fields
into a single row (and get a custom row layout as well).</p>
<p>
If you get compiler errors about classes not being found, ctrl-shift-O or
(cmd-shift-O on the mac) to organize imports.
</p>
<h2 style="clear:right;">Step 13</h2>
<p>Run it!
<ol>
<li>
Right click on the <code>Notepadv1</code> project.</li>
<li>
From the popup menu, select <strong>Run As</strong> &gt;
<strong>Android Application</strong>.</li>
<li>
If you see a dialog come up, select Android Launcher as the way of running
the application (you can also use the link near the top of the dialog to
set this as your default for the workspace; this is recommended as it will
stop the plugin from asking you this every time).</li>
<li>Add new notes by hitting the menu button and selecting <em>Add
Item</em> from the menu.</li>
</ol>
<h2 style="clear:right;">Solution and Next Steps</h2>
<p>You can see the solution to this class in <code>Notepadv1Solution</code>
from
the zip file to compare with your own.</p>
<p>Once you are ready, move on to <a href="notepad-ex2.html">Tutorial
Exercise 2</a> to add the ability to create, edit and delete notes.</p>
@@ -0,0 +1,645 @@
Rpage.title=Notepad Exercise 2
parent.title=Notepad Tutorial
parent.link=index.html
@jd:body
<p><em>In this exercise, you will add a second Activity to your notepad application, to let the user
create and edit notes. You will also allow the user to delete existing notes through a context menu.
The new Activity assumes responsibility for creating new notes by
collecting user input and packing it into a return Bundle provided by the intent. This exercise
demonstrates:</em></p>
<ul>
<li><em>Constructing a new Activity and adding it to the Android manifest</em></li>
<li><em>Invoking another Activity asynchronously using <code>startActivityForResult()</code></em></li>
<li><em>Passing data between Activity in Bundle objects</em></li>
<li><em>How to use a more advanced screen layout</em></li>
<li><em>How to create a context menu</em></li>
</ul>
<div style="float:right;white-space:nowrap">
[<a href="notepad-ex1.html">Exercise 1</a>]
<span style="color:#BBB;">
[<a href="notepad-ex2.html" style="color:#DDD;">Exercise 2</a>]
</span>
[<a href="notepad-ex3.html">Exercise 3</a>]
[<a href="notepad-extra-credit.html">Extra Credit</a>]
</div>
<h2>Step 1</h2>
<p>Create a new Android project using the sources from <code>Notepadv2</code> under the
<code>NotepadCodeLab</code> folder, just like you did for the first exercise. If you see an error about
<code>AndroidManifest.xml</code>, or some problems related to an
<code>android.zip</code> file, right click on the project and select <strong>Android
Tools</strong> &gt; <strong>Fix Project Properties</strong>.</p>
<p>Open the <code>Notepadv2</code> project and take a look around:</p>
<ul>
<li>
Open and look at the <code>strings.xml</code> file under
<code>res/values</code> &mdash; there are several new strings which we will use
for our new functionality
</li>
<li>
Also, open and take a look at the top of the <code>Notepadv2</code> class,
you will notice several new constants have been defined along with a new <code>mNotesCursor</code>
field used to hold the cursor we are using.
</li>
<li>
Note also that the <code>fillData()</code> method has a few more comments and now uses
the new field to store the notes Cursor. The <code>onCreate()</code> method is
unchanged from the first exercise. Also notice that the member field used to store the
notes Cursor is now called <code>mNotesCursor</code>. The <code>m</code> denotes a member
field and is part of the Android coding style standards.
</li>
<li>
There are also a couple of new overridden methods
(<code>onCreateContextMenu()</code>, <code>onContextItemSelected()</code>,
<code>onListItemClick()</code> and <code>onActivityResult()</code>)
which we will be filling in below.
</li>
</ul>
<h2>Step 2</h2>
<div class="sidebox-wrapper">
<div class="sidebox">
<p>Context menus should always be used when performing actions upon specific elements in the UI.
When you register a View to a context menu, the context menu is revealed by performing a "long-click"
on the UI component (press and hold the touchscreen or highlight and hold down the selection key for about two seconds).</p>
</div>
</div>
<p>First, let's create the context menu that will allow users to delete individual notes.
Open the Notepadv2 class.</p>
<ol>
<li>In order for each list item in the ListView to register for the context menu, we call
<code>registerForContextMenu()</code> and pass it our ListView. So, at the very end of
the <code>onCreate()</code> method add this line:
<pre>registerForContextMenu(getListView());</pre>
<p>Because our Activity extends the ListActivity class, <code>getListView()</code> will return us
the local ListView object for the Activity. Now, each list item in this ListView will activate the
context menu.
<li>
Now fill in the <code>onCreateContextMenu()</code> method. This callback is similar to the other
menu callback used for the options menu. Here, we add just one line, which will add a menu item
to delete a note. Call <code>menu.add()</code> like so:
<pre>
public boolean onCreateContextMenu(Menu menu, View v
ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
menu.add(0, DELETE_ID, 0, R.string.menu_delete);
}</pre>
<p>The <code>onCreateContextMenu()</code> callback passes some other information in addition to the Menu object,
such as the View that has been triggered for the menu and
an extra object that may contain additional information about the object selected. However, we don't care about
these here, because we only have one kind of object in the Activity that uses context menus. In the next
step, we'll handle the menu item selection.</p>
</li>
</ol>
<h2>Step 3</h2>
<p>Now that the we've registered our ListView for a context menu and defined our context menu item, we need
to handle the callback when it is selected. For this, we need to identify the list ID of the
selected item, then delete it. So fill in the
<code>onContextItemSelected()</code> method like this:</p>
<pre>
public boolean onContextItemSelected(MenuItem item) {
switch(item.getItemId()) {
case DELETE_ID:
AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
mDbHelper.deleteNote(info.id);
fillData();
return true;
}
return super.onContextItemSelected(item);
}</pre>
<p>Here, we retrieve the {@link android.widget.AdapterView.AdapterContextMenuInfo AdapterContextMenuInfo}
with {@link android.view.MenuItem#getMenuInfo()}. The <var>id</var> field of this object tells us
the position of the item in the ListView. We then pass this to the <code>deleteNote()</code>
method of our NotesDbAdapter and the note is deleted. That's it for the context menu &mdash; notes
can now be deleted.</p>
<h2 style="clear:right;">Step 4</h2>
<div class="sidebox-wrapper">
<div class="sidebox">
<h2>Starting Other Activities</h2>
<p>In this example our Intent uses a class name specifically.
As well as
<a href="{@docRoot}resources/faq/commontasks.html#intentexamples">starting intents</a> in
classes we already know about, be they in our own application or another
application, we can also create Intents without knowing exactly which
application will handle it.</p>
<p>For example, we might want to open a page in a
browser, and for this we still use
an Intent. But instead of specifying a class to handle it, we use
a predefined Intent constant, and a content URI that describes what we
want to do. See {@link android.content.Intent
android.content.Intent} for more information.</p>
</div>
</div>
<p>Fill in the body of the <code>createNote()</code> method:
<p>Create a new <code>Intent</code> to create a note
(<code>ACTIVITY_CREATE</code>) using the <code>NoteEdit</code> class.
Then fire the Intent using the <code>startActivityForResult()</code> method
call:</p>
<pre style="overflow:auto">
Intent i = new Intent(this, NoteEdit.class);
startActivityForResult(i, ACTIVITY_CREATE);</pre>
<p>This form of the Intent call targets a specific class in our Activity, in this case
<code>NoteEdit</code>. Since the Intent class will need to communicate with the Android
operating system to route requests, we also have to provide a Context (<code>this</code>).</p>
<p>The <code>startActivityForResult()</code> method fires the Intent in a way that causes a method
in our Activity to be called when the new Activity is completed. The method in our Activity
that receives the callback is called
<code>onActivityResult()</code> and we will implement it in a later step. The other way
to call an Activity is using <code>startActivity()</code> but this is a "fire-and-forget" way
of calling it &mdash; in this manner, our Activity is not informed when the Activity is completed, and there is
no way to return result information from the called Activity with <code>startActivity()</code>.
<p>Don't worry about the fact that <code>NoteEdit</code> doesn't exist yet,
we will fix that soon. </p>
</li>
<h2>Step 5</h2>
<p>Fill in the body of the <code>onListItemClick()</code> override.</p>
<p><code>onListItemClick()</code> is a callback method that we'll override. It is called when
the user selects an item from the list. It is passed four parameters: the
<code>ListView</code> object it was invoked from, the <code>View</code>
inside the <code>ListView</code> that was clicked on, the
<code>position</code> in the list that was clicked, and the
<code>mRowId</code> of the item that was clicked. In this instance we can
ignore the first two parameters (we only have one <code>ListView</code> it
could be), and we ignore the <code>mRowId</code> as well. All we are
interested in is the <code>position</code> that the user selected. We use
this to get the data from the correct row, and bundle it up to send to
the <code>NoteEdit</code> Activity.</p>
<p>In our implementation of the callback, the method creates an
<code>Intent</code> to edit the note using
the <code>NoteEdit</code> class. It then adds data into the extras Bundle of
the Intent, which will be passed to the called Activity. We use it
to pass in the title and body text, and the <code>mRowId</code> for the note we are
editing. Finally, it will fire the Intent using the
<code>startActivityForResult()</code> method call. Here's the code that
belongs in <code>onListItemClick()</code>:</p>
<pre>
super.onListItemClick(l, v, position, id);
Cursor c = mNotesCursor;
c.moveToPosition(position);
Intent i = new Intent(this, NoteEdit.class);
i.putExtra(NotesDbAdapter.KEY_ROWID, id);
i.putExtra(NotesDbAdapter.KEY_TITLE, c.getString(
c.getColumnIndexOrThrow(NotesDbAdapter.KEY_TITLE)));
i.putExtra(NotesDbAdapter.KEY_BODY, c.getString(
c.getColumnIndexOrThrow(NotesDbAdapter.KEY_BODY)));
startActivityForResult(i, ACTIVITY_EDIT);</pre>
<ul>
<li>
<code>putExtra()</code> is the method to add items into the extras Bundle
to pass in to intent invocations. Here, we are
using the Bundle to pass in the title, body and mRowId of the note we want to edit.
</li>
<li>
The details of the note are pulled out from our query Cursor, which we move to the
proper position for the element that was selected in the list, with
the <code>moveToPosition()</code> method.</li>
<li>With the extras added to the Intent, we invoke the Intent on the
<code>NoteEdit</code> class by passing <code>startActivityForResult()</code>
the Intent and the request code. (The request code will be
returned to <code>onActivityResult</code> as the <code>requestCode</code> parameter.)</li>
</ul>
<p class="note"><b>Note:</b> We assign the mNotesCursor field to a local variable at the
start of the method. This is done as an optimization of the Android code. Accessing a local
variable is much more efficient than accessing a field in the Dalvik VM, so by doing this
we make only one access to the field, and five accesses to the local variable, making the
routine much more efficient. It is recommended that you use this optimization when possible.</p>
<h2>Step 6</h2>
<p>The above <code>createNote()</code> and <code>onListItemClick()</code>
methods use an asynchronous Intent invocation. We need a handler for the callback, so here we fill
in the body of the <code>onActivityResult()</code>. </p>
<p><code>onActivityResult()</code> is the overridden method
which will be called when an Activity returns with a result. (Remember, an Activity
will only return a result if launched with <code>startActivityForResult</code>.) The parameters provided
to the callback are: </p>
<ul>
<li><code>requestCode</code> &mdash; the original request code
specified in the Intent invocation (either <code>ACTIVITY_CREATE</code> or
<code>ACTIVITY_EDIT</code> for us).
</li>
<li><code>resultCode</code> &mdash; the result (or error code) of the call, this
should be zero if everything was OK, but may have a non-zero code indicating
that something failed. There are standard result codes available, and you
can also create your own constants to indicate specific problems.
</li>
<li><code>intent</code> &mdash; this is an Intent created by the Activity returning
results. It can be used to return data in the Intent "extras."
</li>
</ul>
<p>The combination of <code>startActivityForResult()</code> and
<code>onActivityResult()</code> can be thought of as an asynchronous RPC
(remote procedure call) and forms the recommended way for an Activity to invoke
another and share services.</p>
<p>Here's the code that belongs in your <code>onActivityResult()</code>:</p>
<pre>
super.onActivityResult(requestCode, resultCode, intent);
Bundle extras = intent.getExtras();
switch(requestCode) {
case ACTIVITY_CREATE:
String title = extras.getString(NotesDbAdapter.KEY_TITLE);
String body = extras.getString(NotesDbAdapter.KEY_BODY);
mDbHelper.createNote(title, body);
fillData();
break;
case ACTIVITY_EDIT:
Long mRowId = extras.getLong(NotesDbAdapter.KEY_ROWID);
if (mRowId != null) {
String editTitle = extras.getString(NotesDbAdapter.KEY_TITLE);
String editBody = extras.getString(NotesDbAdapter.KEY_BODY);
mDbHelper.updateNote(mRowId, editTitle, editBody);
}
fillData();
break;
}</pre>
<ul>
<li>
We are handling both the <code>ACTIVITY_CREATE</code> and
<code>ACTIVITY_EDIT</code> activity results in this method.
</li>
<li>
In the case of a create, we pull the title and body from the extras (retrieved from the
returned Intent) and use them to create a new note.
</li>
<li>
In the case of an edit, we pull the mRowId as well, and use that to update
the note in the database.
</li>
<li>
<code>fillData()</code> at the end ensures everything is up to date .
</li>
</ul>
<h2>Step 7</h2>
<div class="sidebox-wrapper">
<div class="sidebox">
<h2>The Art of Layout</h2>
<p>The provided
note_edit.xml layout file is the most sophisticated one in the application we will be building,
but that doesn't mean it is even close to the kind of sophistication you will be likely to want
in real Android applications.</p>
<p>Creating a
good UI is part art and part science, and the rest is work. Mastery of <a
href="{@docRoot}guide/topics/ui/declaring-layout.html">Declaring Layout</a> is an essential part of creating
a good looking Android application.</p>
<p>Take a look at the
<a href="{@docRoot}resources/tutorials/views/index.html">Hello Views</a>
for some example layouts and how to use them. The ApiDemos sample project is also a
great resource from which to learn how to create different layouts.</p>
</div>
</div>
<p>Open the file <code>note_edit.xml</code> that has been provided and take a
look at it. This is the UI code for the Note Editor.</p>
<p>This is the most
sophisticated UI we have dealt with yet. The file is given to you to avoid
problems that may sneak in when typing the code. (The XML is very strict
about case sensitivity and structure, mistakes in these are the usual cause
of problems with layout.)</p>
<p>There is a new parameter used
here that we haven't seen before: <code>android:layout_weight</code> (in
this case set to use the value 1 in each case).</p>
<p><code>layout_weight</code> is used in LinearLayouts
to assign "importance" to Views within the layout. All Views have a default
<code>layout_weight</code> of zero, meaning they take up only as much room
on the screen as they need to be displayed. Assigning a value higher than
zero will split up the rest of the available space in the parent View, according
to the value of each View's <code>layout_weight</code> and its ratio to the
overall <code>layout_weight</code> specified in the current layout for this
and other View elements.</p>
<p>To give an example: let's say we have a text label
and two text edit elements in a horizontal row. The label has no
<code>layout_weight</code> specified, so it takes up the minimum space
required to render. If the <code>layout_weight</code> of each of the two
text edit elements is set to 1, the remaining width in the parent layout will
be split equally between them (because we claim they are equally important).
If the first one has a <code>layout_weight</code> of 1
and the second has a <code>layout_weight</code> of 2, then one third of the
remaining space will be given to the first, and two thirds to the
second (because we claim the second one is more important).</p>
<p>This layout also demonstrates how to nest multiple layouts
inside each other to achieve a more complex and pleasant layout. In this
example, a horizontal linear layout is nested inside the vertical one to
allow the title label and text field to be alongside each other,
horizontally.</p>
<h2 style="clear:right;">Step 8</h2>
<p>Create a <code>NoteEdit</code> class that extends
<code>android.app.Activity</code>.</p>
<p>This is the first time we will have
created an Activity without the Android Eclipse plugin doing it for us. When
you do so, the <code>onCreate()</code> method is not automatically
overridden for you. It is hard to imagine an Activity that doesn't override
the <code>onCreate()</code> method, so this should be the first thing you do.</p>
<ol>
<li>Right click on the <code>com.android.demo.notepad2</code> package
in the Package Explorer, and select <strong>New</strong> &gt; <strong>Class</strong> from the popup
menu.</li>
<li>Fill in <code>NoteEdit</code> for the <code>Name:</code> field in the
dialog.</li>
<li>In the <code>Superclass:</code> field, enter
<code>android.app.Activity</code> (you can also just type Activity and hit
Ctrl-Space on Windows and Linux or Cmd-Space on the Mac, to invoke code
assist and find the right package and class).</li>
<li>Click <strong>Finish</strong>.</li>
<li>In the resulting <code>NoteEdit</code> class, right click in the editor
window and select <strong>Source</strong> &gt; <strong>Override/Implement Methods...</strong></li>
<li>Scroll down through the checklist in the dialog until you see
<code>onCreate(Bundle)</code> &mdash; and check the box next to it.</li>
<li>Click <strong>OK</strong>.<p>The method should now appear in your class.</p></li>
</ol>
<h2>Step 9</h2>
<p>Fill in the body of the <code>onCreate()</code> method for <code>NoteEdit</code>.</p>
<p>This will set the title of our new Activity to say "Edit Note" (one
of the strings defined in <code>strings.xml</code>). It will also set the
content view to use our <code>note_edit.xml</code> layout file. We can then
grab handles to the title and body text edit views, and the confirm button,
so that our class can use them to set and get the note title and body,
and attach an event to the confirm button for when it is pressed by the
user.</p>
<p>We can then unbundle the values that were passed in to the Activity
with the extras Bundle attached to the calling Intent. We'll use them to pre-populate
the title and body text edit views so that the user can edit them.
Then we will grab and store the <code>mRowId</code> so we can keep
track of what note the user is editing.</p>
<ol>
<li>
Inside <code>onCreate()</code>, set up the layout:<br>
<pre>setContentView(R.layout.note_edit);</pre>
</li>
<li>
Change the Activity title to the "Edit Note" string:
<pre>setTitle(R.string.edit_note);</pre>
</li>
<li>
Find the {@link android.widget.EditText} and {@link android.widget.Button} components we need:
<p>These are found by the
IDs associated to them in the R class, and need to be cast to the right
type of <code>View</code> (<code>EditText</code> for the two text views,
and <code>Button</code> for the confirm button):</p>
<pre>
mTitleText = (EditText) findViewById(R.id.title);
mBodyText = (EditText) findViewById(R.id.body);
Button confirmButton = (Button) findViewById(R.id.confirm);</pre>
<p>Note that <code>mTitleText</code> and <code>mBodyText</code> are member
fields (you need to declare them at the top of the class definition).</p>
</li>
<li>At the top of the class, declare a <code>Long mRowId</code> private field to store
the current <code>mRowId</code> being edited (if any).
</li>
<li>Continuing inside <code>onCreate()</code>,
add code to initialize the <code>title</code>, <code>body</code> and
<code>mRowId</code> from the extras Bundle in
the Intent (if it is present):<br>
<pre>
mRowId = null;
Bundle extras = getIntent().getExtras();
if (extras != null) {
String title = extras.getString(NotesDbAdapter.KEY_TITLE);
String body = extras.getString(NotesDbAdapter.KEY_BODY);
mRowId = extras.getLong(NotesDbAdapter.KEY_ROWID);
if (title != null) {
mTitleText.setText(title);
}
if (body != null) {
mBodyText.setText(body);
}
}</pre>
<ul>
<li>
We are pulling the <code>title</code> and
<code>body</code> out of the
<code>extras</code> Bundle that was set from the
Intent invocation.
</li><li>
We also null-protect the text field setting (i.e., we don't want to set
the text fields to null accidentally).</li>
</ul>
</li>
<li>
Create an <code>onClickListener()</code> for the button:
<p>Listeners can be one of the more confusing aspects of UI
implementation, but
what we are trying to achieve in this case is simple. We want an
<code>onClick()</code> method to be called when the user presses the
confirm button, and use that to do some work and return the values
of the edited note to the Intent caller. We do this using something called
an anonymous inner class. This is a bit confusing to look at unless you
have seen them before, but all you really need to take away from this is
that you can refer to this code in the future to see how to create a
listener and attach it to a button. (Listeners are a common idiom
in Java development, particularly for user interfaces.) Here's the empty listener:<br>
<pre>
confirmButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
}
});</pre>
</li>
</ol>
<h2>Step 10</h2>
<p>Fill in the body of the <code>onClick()</code> method of the <code>OnClickListener</code> created in the last step.</p>
<p>This is the code that will be run when the user clicks on the
confirm button. We want this to grab the title and body text from the edit
text fields, and put them into the return Bundle so that they can be passed
back to the Activity that invoked this <code>NoteEdit</code> Activity. If the
operation is an edit rather than a create, we also want to put the
<code>mRowId</code> into the Bundle so that the
<code>Notepadv2</code> class can save the changes back to the correct
note.</p>
<ol>
<li>
Create a <code>Bundle</code> and put the title and body text into it using the
constants defined in Notepadv2 as keys:<br>
<pre>
Bundle bundle = new Bundle();
bundle.putString(NotesDbAdapter.KEY_TITLE, mTitleText.getText().toString());
bundle.putString(NotesDbAdapter.KEY_BODY, mBodyText.getText().toString());
if (mRowId != null) {
bundle.putLong(NotesDbAdapter.KEY_ROWID, mRowId);
}</pre>
</li>
<li>
Set the result information (the Bundle) in a new Intent and finish the Activity:
<pre>
Intent mIntent = new Intent();
mIntent.putExtras(bundle);
setResult(RESULT_OK, mIntent);
finish();</pre>
<ul>
<li>The Intent is simply our data carrier that carries our Bundle
(with the title, body and mRowId).</li>
<li>The <code>setResult()</code> method is used to set the result
code and return Intent to be passed back to the
Intent caller. In this case everything worked, so we return RESULT_OK for the
result code.</li>
<li>The <code>finish()</code> call is used to signal that the Activity
is done (like a return call). Anything set in the Result will then be
returned to the caller, along with execution control.</li>
</ul>
</li>
</ol>
<p>The full <code>onCreate()</code> method (plus supporting class fields) should
now look like this:</p>
<pre>
private EditText mTitleText;
private EditText mBodyText;
private Long mRowId;
&#64;Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.note_edit);
mTitleText = (EditText) findViewById(R.id.title);
mBodyText = (EditText) findViewById(R.id.body);
Button confirmButton = (Button) findViewById(R.id.confirm);
mRowId = null;
Bundle extras = getIntent().getExtras();
if (extras != null) {
String title = extras.getString(NotesDbAdapter.KEY_TITLE);
String body = extras.getString(NotesDbAdapter.KEY_BODY);
mRowId = extras.getLong(NotesDbAdapter.KEY_ROWID);
if (title != null) {
mTitleText.setText(title);
}
if (body != null) {
mBodyText.setText(body);
}
}
confirmButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
Bundle bundle = new Bundle();
bundle.putString(NotesDbAdapter.KEY_TITLE, mTitleText.getText().toString());
bundle.putString(NotesDbAdapter.KEY_BODY, mBodyText.getText().toString());
if (mRowId != null) {
bundle.putLong(NotesDbAdapter.KEY_ROWID, mRowId);
}
Intent mIntent = new Intent();
mIntent.putExtras(bundle);
setResult(RESULT_OK, mIntent);
finish();
}
});
}</pre>
</li>
</ol>
<h2>Step 11</h2>
<div class="sidebox-wrapper">
<div class="sidebox">
<h2>The All-Important Android Manifest File</h2>
<p>The AndroidManifest.xml file is the way in which Android sees your
application. This file defines the category of the application, where
it shows up (or even if it shows up) in the launcher or settings, what
activities, services, and content providers it defines, what intents it can
receive, and more. </p>
<p>For more information, see the reference document
<a href="{@docRoot}guide/topics/manifest/manifest-intro.html">The AndroidManifest.xml
File</a></p>
</div>
</div>
<p>Finally, the new Activity has to be defined in the manifest file:</p>
<p>Before the new Activity can be seen by Android, it needs its own
Activity entry in the <code>AndroidManifest.xml</code> file. This is to let
the system know that it is there and can be called. We could also specify
which IntentFilters the activity implements here, but we are going to skip
this for now and just let Android know that the Activity is
defined.</p>
<p>There is a Manifest editor included in the Eclipse plugin that makes it much easier
to edit the AndroidManifest file, and we will use this. If you prefer to edit the file directly
or are not using the Eclipse plugin, see the box at the end for information on how to do this
without using the new Manifest editor.<p>
<ol>
<li>Double click on the <code>AndroidManifest.xml</code> file in the package explorer to open it.
</li>
<li>Click the <strong>Application</strong> tab at the bottom of the Manifest editor.</li>
<li>Click <strong>Add...</strong> in the Application Nodes section.
<p>If you see a dialog with radiobuttons at the top, select the top radio button:
"Create a new element at the top level, in Application".</p></li>
<li>Make sure "(A) Activity" is selected in the selection pane of the dialog, and click <strong>OK</strong>.</li>
<li>Click on the new "Activity" node, in the Application Nodes section, then
type <code>.NoteEdit</code> into the <em>Name*</em>
field to the right. Press Return/Enter.</li>
</ol>
<p>The Android Manifest editor helps you add more complex entries into the AndroidManifest.xml
file, have a look around at some of the other options available (but be careful not to select
them otherwise they will be added to your Manifest). This editor should help you understand
and alter the AndroidManifest.xml file as you move on to more advanced Android applications.</p>
<p class="note">If you prefer to edit this file directly, simply open the
<code>AndroidManifest.xml</code> file and look at the source (use the
<code>AndroidManifest.xml</code> tab in the eclipse editor to see the source code directly).
Then edit the file as follows:<br>
<code>&lt;activity android:name=".NoteEdit" /&gt;</code><br><br>
This should be placed just below the line that reads:<br>
<code>&lt;/activity&gt;</code> for the <code>.Notepadv2</code> activity.</p>
<h2 style="clear:right;">Step 12</h2>
<p>Now Run it!</p>
<p>You should now be able to add real notes from
the menu, as well as delete an existing one. Notice that in order to delete, you must
first use the directional controls on the device to highlight the note.
Furthermore, selecting a note title from the list should bring up the note
editor to let you edit it. Press confirm when finished to save the changes
back to the database.
<h2>Solution and Next Steps</h2>
<p>You can see the solution to this exercise in <code>Notepadv2Solution</code>
from the zip file to compare with your own.</p>
<p>Now try editing a note, and then hitting the back button on the emulator
instead of the confirm button (the back button is below the menu button). You
will see an error come up. Clearly our application still has some problems.
Worse still, if you did make some changes and hit the back button, when you go
back into the notepad to look at the note you changed, you will find that all
your changes have been lost. In the next exercise we will fix these
problems.</p>
<p>
Once you are ready, move on to <a href="notepad-ex3.html">Tutorial
Exercise 3</a> where you will fix the problems with the back button and lost
edits by introducing a proper life cycle into the NoteEdit Activity.</p>
@@ -0,0 +1,365 @@
page.title=Notepad Exercise 3
parent.title=Notepad Tutorial
parent.link=index.html
@jd:body
<p><em>In this exercise, you will use life-cycle event callbacks to store and
retrieve application state data. This exercise demonstrates:</em></p>
<ul>
<li><em>Life-cycle events and how your application can use them</em></li>
<li><em>Techniques for maintaining application state</em></li>
</ul>
<div style="float:right;white-space:nowrap">
[<a href="notepad-ex1.html">Exercise 1</a>]
[<a href="notepad-ex2.html">Exercise 2</a>]
<span style="color:#BBB;">
[<a href="notepad-ex3.html" style="color:#BBB;">Exercise 3</a>]
</span>
[<a href="notepad-extra-credit.html">Extra Credit</a>]
</div>
<h2>Step 1</h2>
<p>Import <code>Notepadv3</code> into Eclipse. If you see an error about
<code>AndroidManifest.xml,</code> or some problems related to an Android zip
file, right click on the project and select <strong>Android Tools</strong> &gt;
<strong>Fix Project Properties</strong> from the popup menu. The starting point for this exercise is
exactly where we left off at the end of the Notepadv2. </p>
<p>The current application has some problems &mdash; hitting the back button when editing
causes a crash, and anything else that happens during editing will cause the
edits to be lost.</p>
<p>To fix this, we will move most of the functionality for creating and editing
the note into the NoteEdit class, and introduce a full life cycle for editing
notes.</p>
<ol>
<li>Remove the code in <code>NoteEdit</code> that parses the title and body
from the extras Bundle.
<p>Instead, we are going to use the <code>DBHelper</code> class
to access the notes from the database directly. All we need passed into the
NoteEdit Activity is a <code>mRowId</code> (but only if we are editing, if creating we pass
nothing). Remove these lines:</p>
<pre>
String title = extras.getString(NotesDbAdapter.KEY_TITLE);
String body = extras.getString(NotesDbAdapter.KEY_BODY);</pre>
</li>
<li>We will also get rid of the properties that were being passed in
the <code>extras</code> Bundle, which we were using to set the title
and body text edit values in the UI. So delete:
<pre>
if (title != null) {
mTitleText.setText(title);
}
if (body != null) {
mBodyText.setText(body);
}</pre>
</li>
</ol>
<h2>Step 2</h2>
<p>Create a class field for a <code>NotesDbAdapter</code> at the top of the NoteEdit class:</p>
<pre>&nbsp;&nbsp;&nbsp; private NotesDbAdapter mDbHelper;</pre>
<p>Also add an instance of <code>NotesDbAdapter</code> in the
<code>onCreate()</code> method (right below the <code>super.onCreate()</code> call):</p>
<pre>
&nbsp;&nbsp;&nbsp; mDbHelper = new NotesDbAdapter(this);<br>
&nbsp;&nbsp;&nbsp; mDbHelper.open();</pre>
<h2>Step 3</h2>
<p>In <code>NoteEdit</code>, we need to check the <var>savedInstanceState</var> for the
<code>mRowId</code>, in case the note
editing contains a saved state in the Bundle, which we should recover (this would happen
if our Activity lost focus and then restarted).</p>
<ol>
<li>
Replace the code that currently initializes the <code>mRowId</code>:<br>
<pre>
mRowId = null;
Bundle extras = getIntent().getExtras();
if (extras != null) {
mRowId = extras.getLong(NotesDbAdapter.KEY_ROWID);
}
</pre>
with this:
<pre>
mRowId = (savedInstanceState == null) ? null :
(Long) savedInstanceState.getSerializable(NotesDbAdapter.KEY_ROWID);
if (mRowId == null) {
Bundle extras = getIntent().getExtras();
mRowId = extras != null ? extras.getLong(NotesDbAdapter.KEY_ROWID)
: null;
}
</pre>
</li>
<li>
Note the null check for <code>savedInstanceState</code>, and we still need to load up
<code>mRowId</code> from the <code>extras</code> Bundle if it is not
provided by the <code>savedInstanceState</code>. This is a ternary operator shorthand
to safely either use the value or null if it is not present.
</li>
<li>
Note the use of <code>Bundle.getSerializable()</code> instead of
<code>Bundle.getLong()</code>. The latter encoding returns a <code>long</code> primitive and
so can not be used to represent the case when <code>mRowId</code> is <code>null</code>.
</li>
</ol>
<h2>Step 4</h2>
<p>Next, we need to populate the fields based on the <code>mRowId</code> if we
have it:</p>
<pre>populateFields();</pre>
<p>This goes before the <code>confirmButton.setOnClickListener()</code> line.
We'll define this method in a moment.</p>
<h2>Step 5</h2>
<p>Get rid of the Bundle creation and Bundle value settings from the
<code>onClick()</code> handler method. The Activity no longer needs to
return any extra information to the caller. And because we no longer have
an Intent to return, we'll use the shorter version
of <code>setResult()</code>:</p>
<pre>
public void onClick(View view) {
setResult(RESULT_OK);
finish();
}</pre>
<p>We will take care of storing the updates or new notes in the database
ourselves, using the life-cycle methods.</p>
<p>The whole <code>onCreate()</code> method should now look like this:</p>
<pre>
super.onCreate(savedInstanceState);
mDbHelper = new NotesDbAdapter(this);
mDbHelper.open();
setContentView(R.layout.note_edit);
mTitleText = (EditText) findViewById(R.id.title);
mBodyText = (EditText) findViewById(R.id.body);
Button confirmButton = (Button) findViewById(R.id.confirm);
mRowId = (savedInstanceState == null) ? null :
(Long) savedInstanceState.getSerializable(NotesDbAdapter.KEY_ROWID);
if (mRowId == null) {
Bundle extras = getIntent().getExtras();
mRowId = extras != null ? extras.getLong(NotesDbAdapter.KEY_ROWID)
: null;
}
populateFields();
confirmButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
setResult(RESULT_OK);
finish();
}
});</pre>
<h2>Step 6</h2>
<p>Define the <code>populateFields()</code> method.</p>
<pre>
private void populateFields() {
if (mRowId != null) {
Cursor note = mDbHelper.fetchNote(mRowId);
startManagingCursor(note);
mTitleText.setText(note.getString(
note.getColumnIndexOrThrow(NotesDbAdapter.KEY_TITLE)));
mBodyText.setText(note.getString(
note.getColumnIndexOrThrow(NotesDbAdapter.KEY_BODY)));
}
}</pre>
<p>This method uses the <code>NotesDbAdapter.fetchNote()</code> method to find the right note to
edit, then it calls <code>startManagingCursor()</code> from the <code>Activity</code> class, which
is an Android convenience method provided to take care of the Cursor life-cycle. This will release
and re-create resources as dictated by the Activity life-cycle, so we don't need to worry about
doing that ourselves. After that, we just look up the title and body values from the Cursor
and populate the View elements with them.</p>
<h2>Step 7</h2>
<div class="sidebox-wrapper">
<div class="sidebox">
<h2>Why handling life-cycle events is important</h2>
<p>If you are used to always having control in your applications, you
might not understand why all this life-cycle work is necessary. The reason
is that in Android, you are not in control of your Activity, the
operating system is!</p>
<p>As we have already seen, the Android model is based around activities
calling each other. When one Activity calls another, the current Activity
is paused at the very least, and may be killed altogether if the
system starts to run low on resources. If this happens, your Activity will
have to store enough state to come back up later, preferably in the same
state it was in when it was killed.</p>
<p>
Android has a <a href="{@docRoot}guide/topics/fundamentals.html#lcycles">well-defined life
cycle</a>.
Lifecycle events can happen even if you are not handing off control to
another Activity explicitly. For example, perhaps a call comes in to the
handset. If this happens, and your Activity is running, it will be swapped
out while the call Activity takes over.</p>
</div>
</div>
<p>Still in the <code>NoteEdit</code> class, we now override the methods
<code>onSaveInstanceState()</code>, <code>onPause()</code> and
<code>onResume()</code>. These are our life-cycle methods
(along with <code>onCreate()</code> which we already have).</p>
<p><code>onSaveInstanceState()</code> is called by Android if the
Activity is being stopped and <strong>may be killed before it is
resumed!</strong> This means it should store any state necessary to
re-initialize to the same condition when the Activity is restarted. It is
the counterpart to the <code>onCreate()</code> method, and in fact the
<code>savedInstanceState</code> Bundle passed in to <code>onCreate()</code> is the same
Bundle that you construct as <code>outState</code> in the
<code>onSaveInstanceState()</code> method.</p>
<p><code>onPause()</code> and <code>onResume()</code> are also
complimentary methods. <code>onPause()</code> is always called when the
Activity ends, even if we instigated that (with a <code>finish()</code> call for example).
We will use this to save the current note back to the database. Good
practice is to release any resources that can be released during an
<code>onPause()</code> as well, to take up less resources when in the
passive state. <code>onResume()</code> will call our <code>populateFields()</code> method
to read the note out of the database again and populate the fields.</p>
<p>So, add some space after the <code>populateFields()</code> method
and add the following life-cycle methods:</p>
<ol type="a">
<li><code>
onSaveInstanceState()</code>:
<pre>
&#64;Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
saveState();
outState.putSerializable(NotesDbAdapter.KEY_ROWID, mRowId);
}</pre>
<p>We'll define <code>saveState()</code> next.</p>
</li>
<li><code>
onPause()</code>:
<pre>
&#64;Override
protected void onPause() {
super.onPause();
saveState();
}</pre>
</li>
<li><code>
onResume()</code>:
<pre>
&#64;Override
protected void onResume() {
super.onResume();
populateFields();
}</pre>
</li>
</ol>
<p>Note that <code>saveState()</code> must be called in both <code>onSaveInstanceState()</code>
and <code>onPause()</code> to ensure that the data is saved. This is because there is no
guarantee that <code>onSaveInstanceState()</code> will be called and because when it <em>is</em>
called, it is called before <code>onPause()</code>.</p>
<h2 style="clear:right;">Step 8</h2>
<p>Define the <code>saveState()</code> method to put the data out to the
database.</p>
<pre>
private void saveState() {
String title = mTitleText.getText().toString();
String body = mBodyText.getText().toString();
if (mRowId == null) {
long id = mDbHelper.createNote(title, body);
if (id > 0) {
mRowId = id;
}
} else {
mDbHelper.updateNote(mRowId, title, body);
}
}</pre>
<p>Note that we capture the return value from <code>createNote()</code> and if a valid row ID is
returned, we store it in the <code>mRowId</code> field so that we can update the note in future
rather than create a new one (which otherwise might happen if the life-cycle events are
triggered).</p>
<h2 style="clear:right;">Step 9</h2>
<p>Now pull out the previous handling code from the
<code>onActivityResult()</code> method in the <code>Notepadv3</code>
class.</p>
<p>All of the note retrieval and updating now happens within the
<code>NoteEdit</code> life cycle, so all the <code>onActivityResult()</code>
method needs to do is update its view of the data, no other work is
necessary. The resulting method should look like this:</p>
<pre>
&#64;Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
fillData();
}</pre>
<p>Because the other class now does the work, all this has to do is refresh
the data.</p>
<h2>Step 10</h2>
<p>Also remove the lines which set the title and body from the
<code>onListItemClick()</code> method (again they are no longer needed,
only the <code>mRowId</code> is):</p>
<pre>
Cursor c = mNotesCursor;
c.moveToPosition(position);</pre>
<br>
and also remove:
<br>
<pre>
i.putExtra(NotesDbAdapter.KEY_TITLE, c.getString(
c.getColumnIndex(NotesDbAdapter.KEY_TITLE)));
i.putExtra(NotesDbAdapter.KEY_BODY, c.getString(
c.getColumnIndex(NotesDbAdapter.KEY_BODY)));</pre>
<br>
so that all that should be left in that method is:
<br>
<pre>
super.onListItemClick(l, v, position, id);
Intent i = new Intent(this, NoteEdit.class);
i.putExtra(NotesDbAdapter.KEY_ROWID, id);
startActivityForResult(i, ACTIVITY_EDIT);</pre>
<p>You can also now remove the mNotesCursor field from the class, and set it back to using
a local variable in the <code>fillData()</code> method:
<br><pre>
Cursor notesCursor = mDbHelper.fetchAllNotes();</pre></p>
<p>Note that the <code>m</code> in <code>mNotesCursor</code> denotes a member field, so when we
make <code>notesCursor</code> a local variable, we drop the <code>m</code>. Remember to rename the
other occurrences of <code>mNotesCursor</code> in your <code>fillData()</code> method.
</ol>
<p>
Run it! (use <em>Run As -&gt; Android Application</em> on the project right
click menu again)</p>
<h2>Solution and Next Steps</h2>
<p>You can see the solution to this exercise in <code>Notepadv3Solution</code>
from
the zip file to compare with your own.</p>
<p>
When you are ready, move on to the <a href="notepad-extra-credit.html">Tutorial
Extra Credit</a> exercise, where you can use the Eclipse debugger to
examine the life-cycle events as they happen.</p>
@@ -0,0 +1,70 @@
page.title=Notepad Extra Credit
parent.title=Notepad Tutorial
parent.link=index.html
@jd:body
<p><em>In this exercise, you will use the debugger to look at the work you did
in Exercise 3. This exercise demonstrates:</em></p>
<ul>
<li><em>How to set breakpoints to observe execution</em> </li>
<li><em>How to run your application in debug mode</code></em></li>
</ul>
<div style="float:right;white-space:nowrap">
[<a href="notepad-ex1.html">Exercise 1</a>]
[<a href="notepad-ex2.html">Exercise 2</a>]
[<a href="notepad-ex3.html">Exercise 3</a>]
<span style="color:#BBB;">
[<a href="notepad-extra-credit.html" style="color:#BBB;">Extra Credit</a>]
</span>
</div>
<h2>Step 1</h2>
<p>Using the working <code>Notepadv3</code>, put breakpoints in the code at the
beginning of the <code>onCreate()</code>, <code>onPause()</code>,
<code>onSaveInstanceState()</code> and <code>onResume()</code> methods in the
<code>NoteEdit</code> class (if you are not familiar with Eclipse, just
right click in the narrow grey border on the left of the edit window at the
line you want a breakpoint, and select <em>Toggle Breakpoint</em>, you
should see a blue dot appear).</p>
<h2>Step 2</h2>
<p>Now start the notepad demo in debug mode:</p>
<ol type="a">
<li>
Right click on the <code>Notepadv3</code> project and from the Debug menu
select <em>Debug As -&gt; Android Application.</em></li>
<li>
The Android emulator should say <em>"waiting for debugger to connect"</em>
briefly and then run the application.</li>
<li>
If it gets stuck on the waiting... screen, quit the emulator and Eclipse,
from the command line do an <code>adb kill-server</code>, and then restart
Eclipse and try again.</li></ol>
<h2>Step 3</h2>
<p>When you edit or create a new note you should see the breakpoints getting
hit and the execution stopping.</p>
<h2>Step 4</h2>
<p>Hit the Resume button to let execution continue (yellow rectangle with a
green triangle to its right in the Eclipse toolbars near the top).</p>
<h2>Step 5</h2>
<p>Experiment a bit with the confirm and back buttons, and try pressing Home and
making other mode changes. Watch what life-cycle events are generated and
when.</p>
<p>The Android Eclipse plugin not only offers excellent debugging support for
your application development, but also superb profiling support. You can also
try using <a href="{@docRoot}guide/developing/tools/traceview.html">Traceview</a> to profile your application. If your application is running too slow, this can help you
find the bottlenecks and fix them.</p>
@@ -0,0 +1,143 @@
page.title=Notepad Tutorial
@jd:body
<p>The tutorial in this section gives you a &quot;hands-on&quot; introduction
to the Android framework and the tools you use to build applications on it.
Starting from a preconfigured project file, it guides you through the process of
developing a simple notepad application and provides concrete examples of how to
set up the project, develop the application logic and user interface, and then
compile and run the application. </p>
<p>The tutorial presents the notepad application development as a set of
exercises (see below), each consisting of several steps. You can follow along
with the steps in each exercise and gradually build up and refine your
application. The exercises explain each step in detail and provide all the
sample code you need to complete the application. </p>
<p>When you are finished with the tutorial, you will have created a functioning
Android application and learned in depth about many of the most important
concepts in Android development. If you want to add more complex features to
your application, you can examine the code in an alternative implementation
of a notepad application, in the
<a href="{@docRoot}samples/NotePad/index.html">Sample Code</a> documentation. </p>
<a name="who"></a>
<h2>Who Should Use this Tutorial</h2>
<p>This tutorial is designed for experienced developers, especially those with
knowledge of the Java programming language. If you haven't written Java
applications before, you can still use the tutorial, but you might need to work
at a slower pace. </p>
<p>The tutorial assumes that you have some familiarity with the basic Android
application concepts and terminology. If you aren't yet familiar with those, you
should read <a href="{@docRoot}intro/anatomy.html">Overview of an Android
Application</a> before continuing. </p>
<p>Also note that this tutorial uses
the Eclipse development environment, with the Android plugin installed. If you
are not using Eclipse, you can follow the exercises and build the application,
but you will need to determine how to accomplish the Eclipse-specific
steps in your environment. </p>
<a name="preparing"></a>
<h2>Preparing for the Exercises</h2>
<p>This tutorial builds on the information provided in the <a
href="{@docRoot}intro/installing.html">Installing the SDK</a> and <a
href="{@docRoot}intro/hello-android.html">Hello Android</a>
documents, which explain in detail how to set up your development environment
for building Android applications. Before you start this tutorial, you should
read both these documents, have the SDK installed, and your work environment set up.</p>
<p>To prepare for this lesson:</p>
<ol>
<li>Download the <a href="codelab/NotepadCodeLab.zip">project
exercises archive (.zip)</a></li>
<li>Unpack the archive file to a suitable location on your machine</li>
<li>Open the <code>NotepadCodeLab</code> folder</li>
</ol>
<p>Inside the <code>NotepadCodeLab</code> folder, you should see six project
files: <code>Notepadv1</code>,
<code>Notepadv2</code>, <code>Notepadv3</code>,
<code>Notepadv1Solution</code>, <code>Notepadv2Solution</code>
and <code>Notepadv3Solution</code>. The <code>Notepadv#</code> projects are
the starting points for each of the exercises, while the
<code>Notepadv#Solution</code> projects are the exercise
solutions. If you are having trouble with a particular exercise, you
can compare your current work against the exercise solution.</p>
<a name="exercises"></a>
<h2> Exercises</h2>
<p>The table below lists the tutorial exercises and describes the development
areas that each covers. Each exercise assumes that you have completed any
previous exercises.</p>
<table border="0" style="padding:4px;spacing:2px;" summary="This
table lists the
tutorial examples and describes what each covers. ">
<tr>
<th width="120"><a href="{@docRoot}intro/tutorial-ex1.html">Exercise
1</a></th>
<td>Start here. Construct a simple notes list that lets the user add new notes but not
edit them. Demonstrates the basics of <code>ListActivity</code> and creating
and handling
menu options. Uses a SQLite database to store the notes.</td>
</tr>
<tr>
<th><a href="{@docRoot}intro/tutorial-ex2.html">Exercise 2</a></th>
<td>Add a second Activity to the
application. Demonstrates constructing a
new Activity, adding it to the Android manifest, passing data between the
activities, and using more advanced screen layout. Also shows how to
invoke another Activity to return a result, using
<code>startActivityForResult()</code>.</td>
</tr>
<tr>
<th><a href="{@docRoot}intro/tutorial-ex3.html">Exercise 3</a></th>
<td>Add handling of life-cycle events to
the application, to let it
maintain application state across the life cycle. </td>
</tr>
<tr>
<th><a href="{@docRoot}intro/tutorial-extra-credit.html">Extra
Credit</a></th>
<td>Demonstrates how to use the Eclipse
debugger and how you can use it to
view life-cycle events as they are generated. This section is optional but
highly recommended.</td>
</tr>
</table>
<a name="other"></a>
<h2>Other Resources and Further Learning</h2>
<ul>
<li>For a lighter but broader introduction to concepts not covered in the
tutorial,
take a look at <a href="{@docRoot}kb/commontasks.html">Common Android Tasks</a>.</li>
<li>The Android SDK includes a variety of fully functioning sample applications
that make excellent opportunities for further learning. You can find the sample
applications in the <code>samples/</code> directory of your downloaded SDK.</li>
<li>This tutorial draws from the full Notepad application included in the
<code>samples/</code> directory of the SDK, though it does not match it exactly.
When you are done with the tutorial,
it is highly recommended that you take a closer look at this version of the Notepad
application,
as it demonstrates a variety of interesting additions for your application,
such as:</li>
<ul>
<li>Setting up a custom striped list for the list of notes.</li>
<li>Creating a custom text edit view that overrides the <code>draw()</code>
method to
make it look like a lined notepad.</li>
<li>Implementing a full <code>ContentProvider</code> for notes.</li>
<li>Reverting and discarding edits instead of just automatically saving
them.</li>
</ul>
</ul>
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,506 @@
page.title=Hello, Testing
@jd:body
<div id="qv-wrapper">
<div id="qv">
<h2>In this document</h2>
<ol>
<li>
<a href="#CreateTestProject">Creating the Test Project</a>
</li>
<li>
<a href="#CreateTestClass">Creating the Test Case Class</a>
<ol>
<li>
<a href="#CreateTestCaseClassFile">Adding the test case class file</a>
</li>
<li>
<a href="#CreateConstructor">Adding the test case constructor</a>
</li>
<li>
<a href="#CreateSetUp">Adding a setup method</a>
</li>
<li>
<a href="#CreatePreConditions">Adding a preconditions test</a>
</li>
<li>
<a href="#CreateText">Adding a unit test</a>
</li>
<li>
<a href="#CompleteTest">The finished test case class</a>
</li>
</ol>
</li>
<li>
<a href="#RunTest">Running the Tests and Seeing the Results</a>
</li>
<li>
<a href="#NextSteps">Next Steps</a>
</li>
</ol>
<h2>Related Tutorials</h2>
<ol>
<li>
<a href="{@docRoot}resources/tutorials/hello-world.html">Hello, World</a>
</li>
<li>
<a href="{@docRoot}resources/tutorials/testing/activity_test.html">Activity Testing</a>
</li>
</ol>
<h2>See Also</h2>
<ol>
<li>
<a href="{@docRoot}guide/topics/testing/testing_android.html">Testing Android Applications</a>
</li>
<li>
{@link android.test.ActivityInstrumentationTestCase2}
</li>
<li>
{@link android.test.InstrumentationTestRunner}
</li>
</ol>
</div>
</div>
<p>
Android offers a powerful but easy-to-use testing framework that is well integrated with the SDK tools. Because writing
tests is an important part of any development effort, this tutorial introduces the basics of testing and helps you get started testing quickly.
To keep things simple, this tutorial builds on the <a href="{@docRoot}resources/tutorials/hello-world.html">Hello World</a> tutorial, which you may have already completed.
It guides you through the process of setting up a test project, adding a test, and running the test against the Hello World application, all from inside the Eclipse environment.
Of course, when you are done with this tutorial, you will want to create a test project for your own app and add various types of tests to it.
</p>
<p>
If you'd like to read an overview of the test and instrumentation framework and the core test case classes available, look at
the <a href="{@docRoot}guide/topics/testing/testing_android.html">Testing Android Applications</a> topic.
If you prefer a more advanced testing tutorial, try the
<a href="{@docRoot}resources/tutorials/testing/activity_test.html">Activity Testing</a> tutorial.
</p>
<h2 id="Prerequisites">Prerequisites</h2>
<p>
This tutorial and its code depend on the Hello World tutorial. If you haven't completed that tutorial already,
do so now. You will learn the fundamentals of Android application development, and you will
have an Android application that is ready to be tested. The tutorial guides you through the
setup of an Android test project using the ADT Plugin for Eclipse and other SDK tools.
You will need an SDK development platform that is version 1.5
(API level 3) or higher.
</p>
<p>
If you aren't developing in Eclipse with ADT or you would like to run tests directly from the
command line, please see the topic <a href="{@docRoot}guide/developing/testing/testing_otheride.html">Testing in Other IDEs</a>
for instructions.
</p>
<h2 id="CreateTestProject">Creating the Test Project</h2>
<p>
In the Hello World tutorial you created Android application project called
HelloAndroid. A test of an Android application is also an Android
application, and you create it within an Eclipse project. The Eclipse with ADT
<strong>New Android Test Project</strong> dialog creates a new test project and the
framework of a new test application at the same time.
</p>
<p>
To create the test project and test application framework in Eclipse with ADT, follow these steps
</p>
<ol>
<li>
In Eclipse, select <strong>New</strong> &gt; <strong>Project</strong> &gt; <strong>Android</strong> &gt; <strong>Android Test Project</strong>.
<p>
<a href="{@docRoot}images/testing/hwtest_new_test_project_menu.png">
<img alt="New Android Test Project menu" src="{@docRoot}images/testing/hwtest_new_test_project_menu.png" style="height:230px"/>
</a>
</p>
<p>
The New Android Test Project dialog appears.
</p>
</li>
<li>
Set the following values:
<ul>
<li>
<em>Test Project Name:</em> &quot;HelloAndroidTest&quot;
</li>
<li>
<em>Test Target:</em> Set &quot;An existing Android project&quot;, click Browse, and then
select &quot;HelloAndroid&quot; from the list of projects.
</li>
<li>
<em>Build Target:</em> Set a target whose platform is Android 1.5 or above.
</li>
<li>
<em>Application name:</em> &quot;HelloAndroidTest&quot;
</li>
<li>
<em>Package name:</em> &quot;<code>com.example.helloandroid.test</code>&quot;
</li>
</ul>
<p>
The dialog should now look like this:
</p>
<a href="{@docRoot}images/testing/hwtest_new_test_project_dialog_complete_callouts.png">
<img alt="New Android Test Project dialog with entries" src="{@docRoot}images/testing/hwtest_new_test_project_dialog_complete_callouts.png" style="height:230px"/>
</a>
</li>
<li>
Click Finish. The new project appears in the Package Explorer.
</li>
</ol>
<h2 id="CreateTestClass">Creating the Test Case Class</h2>
<p>
You now have a test project HelloAndroidTest, and the basic structure of a test application
also called HelloAndroidTest. The basic structure includes all the files and directories you
need to build and run a test application, <em>except for</em> the class that contains
your tests (the <strong>test case class</strong>).
</p>
<p>
The next step is to define the test case class. In this tutorial, you define a test case class
that extends one of Android's test case classes designed for Activities. The class contains
definitions for four methods:
</p>
<ol>
<li>
<code>HelloAndroidTest</code>: This defines the constructor for the class. It is
required by the Android testing framework.
</li>
<li>
<code>setUp()</code>: This overrides the JUnit <code>setUp()</code> method. You use
it to initialize the environment before each test runs.
</li>
<li>
<code>testPreconditions()</code>: This defines a small test that ensures the Hello, Android
application starts up correctly.
</li>
<li>
<code>testText()</code>: This tests that what is displayed on the screen is the
same as what is contained in the application's string resources. It is an example of
a real unit test you would perform against an application's UI.
</li>
</ol>
<p>
The following sections contain the code for the test case class and its methods.
</p>
<h3 id="CreateTestCaseClassFile">Adding the test case class file</h3>
<p>
To add the Java file for the test case class, follow these steps
</p>
<ol>
<li>
In Eclipse, open the HelloAndroidTest project if it is not already open.
</li>
<li>
Within HelloAndroidTest, expand the <code>src/</code> folder and
then find the package icon for <code>com.example.helloandroid.test</code>.
Right-click on the package icon and select <strong>New</strong> &gt; <strong>Class</strong>:
<p>
<a href="{@docRoot}images/testing/hwtest_create_test_class_menu_callouts.png">
<img alt="Menu for creating a new class in the test application" src="{@docRoot}images/testing/hwtest_create_test_class_menu_callouts.png" style="height:230px"/>
</a>
</p>
<p>
The New Java Class dialog appears.
</p>
</li>
<li>
In the dialog, enter the following:
<ul>
<li>
<em>Name:</em> &quot;HelloAndroidTest&quot;. This becomes the name of your test class.
</li>
<li>
<em>Superclass:</em> &quot;<code>android.test.ActivityInstrumentationTestCase2&lt;HelloAndroid&gt;</code>&quot;.
The superclass is parameterized by an Activity class name.
<p>
The dialog should now look like this:
</p>
<a href="{@docRoot}images/testing/hwtest_new_test_class_dialog_complete_callouts.png">
<img alt="New Java Class dialog with entries" src="{@docRoot}images/testing/hwtest_new_test_class_dialog_complete_callouts.png" style="height:230px"/>
</a>
</li>
</ul>
<p>
Do not change any of the other settings. Click Finish.
</p>
</li>
<li>
You now have a new file <code>HelloAndroidTest.java</code> in the project.
This file contains the class <code>HelloAndroidTest</code>,
which extends the Activity test case class
<code>ActivityInstrumentationTestCase2&lt;T&gt;</code>. You parameterize the
class with <code>HelloAndroid</code>, which is the class name of the activity under test.
</li>
<li>
Open <code>HelloAndroidTest.java</code>. It should look like this:
<pre class="prettyprint">
package com.example.helloandroid.test;
import android.test.ActivityInstrumentationTestCase2;
public class HelloAndroidTest extends ActivityInstrumentationTestCase2&lt;HelloAndroid&gt; {
}
</pre>
</li>
<li>
The test case class depends on the <code>HelloAndroid</code> class, which is not
yet imported. To import the class, add the following line just before the current
<code>import</code> statement:
<pre class="prettyprint">
import com.example.helloandroid.HelloAndroid;
</pre>
</li>
</ol>
<h3 id="CreateConstructor">Adding the test case constructor</h3>
<p>
The test case class constructor is used by the Android testing framework when you run the test.
It calls the super constructor with parameters that tell the framework what Android application
should be tested.
</p>
<p>
Add the following constructor method immediately after the class definition:
</p>
<pre class="prettyprint">
public HelloAndroidTest() {
super("com.example.helloandroid", HelloAndroid.class);
}
</pre>
<p>
Save the file <code>HelloAndroidTest.java</code>.
</p>
<h3 id="CreateSetUp">Adding a setup method</h3>
<p>
The <code>setUp()</code> method overrides the JUnit {@link junit.framework.TestCase#setUp() setUp()}
method, which the Android testing framework calls prior to running each test method. You use
<code>setUp()</code> to initialize variables and prepare the test environment. For this
test case, the <code>setUp()</code> method starts the Hello, Android application,
retrieves the text being displayed on the screen, and retrieves the text string in the
resource file.
</p>
<p>
First, add the following code immediately after the constructor method:
</p>
<pre class="prettyprint">
&#064;Override
protected void setUp() throws Exception {
super.setUp();
mActivity = this.getActivity();
mView = (TextView) mActivity.findViewById(com.example.helloandroid.R.id.textview);
resourceString = mActivity.getString(com.example.helloandroid.R.string.hello);
}
</pre>
<p>
For this code to work, you must also add some class members and another import statement. To
add the class members, add the following code immediately after the class definition:
</p>
<pre class="prettyprint">
private HelloAndroid mActivity;
private TextView mView;
private String resourceString;
</pre>
<p>
To add the import statement, add the following statement just after the import for
<code>android.test.ActivityInstrumentationTestCase2</code>:
</p>
<pre class="prettyprint">
import android.widget.TextView;
</pre>
<h3 id="CreatePreConditions">Adding a preconditions test</h3>
<p>
A preconditions test checks the initial application conditions prior to executing other tests.
It's similar to <code>setUp()</code>, but with less overhead, since it only runs once.
</p>
<p>
Although a preconditions test can check for a variety of different conditions,
in this application it only needs to check whether the application under test is
initialized properly and the target TextView exists.
To do this, it calls the inherited
{@link junit.framework.Assert#assertNotNull(Object) assertNotNull()}
method, passing a reference to the TextView.
The test succeeds only if the object reference is not null.
</p>
<pre class="prettyprint">
public void testPreconditions() {
assertNotNull(mView);
}
</pre>
<h3 id="CreateText">Adding a unit test</h3>
<p>
Now add a simple unit test to the test case class.
The method <code>testText()</code> will call a
{@link junit.framework.Assert JUnit Assert}
method to check whether the target TextView is displaying the expected text.
</p>
<p>
For this example, the test expects that the TextView is
displaying the string resource that was originally declared for it in HelloAndroid's
<code>main.xml</code> file, referred to by the resource ID <code>hello</code>.
The call to
{@link junit.framework.Assert#assertEquals(String, String) assertEquals(String,String)}
compares the expected value, read directly from the <code>hello</code>string resource,
to the text displayed by the TextView, obtained from the
TextView's <code>getText()</code> method. The test succeeds only if the strings match.
</p>
<p>
To add this test, add the following code
immediately after the <code>testPreconditions()</code> method:
</p>
<pre class="prettyprint">
public void testText() {
assertEquals(resourceString,(String)mView.getText());
}
</pre>
<h3 id="CompleteTest">The finished test case class</h3>
<p>
You have now finished writing the test. This is what the complete test case class
should look like:
</p>
<pre class="prettyprint">
package com.example.helloandroid.test;
import com.example.helloandroid.HelloAndroid;
import android.test.ActivityInstrumentationTestCase2;
import android.widget.TextView;
public class HelloAndroidTest extends ActivityInstrumentationTestCase2&lt;HelloAndroid&gt; {
private HelloAndroid mActivity; // the activity under test
private TextView mView; // the activity's TextView (the only view)
private String resourceString;
public HelloAndroidTest() {
super("com.example.helloandroid", HelloAndroid.class);
}
&#064;Override
protected void setUp() throws Exception {
super.setUp();
mActivity = this.getActivity();
mView = (TextView) mActivity.findViewById(com.example.helloandroid.R.id.textview);
resourceString = mActivity.getString(com.example.helloandroid.R.string.hello);
}
public void testPreconditions() {
assertNotNull(mView);
}
public void testText() {
assertEquals(resourceString,(String)mView.getText());
}
}
</pre>
<h2 id="RunTest">Running the Tests and Seeing the Results</h2>
<p>
You can now run the tests you've created against the Hello, Android application. In Eclipse with
ADT, you run a test application as an <strong>Android JUnit test</strong> rather than a regular
Android application.
</p>
<p>
To run the test application as an Android JUnit test, in the Package Explorer right-click
the HelloAndroidTest project and select <strong>Run As</strong> &gt; <strong>Android JUnit Test</strong>
</p>
<a href="{@docRoot}images/testing/hwtest_runas_menu_callouts.png">
<img alt="Menu to run Hello, World as an Android JUnit test"
src="{@docRoot}images/testing/hwtest_runas_menu_callouts.png" style="height:230px">
</a>
<p>
The ADT plugin then launches the test application and the application
under test on a the target emulator or device. When both applications are running,
the testing framework runs the tests and reports the results in the JUnit view of Eclipse,
which appears by default as a tab next to the Package Explorer.
</p>
<p>
As shown below, the JUnit view shows test results in two separate panes:
an upper pane summarizes the tests that were run and a lower pane reports the failure traces
for the tests. In this case, the tests in this example have run successfully, so there is no
failure reported in the view:
</p>
<a href="{@docRoot}images/testing/hwtest_junit_success.png">
<img src="{@docRoot}images/testing/hwtest_junit_success.png"
alt="JUnit test run success" style="height:230px"/>
</a>
<p>
The upper pane summarizes the test:
</p>
<ul>
<li>
&quot;Finished after <em>x</em> seconds&quot;: How long the test took to run.
</li>
<li>
&quot;Runs&quot;: The number of tests run.
</li>
<li>
&quot;Errors:&quot;: The number of program errors and exceptions encountered during
the test run.
</li>
<li>
&quot;Failures:&quot;: The number of assertion failures encountered during the
test run.
</li>
<li>
A progress bar. The progress bar extends from left to right as the tests run.
<p>
If all the tests succeed, the bar remains green.
If a test fails, the bar turns from green to red.
</p>
</li>
<li>
A test method summary. Below the bar, you see a line for each class in the
test application, labeled by its fully-qualified class name.
To look at the results for the individual methods in a test case class,
click the arrow at the left of the class to expand the line.
You see the name of each test method. To the right of the method name, you see the
time needed to run that method. You can look at the method's code by
double-clicking its name.
</li>
</ul>
<p>
The lower pane contains the failure trace. If all the tests are successful,
this pane is empty. If some tests fail, then if you select a failed test in the
upper pane, the lower view contains a stack trace for the test.
</p>
<h2 id="NextSteps">Next Steps</h2>
<p>
This simple example test application has shown you how to create a test project,
create a test class and test cases, and then run the tests against a target application.
Now that you are familiar with these fundamentals, here are some suggested next steps:
</p>
<p>
<strong>Learn more about testing on Android</strong>
</p>
<ul>
<li>
The
<a href="{@docRoot}guide/topics/testing/testing_android.html">Testing Android Applications</a>
document in the <em>Dev Guide</em> provides an overview of how testing on Android works.
If you are just getting started with Android testing, reading that document will
help you understand the tools available to you, so that you can develop effective
tests.
</li>
</ul>
<p>
<strong>Learn more about the testing classes available in Android</strong>
</p>
<ul>
<li>
For an overview of the types of testing classes you can use,
browse through the reference documentation for
{@link android.test.ActivityInstrumentationTestCase2},
{@link android.test.ProviderTestCase2},
{@link android.test.ServiceTestCase}, and
{@link junit.framework.Assert}.
</li>
</ul>
<p>
<strong>Explore the Android instrumentation framework</strong>
</p>
<ul>
<li>
The {@link android.test.InstrumentationTestRunner} class contains the code that Android uses
to run tests against an application. The {@link android.test.InstrumentationTestCase} class
is the base class for test case classes that use additional instrumentation features.
</li>
</ul>
<p>
<strong>Follow the Activity Testing tutorial</strong>
</p>
<ul>
<li>
The <a href="{@docRoot}resources/tutorials/testing/activity_test.html">Activity Testing</a>
tutorial is an excellent follow-up to this tutorial.
It guides you through a more complex testing scenario that you develop against a
more realistic application.
</li>
</ul>
@@ -0,0 +1,173 @@
page.title=Auto Complete
parent.title=Hello, Views
parent.link=index.html
@jd:body
<p>To create a text entry widget that provides auto-complete suggestions, use
the {@link android.widget.AutoCompleteTextView} widget. Suggestions are received from a
collection of strings associated with the widget through an {@link
android.widget.ArrayAdapter}.</p>
<p>In this tutorial, you will create a {@link android.widget.AutoCompleteTextView} widget that
provides suggestions for a country name.</p>
<ol>
<li>Start a new project named <em>HelloAutoComplete</em>.</li>
<li>Create an XML file named <code>list_item.xml</code> and save it inside the
<code>res/layout/</code> folder. Edit the file to look like this:
<pre>
&lt;?xml version="1.0" encoding="utf-8"?>
&lt;TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="10dp"
android:textSize="16sp"
android:textColor="#000">
&lt;/TextView>
</pre>
<p>This file defines a simple {@link android.widget.TextView} that will be used for each
item that appears in the list of suggestions.</p>
</li>
<li>Open the <code>res/layout/main.xml</code> file and insert the following:
<pre>
&lt;?xml version="1.0" encoding="utf-8"?>
&lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="5dp">
&lt;TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Country" />
&lt;AutoCompleteTextView android:id="@+id/autocomplete_country"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"/>
&lt;/LinearLayout>
</pre>
<p>The {@link android.widget.TextView} is a label that introduces the {@link
android.widget.AutoCompleteTextView} widget.
</li>
<li>Open <code>HelloAutoComplete.java</code> and insert the following code for the {@link
android.app.Activity#onCreate(Bundle) onCreate()} method:
<pre>
&#64;Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
AutoCompleteTextView textView = (AutoCompleteTextView) findViewById(R.id.autocomplete_country);
ArrayAdapter&lt;String> adapter = new ArrayAdapter&lt;String>(this, R.layout.list_item, COUNTRIES);
textView.setAdapter(adapter);
}
</pre>
<p>After the content view is set to the <code>main.xml</code> layout, the {@link
android.widget.AutoCompleteTextView} widget is captured from the layout with {@link
android.app.Activity#findViewById(int)}. A new {@link
android.widget.ArrayAdapter} is then initialized to bind the <code>list_item.xml</code> layout
to each list item in the <code>COUNTRIES</code> string array (defined in the next step).
Finally, {@link android.widget.AutoCompleteTextView#setAdapter(T) setAdapter()} is called to
associate the {@link android.widget.ArrayAdapter} with the
{@link android.widget.AutoCompleteTextView} widget so that the string array will populate
the list of suggestions.</p>
</li>
<li>Inside the <code>HelloAutoComplete</code> class, add the string array:
<pre>
static final String[] COUNTRIES = new String[] {
"Afghanistan", "Albania", "Algeria", "American Samoa", "Andorra",
"Angola", "Anguilla", "Antarctica", "Antigua and Barbuda", "Argentina",
"Armenia", "Aruba", "Australia", "Austria", "Azerbaijan",
"Bahrain", "Bangladesh", "Barbados", "Belarus", "Belgium",
"Belize", "Benin", "Bermuda", "Bhutan", "Bolivia",
"Bosnia and Herzegovina", "Botswana", "Bouvet Island", "Brazil", "British Indian Ocean Territory",
"British Virgin Islands", "Brunei", "Bulgaria", "Burkina Faso", "Burundi",
"Cote d'Ivoire", "Cambodia", "Cameroon", "Canada", "Cape Verde",
"Cayman Islands", "Central African Republic", "Chad", "Chile", "China",
"Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo",
"Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czech Republic",
"Democratic Republic of the Congo", "Denmark", "Djibouti", "Dominica", "Dominican Republic",
"East Timor", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea",
"Estonia", "Ethiopia", "Faeroe Islands", "Falkland Islands", "Fiji", "Finland",
"Former Yugoslav Republic of Macedonia", "France", "French Guiana", "French Polynesia",
"French Southern Territories", "Gabon", "Georgia", "Germany", "Ghana", "Gibraltar",
"Greece", "Greenland", "Grenada", "Guadeloupe", "Guam", "Guatemala", "Guinea", "Guinea-Bissau",
"Guyana", "Haiti", "Heard Island and McDonald Islands", "Honduras", "Hong Kong", "Hungary",
"Iceland", "India", "Indonesia", "Iran", "Iraq", "Ireland", "Israel", "Italy", "Jamaica",
"Japan", "Jordan", "Kazakhstan", "Kenya", "Kiribati", "Kuwait", "Kyrgyzstan", "Laos",
"Latvia", "Lebanon", "Lesotho", "Liberia", "Libya", "Liechtenstein", "Lithuania", "Luxembourg",
"Macau", "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", "Marshall Islands",
"Martinique", "Mauritania", "Mauritius", "Mayotte", "Mexico", "Micronesia", "Moldova",
"Monaco", "Mongolia", "Montserrat", "Morocco", "Mozambique", "Myanmar", "Namibia",
"Nauru", "Nepal", "Netherlands", "Netherlands Antilles", "New Caledonia", "New Zealand",
"Nicaragua", "Niger", "Nigeria", "Niue", "Norfolk Island", "North Korea", "Northern Marianas",
"Norway", "Oman", "Pakistan", "Palau", "Panama", "Papua New Guinea", "Paraguay", "Peru",
"Philippines", "Pitcairn Islands", "Poland", "Portugal", "Puerto Rico", "Qatar",
"Reunion", "Romania", "Russia", "Rwanda", "Sqo Tome and Principe", "Saint Helena",
"Saint Kitts and Nevis", "Saint Lucia", "Saint Pierre and Miquelon",
"Saint Vincent and the Grenadines", "Samoa", "San Marino", "Saudi Arabia", "Senegal",
"Seychelles", "Sierra Leone", "Singapore", "Slovakia", "Slovenia", "Solomon Islands",
"Somalia", "South Africa", "South Georgia and the South Sandwich Islands", "South Korea",
"Spain", "Sri Lanka", "Sudan", "Suriname", "Svalbard and Jan Mayen", "Swaziland", "Sweden",
"Switzerland", "Syria", "Taiwan", "Tajikistan", "Tanzania", "Thailand", "The Bahamas",
"The Gambia", "Togo", "Tokelau", "Tonga", "Trinidad and Tobago", "Tunisia", "Turkey",
"Turkmenistan", "Turks and Caicos Islands", "Tuvalu", "Virgin Islands", "Uganda",
"Ukraine", "United Arab Emirates", "United Kingdom",
"United States", "United States Minor Outlying Islands", "Uruguay", "Uzbekistan",
"Vanuatu", "Vatican City", "Venezuela", "Vietnam", "Wallis and Futuna", "Western Sahara",
"Yemen", "Yugoslavia", "Zambia", "Zimbabwe"
};
</pre>
<p>This is the list of suggestions that will be provided in a drop-down list when the user types into
the {@link android.widget.AutoCompleteTextView} widget.</p>
</li>
<li>Run the application.</li>
</ol>
<p>As you type, you should see something like this:</p>
<img src="images/hello-autocomplete.png" width="150px" />
<h2>More Information</h2>
<p>Note that using a hard-coded string array is not a recommended design practice because your
application code should focus on behavior, not content. Application content such as strings
should be externalized from the code in order to make modifications to the content easier and
facilitate localization of the content. The hard-coded strings are used in this tutorial only to
make it simple and focus on the {@link android.widget.AutoCompleteTextView} widget.
Instead, your application should declare such string arrays in an XML file. This can be done
with a {@code &lt;string-array&lt;} resource in your project {@code res/values/strings.xml} file.
For example:</p>
<pre>
&lt;?xml version="1.0" encoding="utf-8"?>
&lt;resources>
&lt;string-array name="countries_array">
&lt;item>Bahrain&lt;/item>
&lt;item>Bangladesh&lt;/item>
&lt;item>Barbados&lt;/item>
&lt;item>Belarus&lt;/item>
&lt;item>Belgium&lt;/item>
&lt;item>Belize&lt;/item>
&lt;item>Benin&lt;/item>
&lt;/string-array>
&lt;/resources>
</pre>
<p>To use these resource strings for the {@link android.widget.ArrayAdapter}, replace the original
{@link android.widget.ArrayAdapter} constructor line with the following:</p>
<pre>
String[] countries = getResources().getStringArray(R.array.countries_array);
ArrayAdapter&lt;String> adapter = new ArrayAdapter&lt;String>(this, R.layout.list_item, countries);
</pre>
<h3>References</h3>
<ul>
<li>{@link android.widget.ArrayAdapter}</li>
<li>{@link android.widget.AutoCompleteTextView}</li>
</ul>
@@ -0,0 +1,173 @@
page.title=Date Picker
parent.title=Hello, Views
parent.link=index.html
@jd:body
<p>To provide a widget for selecting a date, use the {@link android.widget.DatePicker}
widget, which allows the user to select the month, day, and year, in a familiar interface.</p>
<p>In this tutorial, you'll create a {@link android.app.DatePickerDialog}, which presents the
date picker in a floating dialog box at the press of a button. When the date is set by
the user, a {@link android.widget.TextView} will update with the new date.</p>
<ol>
<li>Start a new project named <em>HelloDatePicker</em>.</li>
<li>Open the <code>res/layout/main.xml</code> file and insert the following:
<pre>
&lt;?xml version="1.0" encoding="utf-8"?>
&lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
&lt;TextView android:id="@+id/dateDisplay"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=""/>
&lt;Button android:id="@+id/pickDate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Change the date"/>
&lt;/LinearLayout>
</pre>
<p>This creates a basic {@link android.widget.LinearLayout} with a {@link android.widget.TextView}
that will display the date and a {@link android.widget.Button} that will open the {@link
android.app.DatePickerDialog}.</p>
</li>
<li>Open <code>HelloDatePicker.java</code> and add the following members to the class:
<pre>
private TextView mDateDisplay;
private Button mPickDate;
private int mYear;
private int mMonth;
private int mDay;
static final int DATE_DIALOG_ID = 0;
</pre>
<p>The first group of members define variables for the layout {@link android.view.View}s and the
date items. The <code>DATE_DIALOG_ID</code> is a static integer that uniquely identifies the {@link
android.app.Dialog} that will display the date picker.</p>
</li>
<li>Now add the following code for the {@link android.app.Activity#onCreate(Bundle) onCreate()}
method:
<pre>
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// capture our View elements
mDateDisplay = (TextView) findViewById(R.id.dateDisplay);
mPickDate = (Button) findViewById(R.id.pickDate);
// add a click listener to the button
mPickDate.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
showDialog(DATE_DIALOG_ID);
}
});
// get the current date
final Calendar c = Calendar.getInstance();
mYear = c.get(Calendar.YEAR);
mMonth = c.get(Calendar.MONTH);
mDay = c.get(Calendar.DAY_OF_MONTH);
// display the current date (this method is below)
updateDisplay();
}
</pre>
<p>First, the content is set to the <code>main.xml</code> layout. Then the {@link
android.widget.TextView} and {@link android.widget.Button} elements are captured from the layout
with {@link android.app.Activity#findViewById(int)}. A
new {@link android.view.View.OnClickListener} is created for the
{@link android.widget.Button}, so that when it is clicked, it
will call {@link android.app.Activity#showDialog(int)}, passing the unique integer ID for
the date picker dialog. Using {@link android.app.Activity#showDialog(int)} allows the {@link
android.app.Activity} to manage the life-cycle of the dialog and will call the {@link
android.app.Activity#onCreateDialog(int)} callback method to request the {@link android.app.Dialog}
that should be displayed (which you'll
define later). After the on-click listener is set, a new {@link java.util.Calendar} is created
and the current year, month and day are acquired. Finally, the private
<code>updateDisplay()</code> method is called in order to fill the {@link android.widget.TextView}
with the current date.</p>
</li>
<li>Add the <code>updateDisplay()</code> method:
<pre>
// updates the date in the TextView
private void updateDisplay() {
mDateDisplay.setText(
new StringBuilder()
// Month is 0 based so add 1
.append(mMonth + 1).append("-")
.append(mDay).append("-")
.append(mYear).append(" "));
}
</pre>
<p>This method uses the member date values declared for the class to write the date to the layout's
{@link android.widget.TextView}, {@code mDateDisplay}, which was also declared and initialized
above.</p>
</li>
<li>Initialize a new {@link android.app.DatePickerDialog.OnDateSetListener} as a member of the
<code>HelloDatePicker</code> class:
<pre>
// the callback received when the user "sets" the date in the dialog
private DatePickerDialog.OnDateSetListener mDateSetListener =
new DatePickerDialog.OnDateSetListener() {
public void onDateSet(DatePicker view, int year,
int monthOfYear, int dayOfMonth) {
mYear = year;
mMonth = monthOfYear;
mDay = dayOfMonth;
updateDisplay();
}
};
</pre>
<p>The {@link android.app.DatePickerDialog.OnDateSetListener} listens for when the user
has set the date (by clicking the "Set" button). At that time, the {@link
android.app.DatePickerDialog.OnDateSetListener#onDateSet(DatePicker,int,int,int) onDateSet()}
callback method is called, which is defined to update the {@code mYear}, {@code mMonth}, and
{@code mDay} member fields with the new date then call the private <code>updateDisplay()</code>
method to update the {@link android.widget.TextView}.</p>
</li>
<li>Now add the {@link android.app.Activity#onCreateDialog(int)} callback method to the {@code
HelloDatePicker} class:
<pre>
&#64;Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case DATE_DIALOG_ID:
return new DatePickerDialog(this,
mDateSetListener,
mYear, mMonth, mDay);
}
return null;
}
</pre>
<p>This is an {@link android.app.Activity} callback method that is passed the integer ID given to
{@link android.app.Activity#showDialog(int)} (which is called by the button's {@link
android.view.View.OnClickListener}). When the ID matches the switch case defined here, a {@link
android.app.DatePickerDialog} is instantiated with the {@link
android.app.DatePickerDialog.OnDateSetListener} created in the previous
step, along with the date variables to initialize the widget date.</p>
</li>
<li>Run the application.</li>
</ol>
<p>When you press the "Change the date" button, you should see the following:</p>
<img src="images/hello-datepicker.png" width="150px" />
<h3>References</h3>
<ul>
<li>{@link android.app.DatePickerDialog}</li>
<li>{@link android.app.DatePickerDialog.OnDateSetListener}</li>
<li>{@link android.widget.Button}</li>
<li>{@link android.widget.TextView}</li>
<li>{@link java.util.Calendar}</li>
</ul>
@@ -0,0 +1,398 @@
page.title=Form Stuff
parent.title=Hello, Views
parent.link=index.html
@jd:body
<p>This tutorial introduces a variety of widgets that are useful when creating forms, such as
image buttons, text fields, checkboxes and radio buttons.</p>
<ol>
<li>Start a new project named <em>HelloFormStuff</em>.</li>
<li>Your <code>res/layout/main.xml</code> file should already have a basic {@link
android.widget.LinearLayout}:
<pre>
&lt;?xml version="1.0" encoding="utf-8"?>
&lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
&lt;/LinearLayout>
</pre>
<p>For each widget you want to add, just put the respective View inside this {@link
android.widget.LinearLayout}.</p>
</li>
</ol>
<p>Each section below also assumes that your <code>HelloFormStuff</code> Activity has the following
default implementation of the {@link android.app.Activity#onCreate(Bundle) onCreate()} method:</p>
<pre>
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
</pre>
<p>Now select which kind of form widget you'd like to create:</p>
<ul>
<li><a href="#CustomButton">Custom Button</a></li>
<li><a href="#EditText">Edit Text</a></li>
<li><a href="#Checkbox">Checkbox</a></li>
<li><a href="#RadioButtons">Radio Buttons</a></li>
<li><a href="#ToggleButton">Toggle Button</a></li>
<li><a href="#RatingBar">Rating Bar</a></li>
</ul>
<h2 id="CustomButton">Custom Button</h2>
<p>In this section, you will create a button with a custom image instead of text, using the {@link
android.widget.Button} widget and an XML file that defines three different images to use for the
different button states. When the button is pressed, a short message will be displayed.</p>
<img src="images/android_pressed.png" style="float:right;" title="android_pressed.png"/>
<img src="images/android_focused.png" style="float:right;clear:right;" title="android_focused.png"/>
<img src="images/android_normal.png" style="float:right;clear:right;" title="android_normal.png"/>
<ol>
<li>Copy the images on the right into the <code>res/drawable/</code> directory of
your project. These will be used for the different button states.</li>
<li>Create a new file in the <code>res/drawable/</code> directory named
<code>android_button.xml</code>.
Insert the following XML:
<pre>
&lt;?xml version="1.0" encoding="utf-8"?>
&lt;selector xmlns:android="http://schemas.android.com/apk/res/android">
&lt;item android:drawable="@drawable/android_pressed"
android:state_pressed="true" />
&lt;item android:drawable="@drawable/android_focused"
android:state_focused="true" />
&lt;item android:drawable="@drawable/android_normal" />
&lt;/selector>
</pre>
<p>This defines a single drawable resource, which will change its image based on the current
state of the button. The first <code>&lt;item></code> defines
<code>android_pressed.png</code> as the image when the button is pressed (it's been
activated); the second <code>&lt;item></code> defines <code>android_focused.png</code> as the image
when the button is focused (when the button is highlighted using the trackball or directional
pad); and the third <code>&lt;item></code> defines <code>android_normal.png</code> as the image
for the normal state (when neither pressed nor focused). This XML file now represents a single
drawable resource and when referenced by a {@link android.widget.Button} for its background,
the image displayed will change based on these three states.</p>
<p class="note"><strong>Note:</strong> The order of the <code>&lt;item></code> elements is
important. When this drawable is referenced, the <code>&lt;item></code>s are traversed in-order to
determine which one is appropriate for the current button state. Because the "normal" image is last,
it is only applied when the conditions <code>android:state_pressed</code> and
<code>android:state_focused</code> have both evaluated false.</li>
<li>Open the <code>res/layout/main.xml</code> file and add the {@link
android.widget.Button} element:
<pre>
&lt;Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:background="@drawable/android_button" />
</pre>
<p>The <code>android:background</code> attribute specifies the drawable resource to use for the
button background (which, when saved at <code>res/drawable/android.xml</code>, is
referenced as <code>@drawable/android</code>). This replaces the normal background image
used for buttons throughout the system. In order for the drawable to change its image based on
the button state, the image must be applied to the background.</p>
</li>
<li>To make the button do something when pressed, add the following
code at the end of the {@link android.app.Activity#onCreate(Bundle) onCreate()} method:
<pre>
final Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// Perform action on clicks
Toast.makeText(HelloFormStuff.this, "Beep Bop", Toast.LENGTH_SHORT).show();
}
});
</pre>
<p>This captures the {@link android.widget.Button} from the layout, then adds an {@link
android.view.View.OnClickListener}. The {@link android.view.View.OnClickListener}
must implement the {@link android.view.View.OnClickListener#onClick(View)} callback method, which
defines the action to be made when the button is clicked. In this example, a
{@link android.widget.Toast} message will be displayed.</p>
</li>
<li>Now run the application.</li>
</ol>
<h2 id="EditText">Edit Text</h2>
<p>In this section, you will create a text field for user input, using the {@link
android.widget.EditText} widget. Once text has been entered into the field, the "Enter" key will
display the text in a toast message.</p>
<ol>
<li>Open the <code>res/layout/main.xml</code> file and add the {@link android.widget.EditText}
element (inside the {@link android.widget.LinearLayout}):
<pre>
&lt;EditText
android:id="@+id/edittext"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
</pre>
</li>
<li>To do something with the text that the user types, add the following code
to the end of the {@link android.app.Activity#onCreate(Bundle) onCreate()} method:
<pre>
final EditText edittext = (EditText) findViewById(R.id.edittext);
edittext.setOnKeyListener(new OnKeyListener() {
public boolean onKey(View v, int keyCode, KeyEvent event) {
// If the event is a key-down event on the "enter" button
if ((event.getAction() == KeyEvent.ACTION_DOWN) &amp;&amp;
(keyCode == KeyEvent.KEYCODE_ENTER)) {
// Perform action on key press
Toast.makeText(HelloFormStuff.this, edittext.getText(), Toast.LENGTH_SHORT).show();
return true;
}
return false;
}
});
</pre>
<p>This captures the {@link android.widget.EditText} element from the layout and adds an {@link
android.view.View.OnKeyListener}. The {@link android.view.View.OnKeyListener} must implement the
{@link android.view.View.OnKeyListener#onKey(View,int,KeyEvent)} method, which
defines the action to be made when a key is pressed while the widget has focus. In this case, the
method is defined to listen for the Enter key (when pressed down), then pop up a {@link
android.widget.Toast} message with the text that has been entered. The {@link
android.view.View.OnKeyListener#onKey(View,int,KeyEvent)} method should always return
<code>true</code> if the event has been handled, so that the event doesn't bubble-up (which would
result in a carriage return in the text field).</p>
</li>
<li>Run the application.</li>
</ol>
<h2 id="Checkbox">Checkbox</h2>
<p>In this section, you will create a checkbox for selecting items, using the {@link
android.widget.CheckBox} widget. When the checkbox is pressed, a toast message will
indicate the current state of the checkbox.</p>
<ol>
<li>Open the <code>res/layout/main.xml</code> file and add the {@link android.widget.CheckBox}
element (inside the {@link android.widget.LinearLayout}):
<pre>
&lt;CheckBox android:id="@+id/checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="check it out" />
</pre>
</li>
<li>To do something when the state is changed, add the following code
to the end of the {@link android.app.Activity#onCreate(Bundle) onCreate()} method:
<pre>
final CheckBox checkbox = (CheckBox) findViewById(R.id.checkbox);
checkbox.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// Perform action on clicks, depending on whether it's now checked
if (((CheckBox) v).isChecked()) {
Toast.makeText(HelloFormStuff.this, "Selected", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(HelloFormStuff.this, "Not selected", Toast.LENGTH_SHORT).show();
}
}
});
</pre>
<p>This captures the {@link android.widget.CheckBox} element from the layout, then adds an {@link
android.view.View.OnClickListener}. The {@link android.view.View.OnClickListener} must implement the
{@link android.view.View.OnClickListener#onClick(View)} callback method, which
defines the action to be made when the checkbox is clicked. When clicked, {@link
android.widget.CompoundButton#isChecked()} is called to check the new state of the check box. If it
has been checked, then a {@link android.widget.Toast} displays the message "Selected", otherwise it
displays "Not selected". Note that the {@link android.view.View} object that is passed in the {@link
android.view.View.OnClickListener#onClick(View)} callback must be cast to a {@link
android.widget.CheckBox} because the {@link android.widget.CompoundButton#isChecked()} method is
not defined by the parent {@link android.view.View} class. The {@link android.widget.CheckBox}
handles its own state changes, so you only need to query the current state.</p>
</li>
<li>Run it.</li>
</ol>
<p class="note"><strong>Tip:</strong> If you need to change the state
yourself (such as when loading a saved {@link android.preference.CheckBoxPreference}),
use the {@link android.widget.CompoundButton#setChecked(boolean)} or {@link
android.widget.CompoundButton#toggle()} method.</p>
<h2 id="RadioButtons">Radio Buttons</h2>
<p>In this section, you will create two mutually-exclusive radio buttons (enabling one disables
the other), using the {@link android.widget.RadioGroup} and {@link android.widget.RadioButton}
widgets. When either radio button is pressed, a toast message will be displayed.</p>
<ol>
<li>Open the <code>res/layout/main.xml</code> file and add two {@link
android.widget.RadioButton}s, nested in a {@link android.widget.RadioGroup} (inside the {@link
android.widget.LinearLayout}):
<pre>
&lt;RadioGroup
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
&lt;RadioButton android:id="@+id/radio_red"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Red" />
&lt;RadioButton android:id="@+id/radio_blue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Blue" />
&lt;/RadioGroup>
</pre>
<p>It's important that the {@link android.widget.RadioButton}s are grouped together by the {@link
android.widget.RadioGroup} element so that no more than one can be selected at a time. This logic
is automatically handled by the Android system. When one {@link android.widget.RadioButton} within
a group is selected, all others are automatically deselected.</p>
</li>
<li>To do something when each {@link android.widget.RadioButton} is selected, you need an
{@link android.view.View.OnClickListener}. In this case, you want the listener to be re-usable, so
add the following code to create a new member in the <code>HelloFormStuff</code> Activity:
<pre>
private OnClickListener radio_listener = new OnClickListener() {
public void onClick(View v) {
// Perform action on clicks
RadioButton rb = (RadioButton) v;
Toast.makeText(HelloFormStuff.this, rb.getText(), Toast.LENGTH_SHORT).show();
}
};
</pre>
<p>First, the {@link android.view.View} that is passed to the {@link
android.view.View.OnClickListener#onClick(View)} method is cast into a RadioButton. Then a
{@link android.widget.Toast} message displays the selected radio button's text.</p>
<li>Now, at the bottom of the {@link android.app.Activity#onCreate(Bundle) onCreate()} method, add
the following:
<pre>
final RadioButton radio_red = (RadioButton) findViewById(R.id.radio_red);
final RadioButton radio_blue = (RadioButton) findViewById(R.id.radio_blue);
radio_red.setOnClickListener(radio_listener);
radio_blue.setOnClickListener(radio_listener);
</pre>
<p>This captures each of the {@link android.widget.RadioButton}s from the layout and adds the
newly-created {@link android.view.View.OnClickListener} to each.</p>
<li>Run the application.</li>
</ol>
<p class="note"><strong>Tip:</strong> If you need to change the state
yourself (such as when loading a saved {@link android.preference.CheckBoxPreference}),
use the {@link android.widget.CompoundButton#setChecked(boolean)} or {@link
android.widget.CompoundButton#toggle()} method.</p>
<h2 id="ToggleButton">Toggle Button</h2>
<p>In this section, you'll create a button used specifically for toggling between two
states, using the {@link android.widget.ToggleButton} widget. This widget is an excellent
alternative to radio buttons if you have two simple states that are mutually exclusive ("on" and
"off", for example).</p>
<ol>
<li>Open the <code>res/layout/main.xml</code> file and add the {@link android.widget.ToggleButton}
element (inside the {@link android.widget.LinearLayout}):
<pre>
&lt;ToggleButton android:id="@+id/togglebutton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textOn="Vibrate on"
android:textOff="Vibrate off"/>
</pre>
<p>The attributes <code>android:textOn</code> and <code>android:textOff</code> specify the text
for the button when the button has been toggled on or off. The default values are "ON" and
"OFF".</p>
</li>
<li>To do something when the state is changed, add the following code
to the end of the {@link android.app.Activity#onCreate(Bundle) onCreate()} method:
<pre>
final ToggleButton togglebutton = (ToggleButton) findViewById(R.id.togglebutton);
togglebutton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// Perform action on clicks
if (togglebutton.isChecked()) {
Toast.makeText(HelloFormStuff.this, "Checked", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(HelloFormStuff.this, "Not checked", Toast.LENGTH_SHORT).show();
}
}
});
</pre>
<p>This captures the {@link android.widget.ToggleButton} element from the layout, then adds an
{@link android.view.View.OnClickListener}. The {@link android.view.View.OnClickListener} must
implement the {@link android.view.View.OnClickListener#onClick(View)} callback method, which
defines the action to perform when the button is clicked. In this example, the callback
method checks the new state of the button, then shows a {@link android.widget.Toast} message that
indicates the current state.</p>
<p>Notice that the {@link android.widget.ToggleButton} handles its own state change between checked
and unchecked, so you just ask which it is.</p>
<li>Run the application.</li>
</ol>
<p class="note"><strong>Tip:</strong> If you need to change the state
yourself (such as when loading a saved {@link android.preference.CheckBoxPreference}),
use the {@link android.widget.CompoundButton#setChecked(boolean)} or {@link
android.widget.CompoundButton#toggle()} method.</p>
<h2 id="RatingBar">Rating Bar</h2>
<p>In this section, you'll create a widget that allows the user to provide a rating,
with the {@link android.widget.RatingBar} widget.</p>
<ol>
<li>Open the <code>res/layout/main.xml</code> file and add the {@link android.widget.RatingBar}
element (inside the {@link android.widget.LinearLayout}):
<pre>
&lt;RatingBar android:id="@+id/ratingbar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:numStars="5"
android:stepSize="1.0"/>
</pre>
<p>The <code>android:numStars</code> attribute defines how many stars to display for the rating
bar. The <code>android:stepSize</code> attribute defines the granularity for each
star (for example, a value of <code>0.5</code> would allow half-star ratings). </p>
</li>
<li>To do something when a new rating has been set, add the following code
to the end of the {@link android.app.Activity#onCreate(Bundle) onCreate()} method:
<pre>
final RatingBar ratingbar = (RatingBar) findViewById(R.id.ratingbar);
ratingbar.setOnRatingBarChangeListener(new OnRatingBarChangeListener() {
public void onRatingChanged(RatingBar ratingBar, float rating, boolean fromUser) {
Toast.makeText(HelloFormStuff.this, "New Rating: " + rating, Toast.LENGTH_SHORT).show();
}
});
</pre>
<p>This captures the {@link android.widget.RatingBar} widget from the layout with {@link
android.app.Activity#findViewById(int)} and then sets an {@link
android.widget.RatingBar.OnRatingBarChangeListener}. The {@link
android.widget.RatingBar.OnRatingBarChangeListener#onRatingChanged(RatingBar,float,boolean)
onRatingChanged()} callback method then defines the action to perform when the user sets a rating.
In this case, a simple {@link android.widget.Toast} message displays the new rating.</p>
<li>Run the application.</li>
</ol>
<p>If you've added all the form widgets above, your application should look like this:</p>
<img src="images/hello-formstuff.png" width="150px" />
<h3>References</h3>
<ul>
<li>{@link android.widget.ImageButton}</li>
<li>{@link android.widget.EditText}</li>
<li>{@link android.widget.CheckBox}</li>
<li>{@link android.widget.RadioButton}</li>
<li>{@link android.widget.ToggleButton}</li>
<li>{@link android.widget.RatingBar}</li>
</ul>
@@ -0,0 +1,171 @@
page.title=Gallery
parent.title=Hello, Views
parent.link=index.html
@jd:body
<p>{@link android.widget.Gallery} is a layout widget used to display items in a
horizontally scrolling list and positions the current selection at the center of the view.</p>
<p>In this tutorial, you'll create a gallery of photos and then display a toast message each time a
gallery item is selected.</p>
<ol>
<li>Start a new project named <em>HelloGallery</em>.</li>
<li>Find some photos you'd like to use, or use these <a
href="{@docRoot}shareables/sample_images.zip">sample images</a>. Save the images into the project's
<code>res/drawable/</code> directory.</li>
<li>Open the <code>res/layout/main.xml</code> file and insert the following:
<pre>
&lt;?xml version="1.0" encoding="utf-8"?>
&lt;Gallery xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/gallery"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
</pre>
</li>
<li>Open the <code>HelloGallery.java</code> file and insert the following code for the
{@link android.app.Activity#onCreate(Bundle) onCreate()} method:
<pre>
&#64;Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Gallery g = (Gallery) findViewById(R.id.gallery);
g.setAdapter(new ImageAdapter(this));
g.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView parent, View v, int position, long id) {
Toast.makeText(HelloGallery.this, "" + position, Toast.LENGTH_SHORT).show();
}
});
}
</pre>
<p>This starts by setting the {@code main.xml} layout as the content view and then capturing the
{@link android.widget.Gallery} from
the layout with {@link
android.app.Activity#findViewById(int)}. A custom {@link android.widget.BaseAdapter} called
<code>ImageAdapter</code> is
instantiated and applied to the {@link android.widget.Gallery} with {@link
android.widget.AdapterView#setAdapter(T) setAdapter()}. (The <code>ImageAdapter</code> class is
defined next.)
Then an anonymous {@link android.widget.AdapterView.OnItemClickListener} is instantiated. The
{@link android.widget.AdapterView.OnItemClickListener#onItemClick(AdapterView,View,int,long)}
callback method receives the {@link android.widget.AdapterView} where the click occurred, the
specific {@link android.view.View} that received the click, the
position of the {@link android.view.View} clicked (zero-based), and the row ID of the item clicked
(if applicable). In this example, all that's needed is the position of the click to show a {@link
android.widget.Toast} message that says the position of the item, using
{@link android.widget.Toast#makeText(Context,CharSequence,int)} and {@link
android.widget.Toast#show()} (in a real world scenario, this ID could be used to get the full sized
image for some other task).
</p>
</li>
<li>Create a new XML file in the <code>res/values/</code> directory named <code>attrs.xml</code>.
Insert the following:
<pre>
&lt;?xml version="1.0" encoding="utf-8"?>
&lt;resources>
&lt;declare-styleable name="HelloGallery">
&lt;attr name="android:galleryItemBackground" />
&lt;/declare-styleable>
&lt;/resources>
</pre>
<p>This is a custom styleable resource that can be applied to a layout. In this case, it will be
applied to the individual items placed into the {@link android.widget.Gallery} widget. The
<code>&lt;attr></code> element defines a specific attribute for the styleable, and in this case, it
refers to an existing platform attribute, {@link android.R.attr#galleryItemBackground}, which
defines a border styling for gallery items. In the next step, you'll
see how this attribute is referenced and then later applied to each item in the gallery.</p>
</li>
<li>Go back to the <code>HelloGallery.java</code> file. After the {@link
android.app.Activity#onCreate(Bundle)} method, define the custom <code>ImageAdapter</code> class:
<pre>
public class ImageAdapter extends BaseAdapter {
int mGalleryItemBackground;
private Context mContext;
private Integer[] mImageIds = {
R.drawable.sample_1,
R.drawable.sample_2,
R.drawable.sample_3,
R.drawable.sample_4,
R.drawable.sample_5,
R.drawable.sample_6,
R.drawable.sample_7
};
public ImageAdapter(Context c) {
mContext = c;
TypedArray a = obtainStyledAttributes(R.styleable.HelloGallery);
mGalleryItemBackground = a.getResourceId(
R.styleable.HelloGallery_android_galleryItemBackground, 0);
a.recycle();
}
public int getCount() {
return mImageIds.length;
}
public Object getItem(int position) {
return position;
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
ImageView i = new ImageView(mContext);
i.setImageResource(mImageIds[position]);
i.setLayoutParams(new Gallery.LayoutParams(150, 100));
i.setScaleType(ImageView.ScaleType.FIT_XY);
i.setBackgroundResource(mGalleryItemBackground);
return i;
}
}
</pre>
<p>First, there are a few member variables, including an array of IDs that reference
the images saved in the drawable resources directory ({@code res/drawable/}).</p>
<p>Next is the class constructor, where the {@link android.content.Context} for an {@code
ImageAdapter} instance is defined and the styleable
resource defined in the last step is acquired and saved to a local field. At the end of the
constructor, {@link android.content.res.TypedArray#recycle()} is called on the {@link
android.content.res.TypedArray} so it can be re-used by the system.</p>
<p>The methods {@link android.widget.Adapter#getCount()}, {@link
android.widget.Adapter#getItem(int)}, and {@link android.widget.Adapter#getItemId(int)} are methods
that must be implemented for simple queries on the {@link android.widget.Adapter}.
The {@link android.widget.Adapter#getView(int,View,ViewGroup) method does the
work to apply an image to an {@link android.widget.ImageView} that will be embedded in the
{@link android.widget.Gallery}. In this method, the member {@link android.content.Context} is used
to create a new {@link android.widget.ImageView}. The {@link android.widget.ImageView} is prepared
by applying an image from the local array of drawable resources, setting the {@link
android.widget.Gallery.LayoutParams} height and width for the image, setting the scale to fit the
{@link android.widget.ImageView} dimensions, and then finally setting the background to use the
styleable attribute acquired in the constructor.</p>
<p>See {@link android.widget.ImageView.ScaleType} for other image scaling options.</p>
</li>
<li>Run the application.</li>
</ol>
<p>You should see something like this:</p>
<img src="images/hello-gallery.png" width="150px" />
<h3>References</h3>
<ul>
<li>{@link android.widget.BaseAdapter}</li>
<li>{@link android.widget.Gallery}</li>
<li>{@link android.widget.ImageView}</li>
<li>{@link android.widget.AdapterView.OnItemClickListener}</li>
</ul>
@@ -0,0 +1,182 @@
page.title=Grid View
parent.title=Hello, Views
parent.link=index.html
@jd:body
<p>{@link android.widget.GridView} is a {@link android.view.ViewGroup} that displays items in a
two-dimensional,
scrollable grid. The grid items are automatically inserted to the layout using a {@link
android.widget.ListAdapter}.</p>
<p>In this tutorial, you'll create a grid of image thumbnails. When an item is selected, a
toast message will display the position of the image.</p>
<ol>
<li>Start a new project named <em>HelloGridView</em>.</li>
<li>Find some photos you'd like to use, or <a
href="{@docRoot}shareables/sample_images.zip">download these sample images</a>. Save the image files
into the project's
<code>res/drawable/</code> directory.</li>
<li>Open the <code>res/layout/main.xml</code> file and insert the following:
<pre>
&lt;?xml version="1.0" encoding="utf-8"?>
&lt;GridView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/gridview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:columnWidth="90dp"
android:numColumns="auto_fit"
android:verticalSpacing="10dp"
android:horizontalSpacing="10dp"
android:stretchMode="columnWidth"
android:gravity="center"
/>
</pre>
<p>This {@link android.widget.GridView} will fill the entire screen. The attributes are rather
self explanatory. For more information about valid attributes, see the {@link
android.widget.GridView} reference.</p>
</li>
<li>Open <code>HelloGridView.java</code> and insert the following code for the
{@link android.app.Activity#onCreate(Bundle) onCreate()} method:
<pre>
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
GridView gridview = (GridView) findViewById(R.id.gridview);
gridview.setAdapter(new ImageAdapter(this));
gridview.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView&lt;?> parent, View v, int position, long id) {
Toast.makeText(HelloGridView.this, "" + position, Toast.LENGTH_SHORT).show();
}
});
}
</pre>
<p>After the {@code main.xml} layout is set for the content view, the
{@link android.widget.GridView} is captured from the layout with {@link
android.app.Activity#findViewById(int)}. The {@link
android.widget.GridView#setAdapter(T) setAdapter()} method then sets a custom adapter ({@code
ImageAdapter}) as the source for all items to be displayed in the grid. The {@code ImageAdapter} is
created in the next step.</p>
<p>To do something when an item in the grid is clicked, the {@link
android.widget.AdapterView#setOnItemClickListener(OnItemClickListener) setOnItemClickListener()}
method is passed a new {@link android.widget.AdapterView.OnItemClickListener}. This anonymous
instance defines the {@link
android.widget.AdapterView.OnItemClickListener#onItemClick(AdapterView,View,int,long)
onItemClick()} callback method to show a {@link android.widget.Toast} that displays the index
position (zero-based) of the selected item (in a real world scenario, the position could be used to
get the full sized
image for some other task).</p>
</li>
<li>Create a new class called <code>ImageAdapter</code> that extends {@link
android.widget.BaseAdapter}:
<pre>
public class ImageAdapter extends BaseAdapter {
private Context mContext;
public ImageAdapter(Context c) {
mContext = c;
}
public int getCount() {
return mThumbIds.length;
}
public Object getItem(int position) {
return null;
}
public long getItemId(int position) {
return 0;
}
// create a new ImageView for each item referenced by the Adapter
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView;
if (convertView == null) { // if it's not recycled, initialize some attributes
imageView = new ImageView(mContext);
imageView.setLayoutParams(new GridView.LayoutParams(85, 85));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setPadding(8, 8, 8, 8);
} else {
imageView = (ImageView) convertView;
}
imageView.setImageResource(mThumbIds[position]);
return imageView;
}
// references to our images
private Integer[] mThumbIds = {
R.drawable.sample_2, R.drawable.sample_3,
R.drawable.sample_4, R.drawable.sample_5,
R.drawable.sample_6, R.drawable.sample_7,
R.drawable.sample_0, R.drawable.sample_1,
R.drawable.sample_2, R.drawable.sample_3,
R.drawable.sample_4, R.drawable.sample_5,
R.drawable.sample_6, R.drawable.sample_7,
R.drawable.sample_0, R.drawable.sample_1,
R.drawable.sample_2, R.drawable.sample_3,
R.drawable.sample_4, R.drawable.sample_5,
R.drawable.sample_6, R.drawable.sample_7
};
}
</pre>
<p>First, this implements some required methods inherited from {@link
android.widget.BaseAdapter}. The constructor and {@link
android.widget.Adapter#getCount()} are self-explanatory. Normally, {@link
android.widget.Adapter#getItem(int)} should return the actual object at the specified position in
the adapter, but it's ignored for this example. Likewise, {@link
android.widget.Adapter#getItemId(int)} should return the row id of the item, but it's not
needed here.</p>
<p>The first method necessary is {@link android.widget.Adapter#getView(int,View,ViewGroup)
getView()}. This method creates a new {@link android.view.View} for each image added to the {@code
ImageAdapter}. When this is called, a {@link android.view.View} is passed in, which is normally a
recycled object (at least after this has been called once), so there's a check to see if the
object is null. If it <em>is</em> null, an {@link android.widget.ImageView} is instantiated and
configured with desired properties for the image presentation:</p>
<ul>
<li>{@link android.view.View#setLayoutParams(ViewGroup.LayoutParams)} sets
the height and width for the View&mdash;this ensures that, no matter the size of the drawable, each
image is resized and cropped to fit in these dimensions, as appropriate.</li>
<li>{@link android.widget.ImageView#setScaleType(ImageView.ScaleType)} declares that images should
be cropped toward the center (if necessary).</li>
<li>{@link android.widget.ImageView#setPadding(int,int,int,int)} defines the padding for all
sides. (Note that, if the images have different aspect-ratios, then less
padding will cause for more cropping of the image if it does not match
the dimensions given to the ImageView.)</li>
</ul>
<p>If the {@link android.view.View} passed to {@link
android.widget.Adapter#getView(int,View,ViewGroup) getView()} is <em>not</em> null, then the local
{@link android.widget.ImageView} is initialized with the recycled {@link android.view.View}
object.</p>
<p>At the end of the {@link android.widget.Adapter#getView(int,View,ViewGroup) getView()} method,
the {@code
position} integer passed into the method is used to select an image from the {@code mThumbIds}
array, which is set as the image resource for the {@link android.widget.ImageView}.</p>
<p>All that's left is to define the {@code mThumbIds} array of drawable resources.</p>
</li>
<li>Run the application.</li>
</ol>
<p>Your grid layout should look something like this:</p>
<img src="images/hello-gridview.png" width="150px" />
<p>Try experimenting with the behaviors of the {@link android.widget.GridView} and {@link
android.widget.ImageView} elements by adjusting their properties. For example, instead of using
{@link android.view.View#setLayoutParams(ViewGroup.LayoutParams)}, try using
{@link android.widget.ImageView#setAdjustViewBounds(boolean)}. </p>
<h3>References</h3>
<ul>
<li>{@link android.widget.GridView}</li>
<li>{@link android.widget.ImageView}</li>
<li>{@link android.widget.BaseAdapter}</li>
<li>{@link android.widget.AdapterView.OnItemClickListener}</li>
</ul>
@@ -0,0 +1,133 @@
page.title=Linear Layout
parent.title=Hello, Views
parent.link=index.html
@jd:body
<p>{@link android.widget.LinearLayout} is a {@link android.view.ViewGroup} that displays child
{@link android.view.View} elements in a linear direction, either vertically or horizontally.</p>
<p>You should be careful about over-using the {@link android.widget.LinearLayout}. If you begin
nesting multiple {@link android.widget.LinearLayout}s, you may want to consider using a {@link
android.widget.RelativeLayout} instead.</p>
<ol>
<li>Start a new project named <em>HelloLinearLayout</em>.</li>
<li>Open the <code>res/layout/main.xml</code> file and insert the following:
<pre>
&lt;?xml version="1.0" encoding="utf-8"?>
&lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
&lt;LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1">
&lt;TextView
android:text="red"
android:gravity="center_horizontal"
android:background="#aa0000"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_weight="1"/>
&lt;TextView
android:text="green"
android:gravity="center_horizontal"
android:background="#00aa00"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_weight="1"/>
&lt;TextView
android:text="blue"
android:gravity="center_horizontal"
android:background="#0000aa"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_weight="1"/>
&lt;TextView
android:text="yellow"
android:gravity="center_horizontal"
android:background="#aaaa00"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_weight="1"/>
&lt;/LinearLayout>
&lt;LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1">
&lt;TextView
android:text="row one"
android:textSize="15pt"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"/>
&lt;TextView
android:text="row two"
android:textSize="15pt"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"/>
&lt;TextView
android:text="row three"
android:textSize="15pt"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"/>
&lt;TextView
android:text="row four"
android:textSize="15pt"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"/>
&lt;/LinearLayout>
&lt;/LinearLayout>
</pre>
<p>Carefully inspect this XML. There is a root {@link android.widget.LinearLayout} that defines
its orientation to be vertical&mdash;all child {@link android.view.View}s (of which it has two) will
be stacked vertically. The first child is
another {@link android.widget.LinearLayout} that uses a horizontal orientation and the second child
is a {@link android.widget.LinearLayout} that uses a vertical orientation. Each of these nested
{@link android.widget.LinearLayout}s contain several {@link android.widget.TextView} elements, which
are oriented with each other in the manner defined by their parent {@link
android.widget.LinearLayout}.</p>
</li>
<li>Now open <code>HelloLinearLayout.java</code> and be sure it loads the
<code>res/layout/main.xml</code> layout in the
{@link android.app.Activity#onCreate(Bundle) onCreate()} method:</p>
<pre>
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
</pre>
<p>The {@link android.app.Activity#setContentView(int)} method loads the
layout file for the {@link android.app.Activity}, specified by the resource
ID &mdash; <code>R.layout.main</code> refers to the <code>res/layout/main.xml</code> layout
file.</p>
</li>
<li>Run the application.</li>
</ol>
<p>You should see the following:</p>
<img src="images/hello-linearlayout.png" width="150px" />
<p>Notice how the XML attributes define each View's behavior. Try
experimenting with different values for <code>android:layout_weight</code> to see how the screen
real estate is distributed based on the weight of each element. See the <a
href="{@docRoot}guide/topics/ui/layout-objects.html#linearlayout">Common Layout Objects</a>
document for more about how {@link android.widget.LinearLayout} handles the
<code>android:layout_weight</code> attribute.</p>
<h3>References</h3>
<ul>
<li>{@link android.widget.LinearLayout}</li>
<li>{@link android.widget.TextView}</li>
</ul>
@@ -0,0 +1,170 @@
page.title=List View
parent.title=Hello, Views
parent.link=index.html
@jd:body
<p>{@link android.widget.ListView} is a {@link android.view.ViewGroup} that creates a list of
scrollable items. The list items are automatically inserted to the list using a {@link
android.widget.ListAdapter}.</p>
<p>In this tutorial, you'll create a scrollable list of country names that are read from a string array.
When a list item is selected, a toast message will display the position of the item in the list.</p>
<ol>
<li>Start a new project named <em>HelloListView</em>.</li>
<li>Create an XML file named <code>list_item.xml</code> and save it inside the
<code>res/layout/</code> folder. Insert the following:
<pre>
&lt;?xml version="1.0" encoding="utf-8"?>
&lt;TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="10dp"
android:textSize="16sp" >
&lt;/TextView>
</pre>
<p>This file defines the layout for each item that will be placed in the {@link
android.widget.ListView}.</p>
</li>
<li>Open the <code>HelloListView.java</code> and make the class extend {@link
android.app.ListActivity} (instead of {@link android.app.Activity}):
<pre>public class HelloListView extends ListActivity {</pre>
</li>
<li>Insert the following code for the {@link android.app.Activity#onCreate(Bundle) onCreate()}
method:
<pre>
&#64;Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setListAdapter(new ArrayAdapter&lt;String>(this, R.layout.list_item, COUNTRIES));
ListView lv = getListView();
lv.setTextFilterEnabled(true);
lv.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView&lt;?> parent, View view,
int position, long id) {
// When clicked, show a toast with the TextView text
Toast.makeText(getApplicationContext(), ((TextView) view).getText(),
Toast.LENGTH_SHORT).show();
}
});
}
</pre>
<p>Notice that this does not load a layout file for the Activity (which you usually do with {@link
android.app.Activity#setContentView(int)}). Instead, {@link
android.app.ListActivity#setListAdapter(ListAdapter)} automatically
adds a {@link android.widget.ListView} to fill the entire screen of the {@link
android.app.ListActivity}. This method takes an {@link android.widget.ArrayAdapter}, which
manages the array of list items that will be placed into the {@link android.widget.ListView}. The
{@link android.widget.ArrayAdapter} constructor takes the application {@link
android.content.Context}, the
layout description for each list item (created in
the previous step), and a {@link java.util.List} of objects to insert in the {@link
android.widget.ListView} (defined next).</p>
<p>The {@link android.widget.ListView#setTextFilterEnabled(boolean)} method turns on text filtering
for the {@link android.widget.ListView}, so that when the user begins typing, the list will be
filtered.</p>
<p>The {@link android.widget.ListView#setOnItemClickListener(OnItemClickListener)} method defines
the on-click listener for each item. When an item in the {@link android.widget.ListView} is clicked,
the {@link android.widget.AdapterView.OnItemClickListener#onItemClick(AdapterView,View,int,long)
onItemClick()} method is called and a {@link android.widget.Toast} message is displayed, using the
text from the clicked item.</p>
<p class="note"><strong>Tip:</strong> You can use list item designs provided by the platform
instead of defining your own layout file for the {@link android.widget.ListAdapter}. For example,
try using <code>android.R.layout.simple_list_item_1</code> instead of
<code>R.layout.list_item</code>.</p>
</li>
<li>After the {@link android.app.Activity#onCreate(Bundle) onCreate()} method, add the string
array:
<pre>
static final String[] COUNTRIES = new String[] {
"Afghanistan", "Albania", "Algeria", "American Samoa", "Andorra",
"Angola", "Anguilla", "Antarctica", "Antigua and Barbuda", "Argentina",
"Armenia", "Aruba", "Australia", "Austria", "Azerbaijan",
"Bahrain", "Bangladesh", "Barbados", "Belarus", "Belgium",
"Belize", "Benin", "Bermuda", "Bhutan", "Bolivia",
"Bosnia and Herzegovina", "Botswana", "Bouvet Island", "Brazil", "British Indian Ocean Territory",
"British Virgin Islands", "Brunei", "Bulgaria", "Burkina Faso", "Burundi",
"Cote d'Ivoire", "Cambodia", "Cameroon", "Canada", "Cape Verde",
"Cayman Islands", "Central African Republic", "Chad", "Chile", "China",
"Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo",
"Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czech Republic",
"Democratic Republic of the Congo", "Denmark", "Djibouti", "Dominica", "Dominican Republic",
"East Timor", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea",
"Estonia", "Ethiopia", "Faeroe Islands", "Falkland Islands", "Fiji", "Finland",
"Former Yugoslav Republic of Macedonia", "France", "French Guiana", "French Polynesia",
"French Southern Territories", "Gabon", "Georgia", "Germany", "Ghana", "Gibraltar",
"Greece", "Greenland", "Grenada", "Guadeloupe", "Guam", "Guatemala", "Guinea", "Guinea-Bissau",
"Guyana", "Haiti", "Heard Island and McDonald Islands", "Honduras", "Hong Kong", "Hungary",
"Iceland", "India", "Indonesia", "Iran", "Iraq", "Ireland", "Israel", "Italy", "Jamaica",
"Japan", "Jordan", "Kazakhstan", "Kenya", "Kiribati", "Kuwait", "Kyrgyzstan", "Laos",
"Latvia", "Lebanon", "Lesotho", "Liberia", "Libya", "Liechtenstein", "Lithuania", "Luxembourg",
"Macau", "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", "Marshall Islands",
"Martinique", "Mauritania", "Mauritius", "Mayotte", "Mexico", "Micronesia", "Moldova",
"Monaco", "Mongolia", "Montserrat", "Morocco", "Mozambique", "Myanmar", "Namibia",
"Nauru", "Nepal", "Netherlands", "Netherlands Antilles", "New Caledonia", "New Zealand",
"Nicaragua", "Niger", "Nigeria", "Niue", "Norfolk Island", "North Korea", "Northern Marianas",
"Norway", "Oman", "Pakistan", "Palau", "Panama", "Papua New Guinea", "Paraguay", "Peru",
"Philippines", "Pitcairn Islands", "Poland", "Portugal", "Puerto Rico", "Qatar",
"Reunion", "Romania", "Russia", "Rwanda", "Sqo Tome and Principe", "Saint Helena",
"Saint Kitts and Nevis", "Saint Lucia", "Saint Pierre and Miquelon",
"Saint Vincent and the Grenadines", "Samoa", "San Marino", "Saudi Arabia", "Senegal",
"Seychelles", "Sierra Leone", "Singapore", "Slovakia", "Slovenia", "Solomon Islands",
"Somalia", "South Africa", "South Georgia and the South Sandwich Islands", "South Korea",
"Spain", "Sri Lanka", "Sudan", "Suriname", "Svalbard and Jan Mayen", "Swaziland", "Sweden",
"Switzerland", "Syria", "Taiwan", "Tajikistan", "Tanzania", "Thailand", "The Bahamas",
"The Gambia", "Togo", "Tokelau", "Tonga", "Trinidad and Tobago", "Tunisia", "Turkey",
"Turkmenistan", "Turks and Caicos Islands", "Tuvalu", "Virgin Islands", "Uganda",
"Ukraine", "United Arab Emirates", "United Kingdom",
"United States", "United States Minor Outlying Islands", "Uruguay", "Uzbekistan",
"Vanuatu", "Vatican City", "Venezuela", "Vietnam", "Wallis and Futuna", "Western Sahara",
"Yemen", "Yugoslavia", "Zambia", "Zimbabwe"
};
</pre>
<p>This is the array of strings that will be placed into the {@link android.widget.ListView}.</p>
</li>
<li>Run the application.</li>
</ol>
<p>You can scroll the list, or type to filter it, then click an item to see a message. You
should see something like this:</p>
<img src="images/hello-listview.png" width="150px" />
<p>Note that using a hard-coded string array is not the best design practice. One is
used in this tutorial for simplicity, in order to demonstrate the {@link
android.widget.ListView} widget. The better practice is to reference a string array
defined by an external resource, such as with a {@code &lt;string-array&gt;}
resource in your project {@code res/values/strings.xml} file. For example:</p>
<pre>
&lt;?xml version="1.0" encoding="utf-8"?>
&lt;resources>
&lt;string-array name="countries_array">
&lt;item>Bahrain&lt;/item>
&lt;item>Bangladesh&lt;/item>
&lt;item>Barbados&lt;/item>
&lt;item>Belarus&lt;/item>
&lt;item>Belgium&lt;/item>
&lt;item>Belize&lt;/item>
&lt;item>Benin&lt;/item>
&lt;/string-array>
&lt;/resources>
</pre>
<p>To use these resource strings for the {@link android.widget.ArrayAdapter}, replace the original
{@link android.app.ListActivity#setListAdapter(ListAdapter)} line with the following:</p>
<pre>
String[] countries = getResources().getStringArray(R.array.countries_array);
setListAdapter(new ArrayAdapter&lt;String>(this, R.layout.list_item, countries));
</pre>
<h3>References</h3>
<ul>
<li>{@link android.widget.ListView}</li>
<li>{@link android.widget.ListAdapter}</li>
</ul>
@@ -0,0 +1,303 @@
page.title=Google Map View
parent.title=Hello, Views
parent.link=index.html
@jd:body
<p>Using the Google Maps library, you can create your own map-viewing Activity. In this
tutorial, you'll create a simple map application in two parts. In Part 1, you'll create an app that
shows a map the user can pan and zoom. In Part 2, you'll add overlay items that mark
points of interest.</p>
<div class="special">
<p>This tutorial requires that you have the external Google Maps library
installed in your SDK environment. The Maps library is included with the Google APIs
add-on, which you can install using the Android SDK and
AVD Manager. To learn how, see <a href="{@docRoot}sdk/adding-components.html">Adding SDK
Components</a>.</p>
<p>After installing the Google APIs add-on in your SDK, set your project properties to use the build
target called "Google APIs by Google Inc.". See the instructions for setting a build
target in <a href="{@docRoot}guide/developing/eclipse-adt.html">Developing in
Eclipse with ADT</a> or <a
href="{@docRoot}guide/developing/other-ide.html">Developing in Other IDEs</a>,
as appropriate for your environment. </p>
<p>You will also need to set up a new AVD that uses the same Google APIs deployment target. See <a
href="{@docRoot}guide/developing/tools/avd.html">Android Virtual Devices</a> for
more information.</p>
<p>For reference material, see the <a
href="http://code.google.com/android/add-ons/google-apis/reference/index.html">Google Maps
library documentation</a>.</p>
</div>
<h2>Part 1: Creating a Map Activity</h2>
<ol>
<li>Start a new project named <em>HelloGoogleMaps</em>.</li>
<li>Because the Maps library is not a part of the standard Android library, you must
declare it in the Android Manifest. Open the <code>AndroidManifest.xml</code>
file and add the following as a child of the <code>&lt;application></code> element:
<pre>&lt;uses-library android:name="com.google.android.maps" /></pre>
</li>
<li>You also need access to the Internet in order to retrieve map tiles,
so you must also request the {@link android.Manifest.permission#INTERNET} permission.
In the manifest file, add the following as a child of the <code>&lt;manifest></code> element:
<pre>&lt;uses-permission android:name="android.permission.INTERNET" /></pre>
</li>
<li>While you're in the manifest, give the map some more space by getting rid of the title bar
with the "NoTitleBar" theme:
<pre>
&lt;activity android:name=".HelloGoogleMaps" android:label="@string/app_name"
<strong>android:theme="@android:style/Theme.NoTitleBar"</strong>&gt;
</pre>
</li>
<li>Open the <code>res/layout/main.xml</code> file and add a single
{@code com.google.android.maps.MapView} as the root node:
<pre>
&lt;?xml version="1.0" encoding="utf-8"?>
&lt;com.google.android.maps.MapView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/mapview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:clickable="true"
android:apiKey="<em>Your Maps API Key goes here</em>"
/>
</pre>
<p>The <code>android:clickable</code> attribute defines whether you want to allow
user-interaction with the map. If this is "false" then touching the map does nothing.</p>
<p>The <code>android:apiKey</code> attribute holds the Maps API Key for your
application, which proves your application and signer certificate has been registered with the
Maps service. This is required in order to receive the map data, even while you are
developing. Registration to the service is free and it only takes a couple
minutes to register your certificate and get a Maps API Key.</p>
<p>Go now to get a key. For instructions, read
<a href="http://code.google.com/android/add-ons/google-apis/mapkey.html">Obtaining a Maps API
Key</a>. For the purpose of this tutorial, you should <a
href="http://code.google.com/android/add-ons/google-apis/mapkey.html#getdebugfingerprint">register
with the SDK debug certificate</a>, which will only be valid while your application is signed
with the debug key (once you sign with your private key, you will need a new API key).
When you get your key, insert it for the value of <code>android:apiKey</code>.</p>
</li>
<li>Now open the <code>HelloGoogleMaps.java</code> file. For this Activity, extend
{@code MapActivity} (instead of {@code android.app.Activity}):</p>
<pre>public class HelloGoogleMaps extends MapActivity {</pre>
<p>This is a special sub-class of {@link android.app.Activity}, provided by the Maps
library, which provides important map capabilities.</p>
<li>Inside every {@code MapActivity}, the <code>isRouteDisplayed()</code> method is required, so
override this method:
<pre>
&#64;Override
protected boolean isRouteDisplayed() {
return false;
}
</pre>
<p>This method is required for some accounting from the Maps service to see if you're currently
displaying any route information. In this case, you're not, so return false.</p>
</li>
<li>Now add the standard {@link android.app.Activity#onCreate(Bundle) onCreate()} callback method
to the class:
<pre>
&#64;Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
</pre>
<p>This loads the layout file created above. In fact, this is now a workable application that will
display map tiles and allow the user to pan around the map. But there's no ability to zoom.
Fortunately, there's a very simple zoom feature built into the {@code MapView} class, which you can
summon with {@code setBuiltInZoomControls(boolean)}. Do this at the end of the {@link
android.app.Activity#onCreate(Bundle) onCreate()} method:</p>
<pre>
MapView mapView = (MapView) findViewById(R.id.mapview);
mapView.setBuiltInZoomControls(true);
</pre>
</li>
<li>That's all there is to it. Run the application. (Remember, you must have an <a
href="{@docRoot}guide/developing/tools/avd.html">AVD</a> configured to use the Google APIs
target, or be using a development device that includes the Maps library.)</li>
</ol>
<h2>Part 2: Adding Overlay Items</h2>
<p>So, now you have a map, but in many cases you'll also want to create your own map
markers and lay-overs. That's what you'll do now. In order to do so, you must implement the
{@code ItemizedOverlay} class, which can manage a whole set of {@code Overlay} (which are the
individual items placed on the map).</p>
<ol>
<li>Create a new Java class named <code>HelloItemizedOverlay</code> that implements
{@code ItemizedOverlay}.
<p>When using Eclipse, right-click the package name in the Eclipse Package Explorer, and
select <strong>New > Class</strong>. Fill-in
the Name field as <em>HelloItemizedOverlay</em>. For the Superclass, enter
"com.google.android.maps.ItemizedOverlay". Click the checkbox for <em>Constructors from
superclass</em>. Click Finish.</p></li>
<li>First, you need an <code>OverlayItem</code> {@link java.util.ArrayList}, in which you'll put
each of the <code>OverlayItem</code> objects you want on the map. Add this at the top of the
<code>HelloItemizedOverlay</code> class:
<pre>private ArrayList&lt;OverlayItem> mOverlays = new ArrayList&lt;OverlayItem>();</pre>
</li>
<li>Now define the <code>HelloItemizedOverlay</code> constructors. The constructor must
define the default marker for each of the {@code OverlayItem}s. In order for the {@link
android.graphics.drawable.Drawable} to actually get drawn, it must have its bounds defined. Most
commonly, you want the center-point at the bottom of the image to be the point at which it's
attached to the map coordinates. This is handled for you with the {@code boundCenterBottom()}
method. Wrap this around our defaultMarker, so the super constructor call looks like this:
<pre>
public HelloItemizedOverlay(Drawable defaultMarker) {
super(boundCenterBottom(defaultMarker));
}
</pre>
</li>
<li>In order to add new {@code OverlayItem}s to the ArrayList, you need a new method:
<pre>
public void addOverlay(OverlayItem overlay) {
mOverlays.add(overlay);
populate();
}</pre>
<p>Each time you add a new {@code OverlayItem} to the ArrayList, you must call
<code>populate()</code> for the {@code ItemizedOverlay}, which will read each of the
{@code OverlayItem}s and prepare them to be drawn.</p>
</li>
<li>When the <code>populate()</code> method executes, it will call <code>createItem(int)</code> in
the {@code ItemizedOverlay} to retrieve each {@code OverlayItem}. You must override this method to
properly read from the ArrayList and return the {@code OverlayItem} from the position specified
by the given integer. Your override method should look like this:
<pre>
&#64;Override
protected OverlayItem createItem(int i) {
return mOverlays.get(i);
}
</pre>
</li>
<li>You must also override the <code>size()</code> method to return the current number of
items in the ArrayList:
<pre>
&#64;Override
public int size() {
return mOverlays.size();
}
</pre>
</li>
<li>Now set up the ability to handle touch events on the overlay items. First, you're
going to need a reference to the application {@link android.content.Context} as a member of
this class. So add {@code Context mContext} as a class member, then initialize it with a
new class constructor:
<pre>
public HelloItemizedOverlay(Drawable defaultMarker, Context context) {
super(defaultMarker);
mContext = context;
}
</pre>
<p>This passes the {@code defaultMarker} up to the default constructor to bound its coordinates
and then initialize {@code mContext} with the given {@link android.content.Context}.</p>
<p>Then override the {@code onTap(int)} callback method, which will handle the event
when an item is tapped by the user:</p>
<pre>
&#64;Override
protected boolean onTap(int index) {
OverlayItem item = mOverlays.get(index);
AlertDialog.Builder dialog = new AlertDialog.Builder(mContext);
dialog.setTitle(item.getTitle());
dialog.setMessage(item.getSnippet());
dialog.show();
return true;
}
</pre>
<p>This uses the member {@code android.content.Context} to create a new {@link
android.app.AlertDialog.Builder} and uses the tapped {@code OverlayItem}'s title and snippet for
the dialog's title and message text. (You'll see the {@code OverlayItem} title and snippet defined
when you create it below.)</p>
</li>
</ol>
<p>You're now done with the <code>HelloItemizedOverlay</code> class and can start using it
to add items on the map.</p>
<p>Go back to the <code>HelloGoogleMaps</code> class. In the following procedure, you'll create an
{@code OverlayItem} and add it to an instance of the <code>HelloItemizedOverlay</code> class, then
add the <code>HelloItemizedOverlay</code> to the <code>MapView</code> using a {@code GeoPoint}
to define its coordinates on the map.</p>
<img src="images/androidmarker.png" align="right" />
<ol>
<li>First, you need the image for the map overlay. If you don't have one handy, use the Android on
the right. Drag this image (or your own) into the <code>res/drawable/</code> directory of your
project.</li>
<li>At the end of your existing {@code onCreate()} method, instantiate :
<pre>
List&lt;Overlay> mapOverlays = mapView.getOverlays();
Drawable drawable = this.getResources().getDrawable(R.drawable.androidmarker);
HelloItemizedOverlay itemizedoverlay = new HelloItemizedOverlay(drawable);</pre>
<p>All overlay elements on a map are held by the {@code MapView}, so when you want to add some,
you have to get a list from the <code>getOverlays()</code> method. Then instantiate the {@link
android.graphics.drawable.Drawable} used for the map marker, which was saved in the {@code
res/drawable/} directory. The constructor for {@code HelloItemizedOverlay} (your custom {@code
ItemizedOverlay}) takes the Drawable in order to set the default marker for all overlay
items.</p>
</li>
<li>Now create a {@code GeoPoint} that defines the map coordinates for the first overlay item,
and pass it to a new {@code OverlayItem}:
<pre>
GeoPoint point = new GeoPoint(19240000,-99120000);
OverlayItem overlayitem = new OverlayItem(point, "Hola, Mundo!", "I'm in Mexico City!");
</pre>
<p>{@code GeoPoint} coordinates are specified in microdegrees (<code>degrees * 1e6</code>). The
{@code OverlayItem} constructor accepts the {@code GeoPoint} location, a string for the
item's title, and a string for the item's snippet text, respectively.</p>
</li>
<li>All that's left is to add this {@code OverlayItem} to your collection in the
{@code HelloItemizedOverlay} instance, then add the {@code HelloItemizedOverlay} to the MapView:
<pre>
itemizedoverlay.addOverlay(overlayitem);
mapOverlays.add(itemizedoverlay);
</pre>
</li>
<li>Now run the application.</li>
</ol>
<p>You should see the following:</p>
<img src="images/hello-mapview.png" width="150px" />
<p>When you tap the overlay item, you'll see the dialog appear.</p>
<p>Because the {@code ItemizedOverlay} class uses an {@code java.util.ArrayList} for all of the
{@code OverlayItem}s, it's easy to add more. Try adding another one. Before the
<code>addOverlay()</code> method is called, add these lines:</p>
<pre>
GeoPoint point2 = new GeoPoint(35410000, 139460000);
OverlayItem overlayitem2 = new OverlayItem(point2, "Sekai, konichiwa!", "I'm in Japan!");
</pre>
<p>Run the application again. (You probably need to move the map to find the new overlay item.)</p>
@@ -0,0 +1,89 @@
page.title=Relative Layout
parent.title=Hello, Views
parent.link=index.html
@jd:body
<p>{@link android.widget.RelativeLayout} is a {@link android.view.ViewGroup} that displays
child {@link android.view.View} elements in relative positions. The position of a {@link
android.view.View} can be specified as relative to sibling elements (such as to the left-of or below
a given element) or in positions relative to the {@link android.widget.RelativeLayout} area (such as
aligned to the bottom, left of center).</p>
<p>A {@link android.widget.RelativeLayout} is a very powerful utility for designing a user
interface because it can eliminate nested {@link android.view.ViewGroup}s. If you find
yourself using several nested {@link android.widget.LinearLayout} groups, you may be able to
replace them with a single {@link android.widget.RelativeLayout}.</p>
<ol>
<li>Start a new project named <em>HelloRelativeLayout</em>.</li>
<li>Open the <code>res/layout/main.xml</code> file and insert the following:
<pre>
&lt;?xml version="1.0" encoding="utf-8"?>
&lt;RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
&lt;TextView
android:id="@+id/label"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Type here:"/>
&lt;EditText
android:id="@+id/entry"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@android:drawable/editbox_background"
android:layout_below="@id/label"/>
&lt;Button
android:id="@+id/ok"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/entry"
android:layout_alignParentRight="true"
android:layout_marginLeft="10dip"
android:text="OK" />
&lt;Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="@id/ok"
android:layout_alignTop="@id/ok"
android:text="Cancel" />
&lt;/RelativeLayout>
</pre>
<p>Notice each of the <code>android:layout_*</code> attributes, such as <code>layout_below</code>,
<code>layout_alignParentRight</code>, and <code>layout_toLeftOf</code>. When using a {@link
android.widget.RelativeLayout}, you can use these attributes to describe
how you want to position each {@link android.view.View}. Each one of these attributes define a
different kind
of relative position. Some attributes use the resource ID of a sibling {@link android.view.View}
to define its own relative position. For example, the last {@link android.widget.Button} is
defined to lie to the left-of and aligned-with-the-top-of the {@link android.view.View}
identified by the ID <code>ok</code> (which is the previous {@link android.widget.Button}).</p>
<p>All of the available layout attributes are defined in {@link
android.widget.RelativeLayout.LayoutParams}.</p>
</li>
<li>Make sure you load this layout in the
{@link android.app.Activity#onCreate(Bundle) onCreate()} method:</p>
<pre>
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
</pre>
<p>The {@link android.app.Activity#setContentView(int)} method loads the
layout file for the {@link android.app.Activity}, specified by the resource
ID &mdash; <code>R.layout.main</code> refers to the <code>res/layout/main.xml</code> layout
file.</p>
</li>
<li>Run the application.</li>
</ol>
<p>You should see the following layout:</p>
<img src="images/hello-relativelayout.png" width="150px" />
<h3>Resources</h3>
<ul>
<li>{@link android.widget.RelativeLayout}</li>
<li>{@link android.widget.RelativeLayout.LayoutParams}</li>
<li>{@link android.widget.TextView}</li>
<li>{@link android.widget.EditText}</li>
<li>{@link android.widget.Button}</li>
</ul>
@@ -0,0 +1,149 @@
page.title=Spinner
parent.title=Hello, Views
parent.link=index.html
@jd:body
<p>{@link android.widget.Spinner} is a widget similar to a drop-down list for selecting items.</p>
<p>In this tutorial, you'll create
a simple spinner widget that displays a list of planets. When one is selected, a toast message
will display the selected item.</p>
<ol>
<li>Start a new project named <em>HelloSpinner</em>.</li>
<li>Open the <code>res/layout/main.xml</code> file and insert the following:
<pre>
&lt;?xml version="1.0" encoding="utf-8"?>
&lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:padding="10dip"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
&lt;TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dip"
android:text="@string/planet_prompt"
/>
&lt;Spinner
android:id="@+id/spinner"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:prompt="@string/planet_prompt"
/>
&lt;/LinearLayout>
</pre>
<p>Notice that the {@link android.widget.TextView}'s <code>android:text</code> attribute and the
{@link android.widget.Spinner}'s <code>android:prompt</code> attribute both reference the same
string resource. This text behaves as a title for the widget. When applied to the {@link
android.widget.Spinner}, the title text will appear
in the selection dialog that appears upon selecting the widget.</p>
</li>
<li>Create a <code>strings.xml</code> file in <code>res/values/</code> and edit the file to look
like this:
<pre>
&lt;?xml version="1.0" encoding="utf-8"?>
&lt;resources>
&lt;string name="planet_prompt">Choose a planet&lt;/string>
&lt;string-array name="planets_array">
&lt;item>Mercury&lt;/item>
&lt;item>Venus&lt;/item>
&lt;item>Earth&lt;/item>
&lt;item>Mars&lt;/item>
&lt;item>Jupiter&lt;/item>
&lt;item>Saturn&lt;/item>
&lt;item>Uranus&lt;/item>
&lt;item>Neptune&lt;/item>
&lt;/string-array>
&lt;/resources>
</pre>
<p>The {@code &lt;string>} element defines the title string referenced by the {@link
android.widget.TextView} and {@link android.widget.Spinner} in the layout above. The {@code
&lt;string-array} element defines the list of strings that will be displayed as the list in
the {@link android.widget.Spinner} widget.</p>
</li>
<li>Now open the <code>HelloSpinner.java</code> file and insert the following code for the {@link
android.app.Activity#onCreate(Bundle) onCreate()} method:
<pre>
&#64;Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Spinner spinner = (Spinner) findViewById(R.id.spinner);
ArrayAdapter&lt;CharSequence> adapter = ArrayAdapter.createFromResource(
this, R.array.planets_array, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
}
</pre>
<p>After the {@code main.xml} layout is set as the content view, the {@link
android.widget.Spinner} widget is captured from the layout with {@link
android.app.Activity#findViewById(int)}. The {@link
android.widget.ArrayAdapter#createFromResource(Context,int,int) createFromResource()} method then
creates a new {@link android.widget.ArrayAdapter}, which binds each item in the string array
to the initial appearance for the {@link android.widget.Spinner} (which is how each item will
appear in the spinner when selected). The {@code
R.array.planets_array} ID references the {@code string-array} defined above and the
{@code android.R.layout.simple_spinner_item} ID references a layout for the standard spinner
appearance, defined by the platform. Then {@link
android.widget.ArrayAdapter#setDropDownViewResource(int)} is called to define the appearance for
each item when the widget is opened ({@code simple_spinner_dropdown_item} is
another standard layout defined by the platform). Finally, the {@link android.widget.ArrayAdapter}
is set to associate all of its items with the {@link android.widget.Spinner} by calling {@link
android.widget.AdapterView#setAdapter(T)}.</p>
</li>
<li>Now create a nested class that implements {@link
android.widget.AdapterView.OnItemSelectedListener}. This will provide a callback method that will
notify your application when an item has been selected from the {@link
android.widget.Spinner}. Here's what this class should look like:
<pre>
public class MyOnItemSelectedListener implements OnItemSelectedListener {
public void onItemSelected(AdapterView&lt;?> parent,
View view, int pos, long id) {
Toast.makeText(parent.getContext()), "The planet is " +
parent.getItemAtPosition(pos).toString(), Toast.LENGTH_LONG).show();
}
public void onNothingSelected(AdapterView<?> parent) {
// Do nothing.
}
}
</pre>
<p>The {@link android.widget.AdapterView.OnItemSelectedListener} requires the {@link
android.widget.AdapterView.OnItemSelectedListener#onItemSelected(AdapterView,View,int,long)
onItemSelected()} and {@link
android.widget.AdapterView.OnItemSelectedListener#onNothingSelected(AdapterView)
onNothingSelected()} callback methods. The former is called when an item from the {@link
android.widget.AdapterView} is selected, in which case, a short {@link android.widget.Toast}
message displays the selected text; and the latter is called when a selection disappears from the
{@link android.widget.AdapterView}, which doesn't happen in this case, so it's ignored.</p>
</li>
<li>Now the {@code MyOnItemSelectedListener} needs to be applied to the {@link
android.widget.Spinner}. Go back to the {@link android.app.Activity#onCreate(Bundle) onCreate()}
method and add the following line to the end:
<pre>
spinner.setOnItemSelectedListener(new MyOnItemSelectedListener());
</pre>
<p>This creates a new anonymous instance of the {@code MyOnItemSelectedListener} and sets it as the
listener for the {@link android.widget.Spinner}.</p>
</li>
<li>Run the application.</li>
</ol>
<p>It should look like this:</p>
<img src="images/hello-spinner.png" width="150px" />
<h3>Resources</h3>
<ul>
<li>{@link android.R.layout}</li>
<li>{@link android.widget.ArrayAdapter}</li>
<li>{@link android.widget.Spinner}</li>
</ul>
@@ -0,0 +1,124 @@
page.title=Table Layout
parent.title=Hello, Views
parent.link=index.html
@jd:body
<p>{@link android.widget.TableLayout} is a {@link android.view.ViewGroup} that
displays child {@link android.view.View} elements in rows and columns.</p>
<ol>
<li>Start a new project named <em>HelloTableLayout</em>.</li>
<li>Open the <code>res/layout/main.xml</code> file and insert the following:
<pre>
&lt;?xml version="1.0" encoding="utf-8"?>
&lt;TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:stretchColumns="1">
&lt;TableRow>
&lt;TextView
android:layout_column="1"
android:text="Open..."
android:padding="3dip" />
&lt;TextView
android:text="Ctrl-O"
android:gravity="right"
android:padding="3dip" />
&lt;/TableRow>
&lt;TableRow>
&lt;TextView
android:layout_column="1"
android:text="Save..."
android:padding="3dip" />
&lt;TextView
android:text="Ctrl-S"
android:gravity="right"
android:padding="3dip" />
&lt;/TableRow>
&lt;TableRow>
&lt;TextView
android:layout_column="1"
android:text="Save As..."
android:padding="3dip" />
&lt;TextView
android:text="Ctrl-Shift-S"
android:gravity="right"
android:padding="3dip" />
&lt;/TableRow>
&lt;View
android:layout_height="2dip"
android:background="#FF909090" />
&lt;TableRow>
&lt;TextView
android:text="X"
android:padding="3dip" />
&lt;TextView
android:text="Import..."
android:padding="3dip" />
&lt;/TableRow>
&lt;TableRow>
&lt;TextView
android:text="X"
android:padding="3dip" />
&lt;TextView
android:text="Export..."
android:padding="3dip" />
&lt;TextView
android:text="Ctrl-E"
android:gravity="right"
android:padding="3dip" />
&lt;/TableRow>
&lt;View
android:layout_height="2dip"
android:background="#FF909090" />
&lt;TableRow>
&lt;TextView
android:layout_column="1"
android:text="Quit"
android:padding="3dip" />
&lt;/TableRow>
&lt;/TableLayout>
</pre>
<p>Notice how this resembles the structure of an HTML table. The {@link android.widget.TableLayout}
element is like the HTML <code>&lt;table&gt;</code> element; {@link android.widget.TableRow} is like
a <code>>&lt;tr>&gt;</code> element;
but for the cells, you can use any kind of {@link android.view.View} element. In this example, a
{@link android.widget.TextView} is used for each cell. In between some of the rows, there is also a
basic {@link android.view.View}, which is used to draw a horizontal line.</p>
</li>
<li>Make sure your <em>HelloTableLayout</em> Activity loads this layout in the
{@link android.app.Activity#onCreate(Bundle) onCreate()} method:
<pre>
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
</pre>
<p>The {@link android.app.Activity#setContentView(int)} method loads the
layout file for the {@link android.app.Activity}, specified by the resource
ID &mdash; <code>R.layout.main</code> refers to the <code>res/layout/main.xml</code> layout
file.</p>
</li>
<li>Run the application.</li>
</ol>
<p>You should see the following:</p>
<img src="images/hello-tablelayout.png" width="150px" />
<h3>References</h3>
<ul>
<li>{@link android.widget.TableLayout}</li>
<li>{@link android.widget.TableRow}</li>
<li>{@link android.widget.TextView}</li>
</ul>
@@ -0,0 +1,210 @@
page.title=Tab Layout
parent.title=Hello, Views
parent.link=index.html
@jd:body
<p>To create a tabbed UI, you need to use a {@link android.widget.TabHost} and a {@link
android.widget.TabWidget}. The {@link android.widget.TabHost} must be the root node for the layout,
which contains both the {@link android.widget.TabWidget} for displaying the tabs and a {@link
android.widget.FrameLayout} for displaying the tab content.</p>
<p>You can implement your tab content in one of two ways: use the tabs to swap
{@link android.view.View}s within the same {@link android.app.Activity}, or use the tabs to change
between entirely separate activities. Which method you want for your application will depend on your
demands, but if each tab provides a distinct user activity, then it probably makes sense to use
a separate {@link android.app.Activity} for each tab, so that you can better manage the application
in discrete groups, rather than one massive application and layout.</p>
<p>In this tutorial, you'll create a tabbed UI that uses a separate {@link
android.app.Activity} for each tab.</p>
<ol>
<li>Start a new project named <em>HelloTabWidget</em>.</li>
<li>First, create three separate {@link android.app.Activity} classes in your project:
<code>ArtistsActivity</code>, <code>AlbumsActivity</code>, and <code>SongsActivity</code>. These
will each represent a separate tab. For now, make each one display a simple message using a {@link
android.widget.TextView}. For example:
<pre>
public class ArtistsActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
TextView textview = new TextView(this);
textview.setText("This is the Artists tab");
setContentView(textview);
}
}
</pre>
<p>Notice that this doesn't use a layout file. Just create a {@link
android.widget.TextView}, give it some text and set that as the content. Duplicate this for
each of the three activities, and add the corresponding <code>&lt;activity/&gt;</code> tags to the Android Manifest file.</p>
<li>You need an icon for each of your tabs. For each icon, you should create two versions: one
for when the tab is selected and one for when it is unselected. The
general design recommendation is for the selected icon to be a dark color (grey), and the
unselected icon to be a light color (white). (See the <a
href="{@docRoot}guide/practices/ui_guidelines/icon_design.html#tabstructure">Icon Design
Guidelines</a>.) For example:
<p>
<img src="images/ic_tab_artists_white.png" title="unselected tab icon" alt="" />
<img src="images/ic_tab_artists_grey.png" title="selected tab icon" alt="" />
</p>
<p>For this tutorial, you can copy these images and use them for all three tabs. (When you
create tabs in your own application, you should create customized tab icons.)</p>
<p>Now create a <a
href="{@docRoot}guide/topics/resources/drawable-resource.html#StateList">state-list drawable</a>
that specifies which image to use for each tab state:</p>
<ol>
<li>Save the icon images in your project <code>res/drawable/</code> directory.</li>
<li>Create a new XML file in <code>res/drawable/</code>
named <code>ic_tab_artists.xml</code> and insert the following:
<pre>
&lt;?xml version="1.0" encoding="utf-8"?>
&lt;selector xmlns:android="http://schemas.android.com/apk/res/android">
&lt;!-- When selected, use grey -->
&lt;item android:drawable="@drawable/ic_tab_artists_grey"
android:state_selected="true" />
&lt;!-- When not selected, use white-->
&lt;item android:drawable="@drawable/ic_tab_artists_white" />
&lt;/selector>
</pre>
<p>This is a <a
href="{@docRoot}guide/topics/resources/drawable-resource.html#StateList">state-list drawable</a>,
which you will apply as the tab image. When the tab state changes, the tab icon will
automatically switch between the images defined here.</p>
</li>
</ol>
</li>
<li>Open the <code>res/layout/main.xml</code> file and insert the following:
<pre>
&lt;?xml version="1.0" encoding="utf-8"?>
&lt;TabHost xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/tabhost"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
&lt;LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="5dp">
&lt;TabWidget
android:id="@android:id/tabs"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
&lt;FrameLayout
android:id="@android:id/tabcontent"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="5dp" />
&lt;/LinearLayout>
&lt;/TabHost>
</pre>
<p>This is the layout that will display the tabs and provide navigation between each {@link
android.app.Activity} created above.</p>
<p>The {@link android.widget.TabHost} requires that a {@link android.widget.TabWidget} and a
{@link android.widget.FrameLayout} both live somewhere within it. To position the {@link
android.widget.TabWidget} and {@link android.widget.FrameLayout} vertically, a {@link
android.widget.LinearLayout} is used. The {@link android.widget.FrameLayout} is where the content
for each tab goes, which is empty now because the {@link android.widget.TabHost} will automatically
embed each {@link android.app.Activity} within it.</p>
<p>Notice that the {@link android.widget.TabWidget} and the {@link android.widget.FrameLayout}
elements have the IDs {@code tabs} and {@code tabcontent}, respectively. These names
must be used so that the {@link android.widget.TabHost} can retrieve references to each of
them. It expects exactly these names.</p>
</li>
<li>Now open <code>HelloTabWidget.java</code> and make it extend {@link
android.app.TabActivity}:</p>
<pre>
public class HelloTabWidget extends TabActivity {
</pre>
</li>
<li>Use the following code for the {@link android.app.Activity#onCreate(Bundle) onCreate()}
method:
<pre>
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Resources res = getResources(); // Resource object to get Drawables
TabHost tabHost = getTabHost(); // The activity TabHost
TabHost.TabSpec spec; // Resusable TabSpec for each tab
Intent intent; // Reusable Intent for each tab
// Create an Intent to launch an Activity for the tab (to be reused)
intent = new Intent().setClass(this, ArtistsActivity.class);
// Initialize a TabSpec for each tab and add it to the TabHost
spec = tabHost.newTabSpec("artists").setIndicator("Artists",
res.getDrawable(R.drawable.ic_tab_artists))
.setContent(intent);
tabHost.addTab(spec);
// Do the same for the other tabs
intent = new Intent().setClass(this, AlbumsActivity.class);
spec = tabHost.newTabSpec("albums").setIndicator("Albums",
res.getDrawable(R.drawable.ic_tab_albums))
.setContent(intent);
tabHost.addTab(spec);
intent = new Intent().setClass(this, SongsActivity.class);
spec = tabHost.newTabSpec("songs").setIndicator("Songs",
res.getDrawable(R.drawable.ic_tab_songs))
.setContent(intent);
tabHost.addTab(spec);
tabHost.setCurrentTab(2);
}
</pre>
<p>This sets up each tab with their text and icon, and assigns each one an {@link
android.app.Activity}.</p>
<p>A reference to the {@link android.widget.TabHost} is first captured with {@link
android.app.TabActivity#getTabHost()}. Then, for
each tab, a {@link android.widget.TabHost.TabSpec} is created to define the tab properties. The
{@link android.widget.TabHost#newTabSpec(String)} method creates a new {@link
android.widget.TabHost.TabSpec} identified by the given string tag. For each
{@link android.widget.TabHost.TabSpec}, {@link
android.widget.TabHost.TabSpec#setIndicator(CharSequence,Drawable)} is called to set the text and
icon for the tab, and {@link android.widget.TabHost.TabSpec#setContent(Intent)} is called to specify
the {@link android.content.Intent} to open the appropriate {@link android.app.Activity}. Each
{@link android.widget.TabHost.TabSpec} is then added to the {@link android.widget.TabHost} by
calling {@link android.widget.TabHost#addTab(TabHost.TabSpec)}.</p>
<p>At the very end, {@link
android.widget.TabHost#setCurrentTab(int)} opens the tab to be displayed by default, specified
by the index position of the tab.</p>
<p>Notice that not once was the {@link android.widget.TabWidget} object referenced. This is
because a {@link android.widget.TabWidget} must always be a child of a {@link
android.widget.TabHost}, which is what you use for almost all interaction with the tabs. So when
a tab is added to the {@link android.widget.TabHost}, it's automatically added to the child
{@link android.widget.TabWidget}.</p>
</li>
<li>Now open the Android Manifest file and add the <code>NoTitleBar</code> theme to the
<em>HelloTabWidget</em>'s
<code>&lt;activity></code> tag. This will remove the default application title from the top
of the layout, leaving more space for the tabs, which effectively operate as their own titles.
The <code>&lt;activity></code> tag should look like this:
<pre>
&lt;activity android:name=".HelloTabWidget" android:label="@string/app_name"
android:theme="&#64;android:style/Theme.NoTitleBar">
</pre>
</li>
<li>Run the application.</li>
</ol>
<p>Your application should look like this (though your icons may be different):</p>
<img src="images/hello-tabwidget.png" width="150px" />
<h3>References</h3>
<ul>
<li>{@link android.widget.TabWidget}</li>
<li>{@link android.widget.TabHost}</li>
<li>{@link android.widget.TabHost.TabSpec}</li>
<li>{@link android.widget.FrameLayout}</li>
</ul>
@@ -0,0 +1,167 @@
page.title=Time Picker
parent.title=Hello, Views
parent.link=index.html
@jd:body
<p>To provide a widget for selecting a time, use the {@link android.widget.TimePicker}
widget, which allows the user to select the hour and minute in a familiar interface.</p>
<p>In this tutorial, you'll create a {@link android.app.TimePickerDialog}, which presents the
time picker in a floating dialog box at the press of a button. When the time is set by
the user, a {@link android.widget.TextView} will update with the new date.</p>
<ol>
<li>Start a new project named <em>HelloTimePicker</em>.</li>
<li>Open the <code>res/layout/main.xml</code> file and insert the following:
<pre>
&lt;?xml version="1.0" encoding="utf-8"?>
&lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
&lt;TextView android:id="@+id/timeDisplay"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=""/>
&lt;Button android:id="@+id/pickTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Change the time"/>
&lt;/LinearLayout>
</pre>
<p>This is a basic {@link android.widget.LinearLayout} with a {@link android.widget.TextView}
that will display the time and a {@link android.widget.Button} that will open the {@link
android.app.TimePickerDialog}.</p>
</li>
<li>Open <code>HelloTimePicker.java</code> and insert the following class members:
<pre>
private TextView mTimeDisplay;
private Button mPickTime;
private int mHour;
private int mMinute;
static final int TIME_DIALOG_ID = 0;
</pre>
<p>This declares variables for the layout elements and time fields.
The <code>TIME_DIALOG_ID</code> is a static integer that uniquely identifies the dialog.</p>
</li>
<li>Now insert the following code for the {@link android.app.Activity#onCreate(Bundle) onCreate()}
method:
<pre>
&#64;Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// capture our View elements
mTimeDisplay = (TextView) findViewById(R.id.timeDisplay);
mPickTime = (Button) findViewById(R.id.pickTime);
// add a click listener to the button
mPickTime.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
showDialog(TIME_DIALOG_ID);
}
});
// get the current time
final Calendar c = Calendar.getInstance();
mHour = c.get(Calendar.HOUR_OF_DAY);
mMinute = c.get(Calendar.MINUTE);
// display the current date
updateDisplay();
}
</pre>
<p>First, the content is set to the <code>main.xml</code> layout and then the {@link
android.widget.TextView} and {@link android.widget.Button} are captured with {@link
android.app.Activity#findViewById(int)}.
Then an {@link android.view.View.OnClickListener} is created for the {@link android.widget.Button},
so that when clicked, it will call {@link
android.app.Activity#showDialog(int)}, passing the unique integer ID for the time picker
dialog. Using {@link android.app.Activity#showDialog(int)} allows the {@link
android.app.Activity} to manage the life-cycle of the dialog and will call the {@link
android.app.Activity#onCreateDialog(int)} callback method to request the {@link android.app.Dialog}
that should be displayed (which you'll define later). After the on-click listener is set, a new
{@link java.util.Calendar} is created to get the current hour and minute. Finally, the
private <code>updateDisplay()</code> method is called in order to fill the {@link
android.widget.TextView} with the current time.</p>
</li>
<li>Add the <code>updateDisplay()</code> and <code>pad()</code> methods:
<pre>
// updates the time we display in the TextView
private void updateDisplay() {
mTimeDisplay.setText(
new StringBuilder()
.append(pad(mHour)).append(":")
.append(pad(mMinute)));
}
private static String pad(int c) {
if (c >= 10)
return String.valueOf(c);
else
return "0" + String.valueOf(c);
}
</pre>
<p>The <code>updateDisplay()</code> method uses the member fields for the time and inserts them in
the <code>mTimeDisplay</code> {@link android.widget.TextView}. The <code>pad()</code> method returns
the appropriate string representation of the hour or minute&mdash;it will prefix a zero to the
number if it's a single digit.</p>
</li>
<li>Add a class member for a {@link android.app.TimePickerDialog.OnTimeSetListener} that will be
called when the user sets a new time:
<pre>
// the callback received when the user "sets" the time in the dialog
private TimePickerDialog.OnTimeSetListener mTimeSetListener =
new TimePickerDialog.OnTimeSetListener() {
public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
mHour = hourOfDay;
mMinute = minute;
updateDisplay();
}
};
</pre>
<p>When the user is done setting the time (clicks the "Set" button), the
<code>onTimeSet()</code> method is called and it updates the member fields with
the new time and updates the layout's {@link android.widget.TextView}.</p>
</li>
<li>Add the {@link android.app.Activity#onCreateDialog(int)} callback method:
<pre>
&#64;Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case TIME_DIALOG_ID:
return new TimePickerDialog(this,
mTimeSetListener, mHour, mMinute, false);
}
return null;
}
</pre>
<p>This is an {@link android.app.Activity} callback that is passed the identifier you passed to
{@link android.app.Activity#showDialog(int)}, in the {@link android.widget.Button}'s on-click
listener. When the ID matches, this initializes the {@link android.app.TimePickerDialog} with the
member variables initialized at the end of <code>onCreate()</code> and the {@link
android.app.TimePickerDialog.OnTimeSetListener} created in the previous step.</p>
</li>
<li>Run the application.</li>
</ol>
<p>When you press the "Change the time" button, you should see the following:</p>
<img src="images/hello-timepicker.png" width="150px" />
<h3>References</h3>
<ol>
<li>{@link android.widget.TimePicker}</li>
<li>{@link android.app.TimePickerDialog.OnTimeSetListener}</li>
<li>{@link android.widget.Button}</li>
<li>{@link android.widget.TextView}</li>
<li>{@link java.util.Calendar}</li>
</ol>
@@ -0,0 +1,131 @@
page.title=Web View
parent.title=Hello, Views
parent.link=index.html
@jd:body
<p>{@link android.webkit.WebView} allows you to create your own window for viewing web pages (or even
develop a complete browser). In this tutorial, you'll create a simple {@link android.app.Activity}
that can view and navigate web pages.</p>
<ol>
<li>Create a new project named <em>HelloWebView</em>.</li>
<li>Open the <code>res/layout/main.xml</code> file and insert the following:
<pre>
&lt;?xml version="1.0" encoding="utf-8"?>
&lt;WebView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/webview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
</pre>
</li>
<li>Now open the <code>HelloWebView.java</code> file.
At the top of the class, declare a {@link android.webkit.WebView} object:
<pre>WebView mWebView;</pre>
<p>Then use the following code for the {@link android.app.Activity#onCreate(Bundle) onCreate()}
method:</p>
<pre>
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mWebView = (WebView) findViewById(R.id.webview);
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.loadUrl("http://www.google.com");
}
</pre>
<p>This initializes the member {@link android.webkit.WebView} with the one from the
{@link android.app.Activity} layout; requests a {@link android.webkit.WebSettings} object with
{@link android.webkit.WebView#getSettings()}; and enables JavaScript for the {@link
android.webkit.WebView} with {@link android.webkit.WebSettings#setJavaScriptEnabled(boolean)}.
Finally, an initial web page is loaded with {@link
android.webkit.WebView#loadUrl(String)}.</p>
</li>
<li>Because this application needs access to the Internet, you need to add the appropriate
permissions to the Android manifest file. Open the <code>AndroidManifest.xml</code> file
and add the following as a child of the <code>&lt;manifest></code> element:
<pre>&lt;uses-permission android:name="android.permission.INTERNET" /></pre></li>
<li>While you're in the manifest, give some more space for web pages by removing the title
bar, with the "NoTitleBar" theme:
<pre>
&lt;activity android:name=".HelloGoogleMaps" android:label="@string/app_name"
<strong>android:theme="@android:style/Theme.NoTitleBar"</strong>&gt;
</pre>
</li>
<li>Now run the application.
<p>You now have a simplest web page viewer.
It's not quite a browser yet because as soon as you click a link, the default Android Browser
handles the Intent to view a web page, because this {@link android.app.Activity} isn't
technically enabled to do so. Instead of adding an intent filter to view web pages, you can
override the {@link android.webkit.WebViewClient} class and enable this {@link
android.app.Activity} to handle its own URL requests.</p>
</li>
<li>In the <code>HelloAndroid</code> Activity, add this nested class:
<pre>
private class HelloWebViewClient extends WebViewClient {
&#64;Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
}
</pre>
</li>
<li>Then towards the end of the {@link android.app.Activity#onCreate(Bundle)} method, set an
instance of the <code>HelloWebViewClient</code> as the {@link android.webkit.WebViewClient}:
<pre>mWebView.setWebViewClient(new HelloWebViewClient());</pre>
<p>This line can go anywhere following the initialization of the {@link
android.webkit.WebView} object.</p>
<p>This creates a {@link android.webkit.WebViewClient} that will load any URL selected from this
{@link android.webkit.WebView} into the same {@link android.webkit.WebView}. The
{@link android.webkit.WebViewClient#shouldOverrideUrlLoading(WebView,String)} method is passed
the current {@link android.webkit.WebView} and the URL requested, so all it needs to do is load
the URL in the given view. Returning <code>true</code> says that the method has handled the URL
and the event should not propagate (in which case, an Intent would be created that's handled by
the Browser application).</p>
<p>If you run the application again, new pages will now load in this Activity.
However, you can't navigate back to previous pages. To do this, you need to handle the BACK
button on the device, so that it will return to the previous page, rather than exit the
application.</p>
</li>
<li>To handle the BACK button key press, add the following method inside the
<code>HelloWebView</code> Activity:
<pre>
&#64;Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK) &amp;&amp; mWebView.canGoBack()) {
mWebView.goBack();
return true;
}
return super.onKeyDown(keyCode, event);
}
</pre>
<p>This {@link android.app.Activity#onKeyDown(int,KeyEvent)} callback method will be called
anytime a button is pressed while in the Activity. The condition inside uses the {@link
android.view.KeyEvent} to check whether the key pressed is the BACK button and whether the
{@link android.webkit.WebView} is actually capable of navigating back (if it has a history). If
both are true, then the {@link android.webkit.WebView#goBack()} method is called,
which will navigate back one step in the {@link android.webkit.WebView}
history.Returning <code>true</code> indicates that the event has been handled. If this condition
is not met, then the event is sent back to the system.</p>
</li>
<li>Run the application again. You'll now be able to follow links and navigate back through the
page history.</li>
</ol>
<p>When you open the application, it should look like this:</p>
<img src="images/hello-webview.png" width="150px" />
<h3>Resource</h3>
<ul>
<li>{@link android.webkit.WebView}</li>
<li>{@link android.webkit.WebViewClient}</li>
<li>{@link android.view.KeyEvent}</li>
</ul>
Binary file not shown.

After

Width:  |  Height:  |  Size: 693 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 702 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 791 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -0,0 +1,119 @@
page.title=Hello, Views
@jd:body
<style>
.view {float:left; margin:10px; font-size:120%; font-weight:bold;}
#jd-content .view img {border:1px solid black; margin:8px 0 0 0; padding:5px;}
</style>
<p>This is a collection of "Hello World"-style tutorials designed
to get you started quickly with common Android layouts and widgets.</p>
<p>A certain amount of knowledge is assumed for these tutorials. Before you start,
you should have completed the <a href="{@docRoot}resources/tutorials/hello-world.html">Hello,
World</a> tutorial&mdash;it will teach you several things about basic
Android development. More specifically, you should know:</p>
<ul>
<li>How to create an Android project and run it</li>
<li>The basic structure of an Android project (resource files, layout files, etc.)</li>
<li>The basic components of an {@link android.app.Activity}</li>
</ul>
<p class="note"><strong>Note:</strong> In order to make these tutorials as simple as possible,
some code may not conform to best practices for coding Android applications. In particular,
hard-coded strings are used in places, when the better practice is to reference strings from a
<code>res/values/strings.xml</code> resource file.</p>
<p class="note"><strong>Tip:</strong> After you have pasted sample code into an Eclipse project,
press <strong>Ctrl (or Cmd) + Shift + O</strong> to import the required packages.</p>
<h2>Layouts</h2>
<div class="view">
<a href="hello-linearlayout.html">Linear Layout</a><br/>
<a href="hello-linearlayout.html"><img src="images/hello-linearlayout.png" height="285" width="200"
/></a>
</div>
<div class="view">
<a href="hello-relativelayout.html">Relative Layout</a><br/>
<a href="hello-relativelayout.html"><img src="images/hello-relativelayout.png" height="285"
width="200" /></a>
</div>
<div class="view">
<a href="hello-tablelayout.html">Table Layout</a><br/>
<a href="hello-tablelayout.html"><img src="images/hello-tablelayout.png" height="285" width="200"
/></a>
</div>
<div class="view">
<a href="hello-gridview.html">Grid View</a><br/>
<a href="hello-gridview.html"><img src="images/hello-gridview.png" height="285" width="200" /></a>
</div>
<div class="view">
<a href="hello-tabwidget.html">Tab Layout</a><br/>
<a href="hello-tabwidget.html"><img src="images/hello-tabwidget.png" height="285" width="200" /></a>
</div>
<div class="view">
<a href="hello-listview.html">List View</a><br/>
<a href="hello-listview.html"><img src="images/hello-listview.png" height="285" width="200" /></a>
</div>
<p style="clear:left">&nbsp;</p>
<h2>Widgets &amp; Other Views</h2>
<div class="view">
<a href="hello-datepicker.html">Date Picker</a><br/>
<a href="hello-datepicker.html"><img src="images/hello-datepicker.png" height="285" width="200"
/></a>
</div>
<div class="view">
<a href="hello-timepicker.html">Time Picker</a><br/>
<a href="hello-timepicker.html"><img src="images/hello-timepicker.png" height="285" width="200"
/></a>
</div>
<div class="view">
<a href="hello-formstuff.html">Form Stuff</a><br/>
<a href="hello-formstuff.html"><img src="images/hello-formstuff.png" height="285" width="200" /></a>
</div>
<div class="view">
<a href="hello-spinner.html">Spinner</a><br/>
<a href="hello-spinner.html"><img src="images/hello-spinner.png" height="285" width="200" /></a>
</div>
<div class="view">
<a href="hello-autocomplete.html">Auto Complete</a><br/>
<a href="hello-autocomplete.html"><img src="images/hello-autocomplete.png" height="285" width="200"
/></a>
</div>
<div class="view">
<a href="hello-gallery.html">Gallery</a><br/>
<a href="hello-gallery.html"><img src="images/hello-gallery.png" height="285" width="200" /></a>
</div>
<div class="view">
<a href="hello-mapview.html">Google Map View</a><br/>
<a href="hello-mapview.html"><img src="images/hello-mapview.png" height="285" width="200" /></a>
</div>
<div class="view">
<a href="hello-webview.html">Web View</a><br/>
<a href="hello-webview.html"><img src="images/hello-webview.png" height="285" width="200" /></a>
</div>
<p class="note" style="clear:left">
There are plenty more layouts and widgets available. See the {@link android.view.View} class
for more on View layouts, and the {@link android.widget widget package}
for more useful widgets. And for more raw code samples, visit the
<a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/view/index.html">Api
Demos</a>.</p>