Dirk Dougherty | 5f0462a | 2013-11-19 13:15:07 -0800 | [diff] [blame] | 1 | excludeFromSuggestions=true |
Dirk Dougherty | 22558d0 | 2009-12-10 16:25:06 -0800 | [diff] [blame] | 2 | page.title=Notepad Exercise 1 |
| 3 | parent.title=Notepad Tutorial |
| 4 | parent.link=index.html |
| 5 | @jd:body |
| 6 | |
| 7 | |
| 8 | <p><em>In this exercise, you will construct a simple notes list that lets the |
| 9 | user add new notes but not edit them. The exercise demonstrates:</em></p> |
| 10 | <ul> |
| 11 | <li><em>The basics of <code>ListActivities</code> and creating and handling menu |
| 12 | options. </em></li> |
| 13 | <li><em>How to use a SQLite database to store the notes.</em></li> |
| 14 | <li><em>How to bind data from a database cursor into a ListView using a |
| 15 | SimpleCursorAdapter.</em></li> |
| 16 | <li><em>The basics of screen layouts, including how to lay out a list view, how |
| 17 | you can add items to the activity menu, and how the activity handles those menu |
| 18 | selections. </em></li> |
| 19 | </ul> |
| 20 | |
| 21 | <div style="float:right;white-space:nowrap"> |
| 22 | <span style="color:#BBB;"> |
| 23 | [<a href="notepad-ex1.html" style="color:#BBB;">Exercise 1</a>]</span> |
| 24 | [<a href="notepad-ex2.html">Exercise 2</a>] |
| 25 | [<a href="notepad-ex3.html">Exercise 3</a>] |
| 26 | [<a href="notepad-extra-credit.html">Extra Credit</a>] |
| 27 | </div> |
| 28 | |
| 29 | |
| 30 | |
| 31 | <h2>Step 1</h2> |
| 32 | |
| 33 | <p>Open up the <code>Notepadv1</code> project in Eclipse.</p> |
| 34 | |
| 35 | <p><code>Notepadv1</code> is a project that is provided as a starting point. It |
| 36 | takes care of some of the boilerplate work that you have already seen if you |
Scott Main | 50e990c | 2012-06-21 17:14:39 -0700 | [diff] [blame] | 37 | followed the <a href="{@docRoot}training/basics/firstapp/index.html">Hello, |
Dirk Dougherty | 22558d0 | 2009-12-10 16:25:06 -0800 | [diff] [blame] | 38 | World</a> tutorial.</p> |
| 39 | |
| 40 | <ol> |
| 41 | <li> |
| 42 | Start a new Android Project by clicking <strong>File</strong> > |
| 43 | <strong>New</strong> > <strong>Android Project</strong>.</li> |
| 44 | <li> |
| 45 | In the New Android Project dialog, select <strong>Create project from existing source</strong>.</li> |
| 46 | <li> |
| 47 | Click <strong>Browse</strong> and navigate to where you copied the <code>NotepadCodeLab</code> |
Scott Main | 64b1188 | 2012-06-29 16:04:25 -0700 | [diff] [blame] | 48 | (downloaded during <a href="{@docRoot}training/notepad/index.html#preparing">setup</a>) |
Dirk Dougherty | 22558d0 | 2009-12-10 16:25:06 -0800 | [diff] [blame] | 49 | and select <code>Notepadv1</code>.</li> |
| 50 | <li> |
| 51 | The Project Name and other properties should be automatically filled for you. |
| 52 | You must select the Build Target—we recommend selecting a target with the |
| 53 | lowest platform version available. Also add an integer to the Min SDK Version field |
| 54 | that matches the API Level of the selected Build Target.</li> |
| 55 | <li> |
| 56 | Click <strong>Finish</strong>. The <code>Notepadv1</code> project should open and be |
| 57 | visible in your Eclipse package explorer.</li> |
| 58 | </ol> |
| 59 | |
| 60 | <p>If you see an error about <code>AndroidManifest.xml</code>, or some |
| 61 | problems related to an Android zip file, right click on the project and |
| 62 | select <strong>Android Tools</strong> > <strong>Fix Project Properties</strong>. |
| 63 | (The project is looking in the wrong location for the library file, |
| 64 | this will fix it for you.)</p> |
| 65 | |
| 66 | <h2>Step 2</h2> |
| 67 | |
Scott Main | 3b3145e | 2010-03-17 12:39:51 -0700 | [diff] [blame] | 68 | <div class="sidebox-wrapper"> |
| 69 | <div class="sidebox"> |
| 70 | <h2>Accessing and modifying data</h2> |
| 71 | <p>For this |
Dirk Dougherty | 22558d0 | 2009-12-10 16:25:06 -0800 | [diff] [blame] | 72 | exercise, we are using a SQLite database to store our data. This is useful |
| 73 | if only <em>your</em> application will need to access or modify the data. If you wish for |
| 74 | other activities to access or modify the data, you have to expose the data using a |
| 75 | {@link android.content.ContentProvider ContentProvider}.</p> |
Scott Main | 3b3145e | 2010-03-17 12:39:51 -0700 | [diff] [blame] | 76 | <p>If you are interested, you can find out more about |
| 77 | <a href="{@docRoot}guide/topics/providers/content-providers.html">content providers</a> or the |
| 78 | whole |
Dirk Dougherty | 22558d0 | 2009-12-10 16:25:06 -0800 | [diff] [blame] | 79 | subject of <a href="{@docRoot}guide/topics/data/data-storage.html">Data Storage</a>. |
| 80 | The NotePad sample in the <code>samples/</code> folder of the SDK also has an example of how |
| 81 | to create a ContentProvider.</p> |
| 82 | </div> |
Scott Main | 3b3145e | 2010-03-17 12:39:51 -0700 | [diff] [blame] | 83 | </div> |
Dirk Dougherty | 22558d0 | 2009-12-10 16:25:06 -0800 | [diff] [blame] | 84 | |
| 85 | <p>Take a look at the <code>NotesDbAdapter</code> class — this class is provided to |
| 86 | encapsulate data access to a SQLite database that will hold our notes data |
| 87 | and allow us to update it.</p> |
| 88 | <p>At the top of the class are some constant definitions that will be used in the application |
| 89 | to look up data from the proper field names in the database. There is also a database creation |
| 90 | string defined, which is used to create a new database schema if one doesn't exist already.</p> |
| 91 | <p>Our database will have the name <code>data</code>, and have a single table called |
| 92 | <code>notes</code>, which in turn has three fields: <code>_id</code>, <code>title</code> and |
| 93 | <code>body</code>. The <code>_id</code> is named with an underscore convention used in a number of |
| 94 | places inside the Android SDK and helps keep a track of state. The <code>_id</code> |
| 95 | usually has to be specified when querying or updating the database (in the column projections |
| 96 | and so on). The other two fields are simple text fields that will store data. |
| 97 | </p> |
| 98 | <p>The constructor for <code>NotesDbAdapter</code> takes a Context, which allows it to communicate with aspects |
| 99 | of the Android operating system. This is quite common for classes that need to touch the |
| 100 | Android system in some way. The Activity class implements the Context class, so usually you will just pass |
| 101 | <code>this</code> from your Activity, when needing a Context.</p> |
| 102 | <p>The <code>open()</code> method calls up an instance of DatabaseHelper, which is our local |
| 103 | implementation of the SQLiteOpenHelper class. It calls <code>getWritableDatabase()</code>, |
| 104 | which handles creating/opening a database for us.</p> |
| 105 | <p><code>close()</code> just closes the database, releasing resources related to the |
| 106 | connection.</p> |
| 107 | <p><code>createNote()</code> takes strings for the title and body of a new note, |
| 108 | then creates that note in the database. Assuming the new note is created successfully, the |
| 109 | method also returns the row <code>_id</code> value for the newly created note.</p> |
| 110 | <p><code>deleteNote()</code> takes a <var>rowId</var> for a particular note, and deletes that note from |
| 111 | the database.</p> |
| 112 | |
| 113 | <p><code>fetchAllNotes()</code> issues a query to return a {@link android.database.Cursor} over all notes in the |
| 114 | database. The <code>query()</code> call is worth examination and understanding. The first field is the |
| 115 | name of the database table to query (in this case <code>DATABASE_TABLE</code> is "notes"). |
| 116 | The next is the list of columns we want returned, in this case we want the <code>_id</code>, |
| 117 | <code>title</code> and <code>body</code> columns so these are specified in the String array. |
| 118 | The remaining fields are, in order: <code>selection</code>, |
| 119 | <code>selectionArgs</code>, <code>groupBy</code>, <code>having</code> and <code>orderBy</code>. |
| 120 | Having these all <code>null</code> means we want all data, need no grouping, and will take the default |
| 121 | order. See {@link android.database.sqlite.SQLiteDatabase SQLiteDatabase} for more details.</p> |
| 122 | <p class="note"><b>Note:</b> A Cursor is returned rather than a collection of rows. This allows |
| 123 | Android to use resources efficiently -- instead of putting lots of data straight into memory |
| 124 | the cursor will retrieve and release data as it is needed, which is much more efficient for |
| 125 | tables with lots of rows.</p> |
| 126 | |
| 127 | <p><code>fetchNote()</code> is similar to <code>fetchAllNotes()</code> but just gets one note |
| 128 | with the <var>rowId</var> we specify. It uses a slightly different version of the |
| 129 | {@link android.database.sqlite.SQLiteDatabase} <code>query()</code> method. |
| 130 | The first parameter (set <em>true</em>) indicates that we are interested |
| 131 | in one distinct result. The <var>selection</var> parameter (the fourth parameter) has been specified to search |
| 132 | only for the row "where _id =" the <var>rowId</var> we passed in. So we are returned a Cursor on |
| 133 | the one row.</p> |
| 134 | <p>And finally, <code>updateNote()</code> takes a <var>rowId</var>, <var>title</var> and <var>body</var>, and uses a |
| 135 | {@link android.content.ContentValues ContentValues} instance to update the note of the given |
| 136 | <var>rowId</var>.</p> |
| 137 | |
| 138 | <h2 style="clear:right;">Step 3</h2> |
| 139 | |
Scott Main | 3b3145e | 2010-03-17 12:39:51 -0700 | [diff] [blame] | 140 | <div class="sidebox-wrapper"> |
| 141 | <div class="sidebox"> |
| 142 | <h2>Layouts and activities</h2> |
| 143 | <p>Most Activity classes will have a layout associated with them. The layout |
Dirk Dougherty | 22558d0 | 2009-12-10 16:25:06 -0800 | [diff] [blame] | 144 | will be the "face" of the Activity to the user. In this case our layout will |
| 145 | take over the whole screen and provide a list of notes.</p> |
Scott Main | 3b3145e | 2010-03-17 12:39:51 -0700 | [diff] [blame] | 146 | <p>Full screen layouts are not the only option for an Activity however. You |
Dirk Dougherty | 22558d0 | 2009-12-10 16:25:06 -0800 | [diff] [blame] | 147 | might also want to use a <a |
| 148 | href="{@docRoot}resources/faq/commontasks.html#floatingorfull">floating |
| 149 | layout</a> (for example, a <a |
| 150 | href="{@docRoot}resources/faq/commontasks.html#dialogsandalerts">dialog |
| 151 | or alert</a>), |
| 152 | or perhaps you don't need a layout at all (the Activity will be invisible |
| 153 | to the user unless you specify some kind of layout for it to use).</p> |
Scott Main | 3b3145e | 2010-03-17 12:39:51 -0700 | [diff] [blame] | 154 | </div> |
| 155 | </div> |
Dirk Dougherty | 22558d0 | 2009-12-10 16:25:06 -0800 | [diff] [blame] | 156 | |
| 157 | <p>Open the <code>notepad_list.xml</code> file in <code>res/layout</code> |
| 158 | and |
| 159 | take a look at it. (You may have to |
| 160 | hit the <em>xml</em> tab, at the bottom, in order to view the XML markup.)</p> |
| 161 | |
| 162 | <p>This is a mostly-empty layout definition file. Here are some |
| 163 | things you should know about a layout file:</p> |
| 164 | |
| 165 | |
| 166 | <ul> |
| 167 | <li> |
| 168 | All Android layout files must start with the XML header line: |
| 169 | <code><?xml version="1.0" encoding="utf-8"?></code>. </li> |
| 170 | <li> |
| 171 | The next definition will often (but not always) be a layout |
| 172 | definition of some kind, in this case a <code>LinearLayout</code>. </li> |
| 173 | <li> |
| 174 | The XML namespace of Android should always be defined in |
| 175 | the top level component or layout in the XML so that <code>android:</code> tags can |
| 176 | be used through the rest of the file: |
| 177 | <p><code>xmlns:android="http://schemas.android.com/apk/res/android"</code></p> |
| 178 | </li> |
| 179 | </ul> |
| 180 | |
| 181 | <h2 style="clear:right;">Step 4</h2> |
| 182 | <p>We need to create the layout to hold our list. Add code inside |
| 183 | of the <code>LinearLayout</code> element so the whole file looks like this: </p> |
| 184 | <pre> |
| 185 | <?xml version="1.0" encoding="utf-8"?> |
| 186 | <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
| 187 | android:layout_width="wrap_content" |
| 188 | android:layout_height="wrap_content"> |
| 189 | |
| 190 | <ListView android:id="@android:id/list" |
| 191 | android:layout_width="wrap_content" |
| 192 | android:layout_height="wrap_content"/> |
| 193 | <TextView android:id="@android:id/empty" |
| 194 | android:layout_width="wrap_content" |
| 195 | android:layout_height="wrap_content" |
| 196 | android:text="@string/no_notes"/> |
| 197 | |
| 198 | </LinearLayout> |
| 199 | </pre> |
| 200 | <ul> |
| 201 | <li> |
| 202 | The <strong>@</strong> symbol in the id strings of the <code>ListView</code> and |
| 203 | <code>TextView</code> tags means |
| 204 | that the XML parser should parse and expand the rest of |
| 205 | the id string and use an ID resource.</li> |
| 206 | <li> |
| 207 | The <code>ListView</code> and <code>TextView</code> can be |
| 208 | thought as two alternative views, only one of which will be displayed at once. |
| 209 | ListView will be used when there are notes to be shown, while the TextView |
| 210 | (which has a default value of "No Notes Yet!" defined as a string |
| 211 | resource in <code>res/values/strings.xml</code>) will be displayed if there |
| 212 | aren't any notes to display.</li> |
| 213 | <li>The <code>list</code> and <code>empty</code> IDs are |
| 214 | provided for us by the Android platform, so, we must |
| 215 | prefix the <code>id</code> with <code>android:</code> (e.g., <code>@android:id/list</code>).</li> |
| 216 | <li>The View with the <code>empty</code> id is used |
| 217 | automatically when the {@link android.widget.ListAdapter} has no data for the ListView. The |
| 218 | ListAdapter knows to look for this name by default. Alternatively, you could change the |
| 219 | default empty view by using {@link android.widget.AdapterView#setEmptyView(View)} |
| 220 | on the ListView. |
| 221 | <p> |
| 222 | More broadly, the <code>android.R</code> class is a set of predefined |
| 223 | resources provided for you by the platform, while your project's |
| 224 | <code>R</code> class is the set of resources your project has defined. |
| 225 | Resources found in the <code>android.R</code> resource class can be |
| 226 | used in the XML files by using the <code>android:</code> name space prefix |
| 227 | (as we see here).</p> |
| 228 | </li> |
| 229 | </ul> |
| 230 | |
| 231 | <h2 style="clear:right;">Step 5</h2> |
| 232 | |
Scott Main | 3b3145e | 2010-03-17 12:39:51 -0700 | [diff] [blame] | 233 | <div class="sidebox-wrapper"> |
| 234 | <div class="sidebox"> |
| 235 | <h2>Resources and the R class</h2> |
| 236 | <p>The folders under res/ in the Eclipse project are for resources. |
| 237 | There is a <a href="{@docRoot}resources/faq/commontasks.html#filelist">specific structure</a> |
| 238 | to the |
Dirk Dougherty | 22558d0 | 2009-12-10 16:25:06 -0800 | [diff] [blame] | 239 | folders and files under res/.</p> |
Scott Main | 3b3145e | 2010-03-17 12:39:51 -0700 | [diff] [blame] | 240 | <p>Resources defined in these folders and files will have |
Dirk Dougherty | 22558d0 | 2009-12-10 16:25:06 -0800 | [diff] [blame] | 241 | corresponding entries in the R class allowing them to be easily accessed |
| 242 | and used from your application. The R class is automatically generated using the contents |
| 243 | of the res/ folder by the eclipse plugin (or by aapt if you use the command line tools). |
| 244 | Furthermore, they will be bundled and deployed for you as part of the application.</p> |
| 245 | </p> |
| 246 | </div> |
Scott Main | 3b3145e | 2010-03-17 12:39:51 -0700 | [diff] [blame] | 247 | </div> |
| 248 | |
Dirk Dougherty | 22558d0 | 2009-12-10 16:25:06 -0800 | [diff] [blame] | 249 | <p>To make the list of notes in the ListView, we also need to define a View for each row:</p> |
| 250 | <ol> |
| 251 | <li> |
| 252 | Create a new file under <code>res/layout</code> called |
| 253 | <code>notes_row.xml</code>. </li> |
| 254 | <li> |
| 255 | Add the following contents (note: again the XML header is used, and the |
| 256 | first node defines the Android XML namespace)<br> |
| 257 | <pre style="overflow:auto"> |
| 258 | <?xml version="1.0" encoding="utf-8"?> |
| 259 | <TextView android:id="@+id/text1" |
| 260 | xmlns:android="http://schemas.android.com/apk/res/android" |
| 261 | android:layout_width="wrap_content" |
| 262 | android:layout_height="wrap_content"/></pre> |
| 263 | <p> |
| 264 | This is the View that will be used for each notes title row — it has only |
| 265 | one text field in it. </p> |
| 266 | <p>In this case we create a new id called <code>text1</code>. The |
| 267 | <strong>+</strong> after the <strong>@</strong> in the id string indicates that the id should |
| 268 | be automatically created as a resource if it does not already exist, so we are defining |
| 269 | <code>text1</code> on the fly and then using it.</p> |
| 270 | </li> |
| 271 | <li>Save the file.</li> |
| 272 | </ol> |
| 273 | <p>Open the <code>R.java</code> class in the |
| 274 | project and look at it, you should see new definitions for |
| 275 | <code>notes_row</code> and <code>text1</code> (our new definitions) |
| 276 | meaning we can now gain access to these from the our code. </p> |
| 277 | |
| 278 | <h2 style="clear:right;">Step 6</h2> |
| 279 | <p>Next, open the <code>Notepadv1</code> class in the source. In the following steps, we are going to |
| 280 | alter this class to become a list adapter and display our notes, and also |
| 281 | allow us to add new notes.</p> |
| 282 | |
| 283 | <p><code>Notepadv1</code> will inherit from a subclass |
| 284 | of <code>Activity</code> called a <code>ListActivity</code>, |
| 285 | which has extra functionality to accommodate the kinds of |
| 286 | things you might want to do with a list, for |
| 287 | example: displaying an arbitrary number of list items in rows on the screen, |
| 288 | moving through the list items, and allowing them to be selected.</p> |
| 289 | |
| 290 | <p>Take a look through the existing code in <code>Notepadv1</code> class. |
| 291 | There is a currently an unused private field called <code>mNoteNumber</code> that |
| 292 | we will use to create numbered note titles.</p> |
| 293 | <p>There are also three override methods defined: |
| 294 | <code>onCreate</code>, <code>onCreateOptionsMenu</code> and |
| 295 | <code>onOptionsItemSelected</code>; we need to fill these |
| 296 | out:</p> |
| 297 | <ul> |
| 298 | <li><code>onCreate()</code> is called when the activity is |
| 299 | started — it is a little like the "main" method for an Activity. We use |
| 300 | this to set up resources and state for the activity when it is |
| 301 | running.</li> |
| 302 | <li><code>onCreateOptionsMenu()</code> is used to populate the |
| 303 | menu for the Activity. This is shown when the user hits the menu button, |
| 304 | and |
| 305 | has a list of options they can select (like "Create |
| 306 | Note"). </li> |
| 307 | <li><code>onOptionsItemSelected()</code> is the other half of the |
| 308 | menu equation, it is used to handle events generated from the menu (e.g., |
| 309 | when the user selects the "Create Note" item). |
| 310 | </li> |
| 311 | </ul> |
| 312 | |
| 313 | <h2>Step 7</h2> |
| 314 | <p>Change the inheritance of <code>Notepadv1</code> from |
| 315 | <code>Activity</code> |
| 316 | to <code>ListActivity</code>:</p> |
| 317 | <pre>public class Notepadv1 extends ListActivity</pre> |
| 318 | <p>Note: you will have to import <code>ListActivity</code> into the |
| 319 | Notepadv1 |
| 320 | class using Eclipse, <strong>ctrl-shift-O</strong> on Windows or Linux, or |
| 321 | <strong>cmd-shift-O</strong> on the Mac (organize imports) will do this for you |
| 322 | after you've written the above change.</p> |
| 323 | |
| 324 | <h2>Step 8</h2> |
| 325 | <p>Fill out the body of the <code>onCreate()</code> method.</p> |
| 326 | <p>Here we will set the title for the Activity (shown at the top of the |
| 327 | screen), use the <code>notepad_list</code> layout we created in XML, |
| 328 | set up the <code>NotesDbAdapter</code> instance that will |
| 329 | access notes data, and populate the list with the available note |
| 330 | titles:</p> |
| 331 | <ol> |
| 332 | <li> |
| 333 | In the <code>onCreate</code> method, call <code>super.onCreate()</code> with the |
| 334 | <code>savedInstanceState</code> parameter that's passed in.</li> |
| 335 | <li> |
| 336 | Call <code>setContentView()</code> and pass <code>R.layout.notepad_list</code>.</li> |
| 337 | <li> |
| 338 | At the top of the class, create a new private class field called <code>mDbHelper</code> of class |
| 339 | <code>NotesDbAdapter</code>. |
| 340 | </li> |
| 341 | <li> |
| 342 | Back in the <code>onCreate</code> method, construct a new |
| 343 | <code>NotesDbAdapter</code> |
| 344 | instance and assign it to the <code>mDbHelper</code> field (pass |
| 345 | <code>this</code> into the constructor for <code>DBHelper</code>) |
| 346 | </li> |
| 347 | <li> |
| 348 | Call the <code>open()</code> method on <code>mDbHelper</code> to open (or create) the |
| 349 | database. |
| 350 | </li> |
| 351 | <li> |
| 352 | Finally, call a new method <code>fillData()</code>, which will get the data and |
| 353 | populate the ListView using the helper — we haven't defined this method yet. </li> |
| 354 | </ol> |
| 355 | <p> |
| 356 | <code>onCreate()</code> should now look like this:</p> |
| 357 | <pre> |
| 358 | @Override |
| 359 | public void onCreate(Bundle savedInstanceState) { |
| 360 | super.onCreate(savedInstanceState); |
| 361 | setContentView(R.layout.notepad_list); |
| 362 | mDbHelper = new NotesDbAdapter(this); |
| 363 | mDbHelper.open(); |
| 364 | fillData(); |
| 365 | }</pre> |
| 366 | <p>And be sure you have the <code>mDbHelper</code> field definition (right |
| 367 | under the mNoteNumber definition): </p> |
| 368 | <pre> private NotesDbAdapter mDbHelper;</pre> |
| 369 | |
| 370 | <h2>Step 9</h2> |
| 371 | |
Scott Main | 3b3145e | 2010-03-17 12:39:51 -0700 | [diff] [blame] | 372 | <div class="sidebox-wrapper"> |
| 373 | <div class="sidebox"> |
| 374 | <h2>More about menus</h2> |
| 375 | <p>The notepad application we are constructing only scratches the |
Dirk Dougherty | 22558d0 | 2009-12-10 16:25:06 -0800 | [diff] [blame] | 376 | surface with <a href="{@docRoot}resources/faq/commontasks.html#addmenuitems">menus</a>. </p> |
Scott Main | 3b3145e | 2010-03-17 12:39:51 -0700 | [diff] [blame] | 377 | <p>You can also <a href="{@docRoot}resources/faq/commontasks.html#menukeyshortcuts">add |
| 378 | shortcut keys for menu items</a>, <a |
| 379 | href="{@docRoot}resources/faq/commontasks.html#menukeyshortcuts">create |
Dirk Dougherty | 22558d0 | 2009-12-10 16:25:06 -0800 | [diff] [blame] | 380 | submenus</a> and even <a href="{@docRoot}resources/faq/commontasks.html#addingtoothermenus">add |
| 381 | menu items to other applications!</a>. </p> |
| 382 | </div> |
Scott Main | 3b3145e | 2010-03-17 12:39:51 -0700 | [diff] [blame] | 383 | </div> |
Dirk Dougherty | 22558d0 | 2009-12-10 16:25:06 -0800 | [diff] [blame] | 384 | |
| 385 | <p>Fill out the body of the <code>onCreateOptionsMenu()</code> method.</p> |
| 386 | |
| 387 | <p>We will now create the "Add Item" button that can be accessed by pressing the menu |
| 388 | button on the device. We'll specify that it occupy the first position in the menu.</p> |
| 389 | |
| 390 | <ol> |
| 391 | <li> |
| 392 | In <code>strings.xml</code> resource (under <code>res/values</code>), add |
| 393 | a new string named "menu_insert" with its value set to <code>Add Item</code>: |
| 394 | <pre><string name="menu_insert">Add Item</string></pre> |
| 395 | <p>Then save the file and return to <code>Notepadv1</code>.</p> |
| 396 | </li> |
| 397 | <li>Create a menu position constant at the top of the class: |
| 398 | <pre>public static final int INSERT_ID = Menu.FIRST;</pre> |
| 399 | </li> |
| 400 | <li>In the <code>onCreateOptionsMenu()</code> method, change the |
| 401 | <code>super</code> call so we capture the boolean return as <code>result</code>. We'll return this value at the end.</li> |
| 402 | <li>Then add the menu item with <code>menu.add()</code>.</li> |
| 403 | </ol> |
| 404 | <p>The whole method should now look like this: |
| 405 | <pre> |
| 406 | @Override |
| 407 | public boolean onCreateOptionsMenu(Menu menu) { |
| 408 | boolean result = super.onCreateOptionsMenu(menu); |
| 409 | menu.add(0, INSERT_ID, 0, R.string.menu_insert); |
| 410 | return result; |
| 411 | }</pre> |
| 412 | <p>The arguments passed to <code>add()</code> indicate: a group identifier for this menu (none, |
| 413 | in this case), a unique ID (defined above), the order of the item (zero indicates no preference), |
| 414 | and the resource of the string to use for the item.</p> |
| 415 | |
| 416 | <h2 style="clear:right;">Step 10</h2> |
| 417 | <p>Fill out the body of the <code>onOptionsItemSelected()</code> method:</p> |
| 418 | <p>This is going |
| 419 | to handle our new "Add Note" menu item. When this is selected, the |
| 420 | <code>onOptionsItemSelected()</code> method will be called with the |
| 421 | <code>item.getId()</code> set to <code>INSERT_ID</code> (the constant we |
| 422 | used to identify the menu item). We can detect this, and take the |
| 423 | appropriate actions:</p> |
| 424 | <ol> |
| 425 | <li> |
| 426 | The <code>super.onOptionsItemSelected(item)</code> method call goes at the |
| 427 | end of this method — we want to catch our events first! </li> |
| 428 | <li> |
| 429 | Write a switch statement on <code>item.getItemId()</code>. |
| 430 | <p>In the case of <var>INSERT_ID</var>, call a new method, <code>createNote()</code>, |
| 431 | and return true, because we have handled this event and do not want to |
| 432 | propagate it through the system.</p> |
| 433 | </li> |
| 434 | <li>Return the result of the superclass' <code>onOptionsItemSelected()</code> |
| 435 | method at the end.</li> |
| 436 | </ol> |
| 437 | <p> |
| 438 | The whole <code>onOptionsItemSelect()</code> method should now look like |
| 439 | this:</p> |
| 440 | <pre> |
| 441 | @Override |
| 442 | public boolean onOptionsItemSelected(MenuItem item) { |
| 443 | switch (item.getItemId()) { |
| 444 | case INSERT_ID: |
| 445 | createNote(); |
| 446 | return true; |
| 447 | } |
| 448 | |
| 449 | return super.onOptionsItemSelected(item); |
| 450 | }</pre> |
| 451 | |
| 452 | <h2>Step 11</h2> |
| 453 | <p>Add a new <code>createNote()</code> method:</p> |
| 454 | <p>In this first version of |
| 455 | our application, <code>createNote()</code> is not going to be very useful. |
| 456 | We will simply |
| 457 | create a new note with a title assigned to it based on a counter ("Note 1", |
| 458 | "Note 2"...) and with an empty body. At present we have no way of editing |
| 459 | the contents of a note, so for now we will have to be content making one |
| 460 | with some default values:</p> |
| 461 | <ol> |
| 462 | <li>Construct the name using "Note" and the counter we defined in the class: <code> |
| 463 | String noteName = "Note " + mNoteNumber++</code></li> |
| 464 | <li> |
| 465 | Call <code>mDbHelper.createNote()</code> using <code>noteName</code> as the |
| 466 | title and <code>""</code> for the body |
| 467 | </li> |
| 468 | <li> |
| 469 | Call <code>fillData()</code> to populate the list of notes (inefficient but |
| 470 | simple) — we'll create this method next.</li> |
| 471 | </ol> |
| 472 | <p> |
| 473 | The whole <code>createNote()</code> method should look like this: </p> |
| 474 | <pre> |
| 475 | private void createNote() { |
| 476 | String noteName = "Note " + mNoteNumber++; |
| 477 | mDbHelper.createNote(noteName, ""); |
| 478 | fillData(); |
| 479 | }</pre> |
| 480 | |
| 481 | |
| 482 | <h2>Step 12</h2> |
Scott Main | 3b3145e | 2010-03-17 12:39:51 -0700 | [diff] [blame] | 483 | <div class="sidebox-wrapper"> |
| 484 | <div class="sidebox"> |
| 485 | <h2>List adapters</h2> |
| 486 | <p>Our example uses a {@link android.widget.SimpleCursorAdapter |
Dirk Dougherty | 22558d0 | 2009-12-10 16:25:06 -0800 | [diff] [blame] | 487 | SimpleCursorAdapter} to bind a database {@link android.database.Cursor Cursor} |
| 488 | into a ListView, and this is a common way to use a {@link android.widget.ListAdapter |
| 489 | ListAdapter}. Other options exist like {@link android.widget.ArrayAdapter ArrayAdapter} which |
| 490 | can be used to take a List or Array of in-memory data and bind it in to |
| 491 | a list as well.</p> |
| 492 | </div> |
Scott Main | 3b3145e | 2010-03-17 12:39:51 -0700 | [diff] [blame] | 493 | </div> |
Dirk Dougherty | 22558d0 | 2009-12-10 16:25:06 -0800 | [diff] [blame] | 494 | |
| 495 | <p>Define the <code>fillData()</code> method:</p> |
| 496 | <p>This |
| 497 | method uses <code>SimpleCursorAdapter,</code> which takes a database <code>Cursor</code> |
| 498 | and binds it to fields provided in the layout. These fields define the row elements of our list |
| 499 | (in this case we use the <code>text1</code> field in our |
| 500 | <code>notes_row.xml</code> layout), so this allows us to easily populate the list with |
| 501 | entries from our database.</p> |
| 502 | <p>To do this we have to provide a mapping from the <code>title</code> field in the returned Cursor, to |
| 503 | our <code>text1</code> TextView, which is done by defining two arrays: the first a string array |
| 504 | with the list of columns to map <em>from</em> (just "title" in this case, from the constant |
| 505 | <code>NotesDbAdapter.KEY_TITLE</code>) and, the second, an int array |
| 506 | containing references to the views that we'll bind the data <em>into</em> |
| 507 | (the <code>R.id.text1</code> TextView).</p> |
| 508 | <p>This is a bigger chunk of code, so let's first take a look at it:</p> |
| 509 | |
| 510 | <pre> |
| 511 | private void fillData() { |
| 512 | // Get all of the notes from the database and create the item list |
| 513 | Cursor c = mDbHelper.fetchAllNotes(); |
| 514 | startManagingCursor(c); |
| 515 | |
| 516 | String[] from = new String[] { NotesDbAdapter.KEY_TITLE }; |
| 517 | int[] to = new int[] { R.id.text1 }; |
| 518 | |
| 519 | // Now create an array adapter and set it to display using our row |
| 520 | SimpleCursorAdapter notes = |
| 521 | new SimpleCursorAdapter(this, R.layout.notes_row, c, from, to); |
| 522 | setListAdapter(notes); |
| 523 | }</pre> |
| 524 | |
| 525 | <p>Here's what we've done:</p> |
| 526 | <ol> |
| 527 | <li> |
| 528 | After obtaining the Cursor from <code>mDbHelper.fetchAllNotes()</code>, we |
| 529 | use an Activity method called |
| 530 | <code>startManagingCursor()</code> that allows Android to take care of the |
| 531 | Cursor lifecycle instead of us needing to worry about it. (We will cover the implications |
| 532 | of the lifecycle in exercise 3, but for now just know that this allows Android to do some |
| 533 | of our resource management work for us.)</li> |
| 534 | <li> |
| 535 | Then we create a string array in which we declare the column(s) we want |
| 536 | (just the title, in this case), and an int array that defines the View(s) |
| 537 | to which we'd like to bind the columns (these should be in order, respective to |
| 538 | the string array, but here we only have one for each).</li> |
| 539 | <li> |
| 540 | Next is the SimpleCursorAdapter instantiation. |
| 541 | Like many classes in Android, the SimpleCursorAdapter needs a Context in order to do its |
| 542 | work, so we pass in <code>this</code> for the context (since subclasses of Activity |
| 543 | implement Context). We pass the <code>notes_row</code> View we created as the receptacle |
| 544 | for the data, the Cursor we just created, and then our arrays.</li> |
| 545 | </ol> |
| 546 | <p> |
| 547 | In the future, remember that the mapping between the <strong>from</strong> columns and <strong>to</strong> resources |
| 548 | is done using the respective ordering of the two arrays. If we had more columns we wanted |
| 549 | to bind, and more Views to bind them in to, we would specify them in order, for example we |
| 550 | might use <code>{ NotesDbAdapter.KEY_TITLE, NotesDbAdapter.KEY_BODY }</code> and |
| 551 | <code>{ R.id.text1, R.id.text2 }</code> to bind two fields into the row (and we would also need |
| 552 | to define text2 in the notes_row.xml, for the body text). This is how you can bind multiple fields |
| 553 | into a single row (and get a custom row layout as well).</p> |
| 554 | <p> |
| 555 | If you get compiler errors about classes not being found, ctrl-shift-O or |
| 556 | (cmd-shift-O on the mac) to organize imports. |
| 557 | </p> |
| 558 | |
| 559 | <h2 style="clear:right;">Step 13</h2> |
| 560 | <p>Run it! |
| 561 | <ol> |
| 562 | <li> |
| 563 | Right click on the <code>Notepadv1</code> project.</li> |
| 564 | <li> |
| 565 | From the popup menu, select <strong>Run As</strong> > |
| 566 | <strong>Android Application</strong>.</li> |
| 567 | <li> |
| 568 | If you see a dialog come up, select Android Launcher as the way of running |
| 569 | the application (you can also use the link near the top of the dialog to |
| 570 | set this as your default for the workspace; this is recommended as it will |
| 571 | stop the plugin from asking you this every time).</li> |
| 572 | <li>Add new notes by hitting the menu button and selecting <em>Add |
| 573 | Item</em> from the menu.</li> |
| 574 | </ol> |
| 575 | |
| 576 | <h2 style="clear:right;">Solution and Next Steps</h2> |
| 577 | <p>You can see the solution to this class in <code>Notepadv1Solution</code> |
| 578 | from |
| 579 | the zip file to compare with your own.</p> |
| 580 | |
| 581 | <p>Once you are ready, move on to <a href="notepad-ex2.html">Tutorial |
| 582 | Exercise 2</a> to add the ability to create, edit and delete notes.</p> |
| 583 | |