diff options
Diffstat (limited to 'docs/html/guide/tutorials/notepad')
-rwxr-xr-x | docs/html/guide/tutorials/notepad/codelab/NotepadCodeLab.zip | bin | 0 -> 362176 bytes | |||
-rw-r--r-- | docs/html/guide/tutorials/notepad/notepad-ex1.jd | 588 | ||||
-rw-r--r-- | docs/html/guide/tutorials/notepad/notepad-ex2.jd | 640 | ||||
-rw-r--r-- | docs/html/guide/tutorials/notepad/notepad-ex3.jd | 358 | ||||
-rw-r--r-- | docs/html/guide/tutorials/notepad/notepad-extra-credit.jd | 70 | ||||
-rw-r--r-- | docs/html/guide/tutorials/notepad/notepad-index.jd | 143 |
6 files changed, 1799 insertions, 0 deletions
diff --git a/docs/html/guide/tutorials/notepad/codelab/NotepadCodeLab.zip b/docs/html/guide/tutorials/notepad/codelab/NotepadCodeLab.zip Binary files differnew file mode 100755 index 000000000000..86f5e9dc5e6b --- /dev/null +++ b/docs/html/guide/tutorials/notepad/codelab/NotepadCodeLab.zip diff --git a/docs/html/guide/tutorials/notepad/notepad-ex1.jd b/docs/html/guide/tutorials/notepad/notepad-ex1.jd new file mode 100644 index 000000000000..715267fb0a45 --- /dev/null +++ b/docs/html/guide/tutorials/notepad/notepad-ex1.jd @@ -0,0 +1,588 @@ +page.title=Notepad Exercise 1 +@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="tutorial-ex1.html" style="color:#BBB;">Exercise 1</a>]</span> + [<a href="tutorial-ex2.html">Exercise 2</a>] + [<a href="tutorial-ex3.html">Exercise 3</a>] + [<a href="tutorial-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}intro/hello-android.html">Hello + Android tutorial.</a></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="/android/intro/tutorial.html#preparing">setup</a>). Select + <code>Notepadv1</code> and click <strong>Choose</strong>.</li> + <li> + You should see <code>Notepadv1</code> in the <em>Project name</em> and also see the <em>Location</em> + filled in with the path you selected.</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" style="border:2px solid #FFFFDD;float:right; + background-color:#FFFFEE;margin-right:0px; + margin-bottom:.5em;margin-top:1em;padding:0em;width:240px;"> + <h2 style="border:0;font-size:12px;padding:.5em .5em .5em 1em;margin:0; + background-color:#FFFFDD;">Accessing and modifying data</h2> + <p style="padding-left:.5em;font-size:12px;margin:0; padding:.0em .5em .5em 1em;">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 style="padding-left:.5em;font-size:12px;margin:0; + padding:.0em .5em .5em 1em;">If you are interested, you can find out more about + <a href="{@docRoot}devel/data/contentproviders.html">content providers</a> or the whole + subject of <a href="{@docRoot}devel/data.html">Storing, Retrieving, and Exposing Data</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> + + <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" style="border:2px solid #FFFFDD;float:right; + background-color:#FFFFEE;margin-right:0px; + margin-bottom:.5em;margin-top:1em;padding:0em;width:240px;"> + <h2 style="border:0;font-size:12px;padding:.5em .5em .5em 1em;margin:0; + background-color:#FFFFDD;">Layouts and activities</h2> + <p style="padding-left:.5em;font-size:12px;margin:0; + padding:.0em .5em .5em 1em;">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 style="padding-left:.5em;font-size:12px;margin:0; + padding:.0em .5em .5em 1em;">Full screen layouts are not the only option for an Activity however. You + might also want to use a <a +href="{@docRoot}kb/commontasks.html#floatingorfull">floating + layout</a> (for example, a <a +href="{@docRoot}kb/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> + + <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" style="border:2px solid #FFFFDD;float:right; + background-color:#FFFFEE;margin-right:0px; + margin-bottom:.5em;margin-top:1em;padding:0em;width:240px;"> + <h2 style="border:0;font-size:12px;padding:.5em .5em .5em 1em;margin:0; + background-color:#FFFFDD;">Resources and the R class</h2> + <p style="padding-left:.5em;font-size:12px;margin:0; + padding:.0em .5em .5em 1em;">The folders under res/ in the Eclipse project are for resources. + There is a <a href="{@docRoot}kb/commontasks.html#filelist">specific structure</a> to the + folders and files under res/.</p> + <p style="padding-left:.5em;font-size:12px; +margin:0; padding:.0em .5em .5em 1em;">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> + <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()</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" style="border:2px solid #FFFFDD;float:right; + background-color:#FFFFEE;margin-right:0px; + margin-bottom:.5em;margin-top:1em;padding:0em;width:240px;"> + <h2 style="border:0;font-size:12px;padding:.5em .5em .5em 1em;margin:0; + background-color:#FFFFDD;">More on menus</h2> + <p style="padding-left:.5em;font-size:12px;margin:0; + padding:.0em .5em .5em 1em;">The notepad application we are constructing only scratches the + surface with <a href="{@docRoot}kb/commontasks.html#addmenuitems">menus</a>. </p> + <p style="padding-left:.5em;font-size:12px;margin:0; + padding:.0em .5em .5em 1em;">You can also <a href="{@docRoot}kb/commontasks.html#menukeyshortcuts">add +shortcut keys for menu items</a>, <a href="{@docRoot}kb/commontasks.html#menukeyshortcuts">create +submenus</a> and even <a href="{@docRoot}kb/commontasks.html#addingtoothermenus">add +menu items to other applications!</a>. </p> + </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" style="border:2px solid #FFFFDD;float:right; + background-color:#FFFFEE;margin-right:0px; + margin-bottom:.5em;margin-top:1em;padding:0em;width:240px;"> + <h2 style="border:0;font-size:12px;padding:.5em .5em .5em 1em;margin:0; + background-color:#FFFFDD;">List adapters</h2> + <p style="padding-left:.5em;font-size:12px;margin:0; + padding:.0em .5em .5em 1em;">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> + + <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="tutorial-ex2.html">Tutorial +Exercise 2</a> to add the ability to create, edit and delete notes.</p> +<p><a href="tutorial.html">Back to the Tutorial main page...</a></p> + diff --git a/docs/html/guide/tutorials/notepad/notepad-ex2.jd b/docs/html/guide/tutorials/notepad/notepad-ex2.jd new file mode 100644 index 000000000000..ce7681b2cc57 --- /dev/null +++ b/docs/html/guide/tutorials/notepad/notepad-ex2.jd @@ -0,0 +1,640 @@ +page.title=Notepad Exercise 2 +@jd:body + + +<p><em>In this exercise, you will add a second Activity to your notepad application, to let the user +create, edit, and delete notes. 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> +</ul> + +<div style="float:right;white-space:nowrap"> + [<a href="tutorial-ex1.html">Exercise 1</a>] + <span style="color:#BBB;"> + [<a href="tutorial-ex2.html" style="color:#DDD;">Exercise 2</a>] + </span> + [<a href="tutorial-ex3.html">Exercise 3</a>] + [<a href="tutorial-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>onListItemClick()</code> and <code>onActivityResult()</code>) + which we will be filling in below. + </li> +</ul> + + +<h2>Step 2</h2> + + <p>Add an entry to the menu for deleting a note:</p> +<ol> + <li> + In the <code>onCreateOptionsMenu()</code> method, add a new line: + <pre>menu.add(0, DELETE_ID, 0, R.string.menu_delete);</pre> + </li> + <li> + The whole method should now look like this:<br> + <pre> +@Override +public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + menu.add(0, INSERT_ID, 0, R.string.menu_insert); + menu.add(0, DELETE_ID, 0, R.string.menu_delete); + return true; +}</pre> + </li> +</ol> + +<h2>Step 3</h2> + <p>In the <code>onMenuItemSelected()</code> method, add a new case for + <code>DELETE_ID</code>:</p> + <pre> +mDbHelper.deleteNote(getListView().getSelectedItemId()); +fillData(); +return true;</pre> + + <ol> + <li> + Here, we use the <code>deleteNote</code> method to remove the note specified by ID. + In order to get the ID, we call <code>getListView().getSelectedItemId()</code>. + </li> + <li> + Then we fill the data to keep everything up to date. + </li> + </ol> + <p> + The whole method should now look like this:</p> + <pre> +@Override +public boolean onMenuItemSelected(int featureId, MenuItem item) { + switch(item.getItemId()) { + case INSERT_ID: + createNote(); + return true; + case DELETE_ID: + mDbHelper.deleteNote(getListView().getSelectedItemId()); + fillData(); + return true; + } + + return super.onMenuItemSelected(featureId, item); +}</pre> + +<h2 style="clear:right;">Step 4</h2> +<div class="sidebox" style="border:2px solid #FFFFDD;float:right; + background-color:#FFFFEE;margin-right:0px; + margin-bottom:.5em;margin-top:1em;padding:0em;width:240px;"> + <h2 style="border:0;font-size:12px;padding:.5em .5em .5em 1em;margin:0; + background-color:#FFFFDD;">Starting Other Activities</h2> + <p style="padding-left:.5em;font-size:12px;margin:0; + padding:.0em .5em .5em 1em;">In this example our Intent uses a class name specifically. + As well as + <a href="{@docRoot}kb/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 style="padding-left:.5em;font-size:12px;margin:0; + padding:.0em .5em .5em 1em;">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> + + <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" style="border:2px solid #FFFFDD;float:right; + background-color:#FFFFEE;margin-right:0px; + margin-bottom:.5em;margin-top:1em;padding:0em;width:240px;"> + <h2 style="border:0;font-size:12px;padding:.5em .5em .5em 1em;margin:0; + background-color:#FFFFDD;">The Art of Layout</h2> + <p style="padding-left:.5em;font-size:12px;margin:0; padding:.0em .5em .5em 1em;">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 style="padding-left:.5em;font-size:12px;margin:0; padding:.0em .5em .5em 1em;">Creating a + good UI is part art and part science, and the rest is work. Mastering <a + href="{@docRoot}devel/implementing-ui.html">Android layout</a> is an essential part of creating + a good looking Android application.</p> + <p style="padding-left:.5em;font-size:12px;margin:0; + padding:.0em .5em .5em 1em;">Take a look at the + <a href="{@docRoot}reference/view-gallery.html">View Gallery</a> + for some example layouts and how to use them. The ApiDemos sample project is also a + great resource from which to learn how to create different layouts.</p> + </div> + +<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> + Find the edit and 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 in our listener.</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" style="border:2px solid #FFFFDD;float:right; + background-color:#FFFFEE;margin-right:0px; + margin-bottom:.5em;margin-top:1em;padding:0em;width:240px;"> + <h2 style="border:0;font-size:12px;padding:.5em .5em .5em 1em;margin:0; + background-color:#FFFFDD;">The All-Important Android Manifest File</h2> + <p style="padding-left:.5em;font-size:12px;margin:0; + padding:.0em .5em .5em 1em;">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 style="padding-left:.5em;font-size:12px;margin:0; + padding:.0em .5em .5em 1em;">For more information, see the reference document + <a href="{@docRoot}devel/bblocks-manifest.html">AndroidManifest.xml</a></p> + </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"></activity></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="tutorial-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> + +<p><a href="tutorial.html">Back to the Tutorial main page...</a>.</p> + diff --git a/docs/html/guide/tutorials/notepad/notepad-ex3.jd b/docs/html/guide/tutorials/notepad/notepad-ex3.jd new file mode 100644 index 000000000000..b42734f826d9 --- /dev/null +++ b/docs/html/guide/tutorials/notepad/notepad-ex3.jd @@ -0,0 +1,358 @@ +page.title=Notepad Exercise 3 +@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="tutorial-ex1.html">Exercise 1</a>] + [<a href="tutorial-ex2.html">Exercise 2</a>] + <span style="color:#BBB;"> + [<a href="tutorial-ex3.html" style="color:#BBB;">Exercise 3</a>] + </span> + [<a href="tutorial-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 ? savedInstanceState.getLong(NotesDbAdapter.KEY_ROWID) + : null; + 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> + </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 ? savedInstanceState.getLong(NotesDbAdapter.KEY_ROWID) + : null; +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" style="border:2px solid #FFFFDD;float:right; + background-color:#FFFFEE;margin-right:0px;margin-bottom:.5em; + margin-top:1em;padding:0em;width:240px;"> + <h2 style="border:0;font-size:12px;padding:.5em .5em .5em 1em;margin:0; + background-color:#FFFFDD;">Why handling life-cycle events is important</h2> + <p style="padding-left:.5em;font-size:12px;margin:0; + padding:.0em .5em .5em 1em;">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 style="padding-left:.5em;font-size:12px;margin:0; + padding:.0em .5em .5em 1em;">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 style="padding-left:.5em;font-size:12px;margin:0;padding:.0em .5em .5em 1em;"> + Android has a <a href="{@docRoot}intro/lifecycle.html">well-defined life cycle</a>. + Life-cycle 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> + +<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); + outState.putLong(NotesDbAdapter.KEY_ROWID, mRowId); + }</pre> + </li> + <li><code> + onPause()</code>: + <pre> + @Override + protected void onPause() { + super.onPause(); + saveState(); + }</pre> + <p>We'll define <code>saveState()</code> next.</p> + </li> + <li><code> + onResume()</code>: + <pre> + @Override + protected void onResume() { + super.onResume(); + populateFields(); + }</pre> + </li> + </ol> + + +<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="{@docRoot}intro/tutorial-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> +<p><a href="{@docRoot}intro/tutorial.html">Back to the Tutorial main +page...</a></p> diff --git a/docs/html/guide/tutorials/notepad/notepad-extra-credit.jd b/docs/html/guide/tutorials/notepad/notepad-extra-credit.jd new file mode 100644 index 000000000000..9ab84ce677c1 --- /dev/null +++ b/docs/html/guide/tutorials/notepad/notepad-extra-credit.jd @@ -0,0 +1,70 @@ +page.title=Tutorial: Extra Credit +@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="tutorial-ex1.html">Exercise 1</a>] + [<a href="tutorial-ex2.html">Exercise 2</a>] + [<a href="tutorial-ex3.html">Exercise 3</a>] + <span style="color:#BBB;"> + [<a href="tutorial-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}reference/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> +<p><a href="{@docRoot}intro/tutorial.html">Back to the Tutorial main +page...</a></p> + diff --git a/docs/html/guide/tutorials/notepad/notepad-index.jd b/docs/html/guide/tutorials/notepad/notepad-index.jd new file mode 100644 index 000000000000..151c50dcda67 --- /dev/null +++ b/docs/html/guide/tutorials/notepad/notepad-index.jd @@ -0,0 +1,143 @@ +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> |