docs: Removing obsolete Notepad tutorial.
bug: 18940278, 1426263, 1426251 Change-Id: Id47439071454f333129465d88eee5ab45fecce5d
This commit is contained in:
Binary file not shown.
@@ -1,136 +0,0 @@
|
||||
excludeFromSuggestions=true
|
||||
page.title=Notepad Tutorial
|
||||
parent.title=Tutorials
|
||||
@jd:body
|
||||
|
||||
|
||||
<p>This tutorial on writing a notepad application gives you a "hands-on" 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><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.</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/components/fundamentals.html">Application
|
||||
Fundamentals</a> before continuing. </p>
|
||||
|
||||
<p>This tutorial also builds on the introductory information provided in the
|
||||
<a href="{@docRoot}training/basics/firstapp/index.html">Building Your First App</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}training/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}training/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}training/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}training/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>This tutorial draws from the full Notepad application included in the
|
||||
<code>samples/legacy/NotePad</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>
|
||||
@@ -1,583 +0,0 @@
|
||||
excludeFromSuggestions=true
|
||||
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}training/basics/firstapp/index.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}training/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—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 — 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><?xml version="1.0" encoding="utf-8"?></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>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ListView android:id="@android:id/list"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
<TextView android:id="@android:id/empty"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/no_notes"/>
|
||||
|
||||
</LinearLayout>
|
||||
</pre>
|
||||
<ul>
|
||||
<li>
|
||||
The <strong>@</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">
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView android:id="@+id/text1"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/></pre>
|
||||
<p>
|
||||
This is the View that will be used for each notes title row — 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 — 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 — we haven't defined this method yet. </li>
|
||||
</ol>
|
||||
<p>
|
||||
<code>onCreate()</code> should now look like this:</p>
|
||||
<pre>
|
||||
@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><string name="menu_insert">Add Item</string></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>
|
||||
@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 — 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>
|
||||
@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) — 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 = "Note " + mNoteNumber++;
|
||||
mDbHelper.createNote(noteName, "");
|
||||
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> >
|
||||
<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>
|
||||
|
||||
@@ -1,645 +0,0 @@
|
||||
excludeFromSuggestions=true
|
||||
page.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> > <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> — 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 void onCreateContextMenu(ContextMenu 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 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 — 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 — 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> — 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> — 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> — 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">XML Layouts</a> is an essential part of
|
||||
creating
|
||||
a good looking Android application.</p>
|
||||
<p>The <a href="https://github.com/android/platform_development/tree/master/samples/ApiDemos"
|
||||
class="external-link">ApiDemos sample project</a> is a good resource for
|
||||
learning 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> > <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> > <strong>Override/Implement Methods...</strong></li>
|
||||
<li>Scroll down through the checklist in the dialog until you see
|
||||
<code>onCreate(Bundle)</code> — 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;
|
||||
|
||||
@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><activity android:name=".NoteEdit" /></code><br><br>
|
||||
This should be placed just below the line that reads:<br>
|
||||
<code></activity></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>
|
||||
|
||||
|
||||
@@ -1,367 +0,0 @@
|
||||
excludeFromSuggestions=true
|
||||
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> >
|
||||
<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 — 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> 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>
|
||||
mDbHelper = new NotesDbAdapter(this);<br>
|
||||
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>
|
||||
Activities have a <a
|
||||
href="{@docRoot}guide/components/activities.html#Lifecycle">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>
|
||||
@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>
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
saveState();
|
||||
}</pre>
|
||||
</li>
|
||||
<li><code>
|
||||
onResume()</code>:
|
||||
<pre>
|
||||
@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>
|
||||
@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 -> 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>
|
||||
@@ -1,71 +0,0 @@
|
||||
excludeFromSuggestions=true
|
||||
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 -> 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}tools/help/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>
|
||||
|
||||
@@ -1,144 +0,0 @@
|
||||
excludeFromSuggestions=true
|
||||
page.title=Notepad Tutorial
|
||||
@jd:body
|
||||
|
||||
|
||||
<p>The tutorial in this section gives you a "hands-on" 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>
|
||||
Reference in New Issue
Block a user