blob: d15499b3750f6d7a7c188e4c7c754cf6c56d5612 [file] [log] [blame]
Dirk Dougherty5f0462a2013-11-19 13:15:07 -08001excludeFromSuggestions=true
2page.title=Notepad Exercise 2
Dirk Dougherty22558d02009-12-10 16:25:06 -08003parent.title=Notepad Tutorial
4parent.link=index.html
5@jd:body
6
7
8<p><em>In this exercise, you will add a second Activity to your notepad application, to let the user
Tom O'Neillc88f13e2009-12-17 12:24:05 -08009create and edit notes. You will also allow the user to delete existing notes through a context menu.
Dirk Dougherty22558d02009-12-10 16:25:06 -080010The new Activity assumes responsibility for creating new notes by
11collecting user input and packing it into a return Bundle provided by the intent. This exercise
12demonstrates:</em></p>
13<ul>
14<li><em>Constructing a new Activity and adding it to the Android manifest</em></li>
15<li><em>Invoking another Activity asynchronously using <code>startActivityForResult()</code></em></li>
16<li><em>Passing data between Activity in Bundle objects</em></li>
17<li><em>How to use a more advanced screen layout</em></li>
18<li><em>How to create a context menu</em></li>
19</ul>
20
21<div style="float:right;white-space:nowrap">
22 [<a href="notepad-ex1.html">Exercise 1</a>]
23 <span style="color:#BBB;">
24 [<a href="notepad-ex2.html" style="color:#DDD;">Exercise 2</a>]
25 </span>
26 [<a href="notepad-ex3.html">Exercise 3</a>]
27 [<a href="notepad-extra-credit.html">Extra Credit</a>]
28</div>
29
30<h2>Step 1</h2>
31
32<p>Create a new Android project using the sources from <code>Notepadv2</code> under the
33<code>NotepadCodeLab</code> folder, just like you did for the first exercise. If you see an error about
34<code>AndroidManifest.xml</code>, or some problems related to an
35<code>android.zip</code> file, right click on the project and select <strong>Android
36Tools</strong> &gt; <strong>Fix Project Properties</strong>.</p>
37
38<p>Open the <code>Notepadv2</code> project and take a look around:</p>
39<ul>
40 <li>
41 Open and look at the <code>strings.xml</code> file under
42 <code>res/values</code> &mdash; there are several new strings which we will use
43 for our new functionality
44 </li>
45 <li>
46 Also, open and take a look at the top of the <code>Notepadv2</code> class,
47 you will notice several new constants have been defined along with a new <code>mNotesCursor</code>
48 field used to hold the cursor we are using.
49 </li>
50 <li>
51 Note also that the <code>fillData()</code> method has a few more comments and now uses
52 the new field to store the notes Cursor. The <code>onCreate()</code> method is
53 unchanged from the first exercise. Also notice that the member field used to store the
54 notes Cursor is now called <code>mNotesCursor</code>. The <code>m</code> denotes a member
55 field and is part of the Android coding style standards.
56 </li>
57 <li>
58 There are also a couple of new overridden methods
Tom O'Neillc88f13e2009-12-17 12:24:05 -080059 (<code>onCreateContextMenu()</code>, <code>onContextItemSelected()</code>,
Dirk Dougherty22558d02009-12-10 16:25:06 -080060 <code>onListItemClick()</code> and <code>onActivityResult()</code>)
61 which we will be filling in below.
62 </li>
63</ul>
64
65
66<h2>Step 2</h2>
Scott Main3b3145e2010-03-17 12:39:51 -070067<div class="sidebox-wrapper">
Dirk Dougherty22558d02009-12-10 16:25:06 -080068<div class="sidebox">
69<p>Context menus should always be used when performing actions upon specific elements in the UI.
70When you register a View to a context menu, the context menu is revealed by performing a "long-click"
71on the UI component (press and hold the touchscreen or highlight and hold down the selection key for about two seconds).</p>
72</div>
Scott Main3b3145e2010-03-17 12:39:51 -070073</div>
Dirk Dougherty22558d02009-12-10 16:25:06 -080074
75<p>First, let's create the context menu that will allow users to delete individual notes.
76Open the Notepadv2 class.</p>
77
78<ol>
79 <li>In order for each list item in the ListView to register for the context menu, we call
Tom O'Neillc88f13e2009-12-17 12:24:05 -080080 <code>registerForContextMenu()</code> and pass it our ListView. So, at the very end of
Dirk Dougherty22558d02009-12-10 16:25:06 -080081 the <code>onCreate()</code> method add this line:
82 <pre>registerForContextMenu(getListView());</pre>
83 <p>Because our Activity extends the ListActivity class, <code>getListView()</code> will return us
84 the local ListView object for the Activity. Now, each list item in this ListView will activate the
85 context menu.
86 <li>
87 Now fill in the <code>onCreateContextMenu()</code> method. This callback is similar to the other
88 menu callback used for the options menu. Here, we add just one line, which will add a menu item
89 to delete a note. Call <code>menu.add()</code> like so:
90 <pre>
Scott Main93dc6422012-02-24 12:04:06 -080091public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
Dirk Dougherty22558d02009-12-10 16:25:06 -080092 super.onCreateContextMenu(menu, v, menuInfo);
93 menu.add(0, DELETE_ID, 0, R.string.menu_delete);
94}</pre>
Tom O'Neillc88f13e2009-12-17 12:24:05 -080095 <p>The <code>onCreateContextMenu()</code> callback passes some other information in addition to the Menu object,
Dirk Dougherty22558d02009-12-10 16:25:06 -080096 such as the View that has been triggered for the menu and
97 an extra object that may contain additional information about the object selected. However, we don't care about
98 these here, because we only have one kind of object in the Activity that uses context menus. In the next
99 step, we'll handle the menu item selection.</p>
100 </li>
101</ol>
102
103<h2>Step 3</h2>
Scott Kennedy7ed189e2013-01-11 22:31:43 -0800104 <p>Now that we've registered our ListView for a context menu and defined our context menu item, we need
Dirk Dougherty22558d02009-12-10 16:25:06 -0800105 to handle the callback when it is selected. For this, we need to identify the list ID of the
Tom O'Neillc88f13e2009-12-17 12:24:05 -0800106 selected item, then delete it. So fill in the
Dirk Dougherty22558d02009-12-10 16:25:06 -0800107 <code>onContextItemSelected()</code> method like this:</p>
108<pre>
109public boolean onContextItemSelected(MenuItem item) {
110 switch(item.getItemId()) {
111 case DELETE_ID:
112 AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
113 mDbHelper.deleteNote(info.id);
114 fillData();
115 return true;
116 }
117 return super.onContextItemSelected(item);
118}</pre>
119<p>Here, we retrieve the {@link android.widget.AdapterView.AdapterContextMenuInfo AdapterContextMenuInfo}
120with {@link android.view.MenuItem#getMenuInfo()}. The <var>id</var> field of this object tells us
121the position of the item in the ListView. We then pass this to the <code>deleteNote()</code>
122method of our NotesDbAdapter and the note is deleted. That's it for the context menu &mdash; notes
123can now be deleted.</p>
124
125<h2 style="clear:right;">Step 4</h2>
Scott Main3b3145e2010-03-17 12:39:51 -0700126 <div class="sidebox-wrapper">
127 <div class="sidebox">
128 <h2>Starting Other Activities</h2>
129 <p>In this example our Intent uses a class name specifically.
Tom O'Neillc88f13e2009-12-17 12:24:05 -0800130 As well as
131 <a href="{@docRoot}resources/faq/commontasks.html#intentexamples">starting intents</a> in
132 classes we already know about, be they in our own application or another
133 application, we can also create Intents without knowing exactly which
Dirk Dougherty22558d02009-12-10 16:25:06 -0800134 application will handle it.</p>
Scott Main3b3145e2010-03-17 12:39:51 -0700135 <p>For example, we might want to open a page in a
Dirk Dougherty22558d02009-12-10 16:25:06 -0800136 browser, and for this we still use
137 an Intent. But instead of specifying a class to handle it, we use
138 a predefined Intent constant, and a content URI that describes what we
139 want to do. See {@link android.content.Intent
140 android.content.Intent} for more information.</p>
Scott Main3b3145e2010-03-17 12:39:51 -0700141 </div>
142 </div>
Dirk Dougherty22558d02009-12-10 16:25:06 -0800143
144 <p>Fill in the body of the <code>createNote()</code> method:
145 <p>Create a new <code>Intent</code> to create a note
146 (<code>ACTIVITY_CREATE</code>) using the <code>NoteEdit</code> class.
147 Then fire the Intent using the <code>startActivityForResult()</code> method
148 call:</p>
149 <pre style="overflow:auto">
150Intent i = new Intent(this, NoteEdit.class);
151startActivityForResult(i, ACTIVITY_CREATE);</pre>
152 <p>This form of the Intent call targets a specific class in our Activity, in this case
153 <code>NoteEdit</code>. Since the Intent class will need to communicate with the Android
154 operating system to route requests, we also have to provide a Context (<code>this</code>).</p>
155 <p>The <code>startActivityForResult()</code> method fires the Intent in a way that causes a method
Tom O'Neillc88f13e2009-12-17 12:24:05 -0800156 in our Activity to be called when the new Activity is completed. The method in our Activity
Dirk Dougherty22558d02009-12-10 16:25:06 -0800157 that receives the callback is called
158 <code>onActivityResult()</code> and we will implement it in a later step. The other way
159 to call an Activity is using <code>startActivity()</code> but this is a "fire-and-forget" way
160 of calling it &mdash; in this manner, our Activity is not informed when the Activity is completed, and there is
161 no way to return result information from the called Activity with <code>startActivity()</code>.
162 <p>Don't worry about the fact that <code>NoteEdit</code> doesn't exist yet,
163 we will fix that soon. </p>
164 </li>
Tom O'Neillc88f13e2009-12-17 12:24:05 -0800165
Dirk Dougherty22558d02009-12-10 16:25:06 -0800166
167<h2>Step 5</h2>
168
169 <p>Fill in the body of the <code>onListItemClick()</code> override.</p>
170 <p><code>onListItemClick()</code> is a callback method that we'll override. It is called when
171 the user selects an item from the list. It is passed four parameters: the
172 <code>ListView</code> object it was invoked from, the <code>View</code>
173 inside the <code>ListView</code> that was clicked on, the
174 <code>position</code> in the list that was clicked, and the
175 <code>mRowId</code> of the item that was clicked. In this instance we can
176 ignore the first two parameters (we only have one <code>ListView</code> it
177 could be), and we ignore the <code>mRowId</code> as well. All we are
178 interested in is the <code>position</code> that the user selected. We use
179 this to get the data from the correct row, and bundle it up to send to
180 the <code>NoteEdit</code> Activity.</p>
Tom O'Neillc88f13e2009-12-17 12:24:05 -0800181 <p>In our implementation of the callback, the method creates an
Dirk Dougherty22558d02009-12-10 16:25:06 -0800182 <code>Intent</code> to edit the note using
183 the <code>NoteEdit</code> class. It then adds data into the extras Bundle of
184 the Intent, which will be passed to the called Activity. We use it
185 to pass in the title and body text, and the <code>mRowId</code> for the note we are
186 editing. Finally, it will fire the Intent using the
187 <code>startActivityForResult()</code> method call. Here's the code that
188 belongs in <code>onListItemClick()</code>:</p>
189 <pre>
190super.onListItemClick(l, v, position, id);
191Cursor c = mNotesCursor;
192c.moveToPosition(position);
193Intent i = new Intent(this, NoteEdit.class);
194i.putExtra(NotesDbAdapter.KEY_ROWID, id);
195i.putExtra(NotesDbAdapter.KEY_TITLE, c.getString(
196 c.getColumnIndexOrThrow(NotesDbAdapter.KEY_TITLE)));
197i.putExtra(NotesDbAdapter.KEY_BODY, c.getString(
198 c.getColumnIndexOrThrow(NotesDbAdapter.KEY_BODY)));
199startActivityForResult(i, ACTIVITY_EDIT);</pre>
200 <ul>
201 <li>
202 <code>putExtra()</code> is the method to add items into the extras Bundle
203 to pass in to intent invocations. Here, we are
204 using the Bundle to pass in the title, body and mRowId of the note we want to edit.
205 </li>
206 <li>
207 The details of the note are pulled out from our query Cursor, which we move to the
Tom O'Neillc88f13e2009-12-17 12:24:05 -0800208 proper position for the element that was selected in the list, with
Dirk Dougherty22558d02009-12-10 16:25:06 -0800209 the <code>moveToPosition()</code> method.</li>
210 <li>With the extras added to the Intent, we invoke the Intent on the
211 <code>NoteEdit</code> class by passing <code>startActivityForResult()</code>
212 the Intent and the request code. (The request code will be
213 returned to <code>onActivityResult</code> as the <code>requestCode</code> parameter.)</li>
214 </ul>
215 <p class="note"><b>Note:</b> We assign the mNotesCursor field to a local variable at the
216 start of the method. This is done as an optimization of the Android code. Accessing a local
217 variable is much more efficient than accessing a field in the Dalvik VM, so by doing this
218 we make only one access to the field, and five accesses to the local variable, making the
219 routine much more efficient. It is recommended that you use this optimization when possible.</p>
Tom O'Neillc88f13e2009-12-17 12:24:05 -0800220
Dirk Dougherty22558d02009-12-10 16:25:06 -0800221
222<h2>Step 6</h2>
223
224<p>The above <code>createNote()</code> and <code>onListItemClick()</code>
225 methods use an asynchronous Intent invocation. We need a handler for the callback, so here we fill
226 in the body of the <code>onActivityResult()</code>. </p>
227<p><code>onActivityResult()</code> is the overridden method
228 which will be called when an Activity returns with a result. (Remember, an Activity
229 will only return a result if launched with <code>startActivityForResult</code>.) The parameters provided
230 to the callback are: </p>
231 <ul>
232 <li><code>requestCode</code> &mdash; the original request code
233 specified in the Intent invocation (either <code>ACTIVITY_CREATE</code> or
234 <code>ACTIVITY_EDIT</code> for us).
235 </li>
236 <li><code>resultCode</code> &mdash; the result (or error code) of the call, this
237 should be zero if everything was OK, but may have a non-zero code indicating
238 that something failed. There are standard result codes available, and you
239 can also create your own constants to indicate specific problems.
240 </li>
241 <li><code>intent</code> &mdash; this is an Intent created by the Activity returning
242 results. It can be used to return data in the Intent "extras."
243 </li>
Tom O'Neillc88f13e2009-12-17 12:24:05 -0800244 </ul>
Dirk Dougherty22558d02009-12-10 16:25:06 -0800245 <p>The combination of <code>startActivityForResult()</code> and
246 <code>onActivityResult()</code> can be thought of as an asynchronous RPC
247 (remote procedure call) and forms the recommended way for an Activity to invoke
248 another and share services.</p>
249 <p>Here's the code that belongs in your <code>onActivityResult()</code>:</p>
250 <pre>
251super.onActivityResult(requestCode, resultCode, intent);
252Bundle extras = intent.getExtras();
253
254switch(requestCode) {
255case ACTIVITY_CREATE:
256 String title = extras.getString(NotesDbAdapter.KEY_TITLE);
257 String body = extras.getString(NotesDbAdapter.KEY_BODY);
258 mDbHelper.createNote(title, body);
259 fillData();
260 break;
261case ACTIVITY_EDIT:
262 Long mRowId = extras.getLong(NotesDbAdapter.KEY_ROWID);
263 if (mRowId != null) {
264 String editTitle = extras.getString(NotesDbAdapter.KEY_TITLE);
265 String editBody = extras.getString(NotesDbAdapter.KEY_BODY);
266 mDbHelper.updateNote(mRowId, editTitle, editBody);
267 }
268 fillData();
269 break;
270}</pre>
271
272 <ul>
273 <li>
274 We are handling both the <code>ACTIVITY_CREATE</code> and
275 <code>ACTIVITY_EDIT</code> activity results in this method.
276 </li>
277 <li>
Tom O'Neillc88f13e2009-12-17 12:24:05 -0800278 In the case of a create, we pull the title and body from the extras (retrieved from the
Dirk Dougherty22558d02009-12-10 16:25:06 -0800279 returned Intent) and use them to create a new note.
280 </li>
281 <li>
282 In the case of an edit, we pull the mRowId as well, and use that to update
283 the note in the database.
284 </li>
285 <li>
286 <code>fillData()</code> at the end ensures everything is up to date .
287 </li>
288 </ul>
Tom O'Neillc88f13e2009-12-17 12:24:05 -0800289
Dirk Dougherty22558d02009-12-10 16:25:06 -0800290
291<h2>Step 7</h2>
292
Scott Main3b3145e2010-03-17 12:39:51 -0700293 <div class="sidebox-wrapper">
294 <div class="sidebox">
295 <h2>The Art of Layout</h2>
296 <p>The provided
Dirk Dougherty22558d02009-12-10 16:25:06 -0800297 note_edit.xml layout file is the most sophisticated one in the application we will be building,
298 but that doesn't mean it is even close to the kind of sophistication you will be likely to want
299 in real Android applications.</p>
Scott Main3b3145e2010-03-17 12:39:51 -0700300 <p>Creating a
Dirk Dougherty22558d02009-12-10 16:25:06 -0800301 good UI is part art and part science, and the rest is work. Mastery of <a
Scott Mainb10b48f2011-09-13 16:40:52 -0700302 href="{@docRoot}guide/topics/ui/declaring-layout.html">XML Layouts</a> is an essential part of
303creating
Dirk Dougherty22558d02009-12-10 16:25:06 -0800304 a good looking Android application.</p>
Scott Main3b3145e2010-03-17 12:39:51 -0700305 <p>Take a look at the
Dirk Dougherty22558d02009-12-10 16:25:06 -0800306 <a href="{@docRoot}resources/tutorials/views/index.html">Hello Views</a>
307 for some example layouts and how to use them. The ApiDemos sample project is also a
308 great resource from which to learn how to create different layouts.</p>
309 </div>
Scott Main3b3145e2010-03-17 12:39:51 -0700310 </div>
Dirk Dougherty22558d02009-12-10 16:25:06 -0800311
312<p>Open the file <code>note_edit.xml</code> that has been provided and take a
313 look at it. This is the UI code for the Note Editor.</p>
314 <p>This is the most
315 sophisticated UI we have dealt with yet. The file is given to you to avoid
316 problems that may sneak in when typing the code. (The XML is very strict
317 about case sensitivity and structure, mistakes in these are the usual cause
318 of problems with layout.)</p>
319 <p>There is a new parameter used
320 here that we haven't seen before: <code>android:layout_weight</code> (in
321 this case set to use the value 1 in each case).</p>
322 <p><code>layout_weight</code> is used in LinearLayouts
323 to assign "importance" to Views within the layout. All Views have a default
324 <code>layout_weight</code> of zero, meaning they take up only as much room
325 on the screen as they need to be displayed. Assigning a value higher than
326 zero will split up the rest of the available space in the parent View, according
327 to the value of each View's <code>layout_weight</code> and its ratio to the
328 overall <code>layout_weight</code> specified in the current layout for this
329 and other View elements.</p>
330 <p>To give an example: let's say we have a text label
331 and two text edit elements in a horizontal row. The label has no
332 <code>layout_weight</code> specified, so it takes up the minimum space
333 required to render. If the <code>layout_weight</code> of each of the two
334 text edit elements is set to 1, the remaining width in the parent layout will
Tom O'Neillc88f13e2009-12-17 12:24:05 -0800335 be split equally between them (because we claim they are equally important).
Dirk Dougherty22558d02009-12-10 16:25:06 -0800336 If the first one has a <code>layout_weight</code> of 1
337 and the second has a <code>layout_weight</code> of 2, then one third of the
338 remaining space will be given to the first, and two thirds to the
339 second (because we claim the second one is more important).</p>
340 <p>This layout also demonstrates how to nest multiple layouts
341 inside each other to achieve a more complex and pleasant layout. In this
342 example, a horizontal linear layout is nested inside the vertical one to
343 allow the title label and text field to be alongside each other,
344 horizontally.</p>
345
346
347<h2 style="clear:right;">Step 8</h2>
348
349 <p>Create a <code>NoteEdit</code> class that extends
350 <code>android.app.Activity</code>.</p>
351 <p>This is the first time we will have
352 created an Activity without the Android Eclipse plugin doing it for us. When
353 you do so, the <code>onCreate()</code> method is not automatically
354 overridden for you. It is hard to imagine an Activity that doesn't override
355 the <code>onCreate()</code> method, so this should be the first thing you do.</p>
356 <ol>
357 <li>Right click on the <code>com.android.demo.notepad2</code> package
358 in the Package Explorer, and select <strong>New</strong> &gt; <strong>Class</strong> from the popup
359 menu.</li>
360 <li>Fill in <code>NoteEdit</code> for the <code>Name:</code> field in the
361 dialog.</li>
362 <li>In the <code>Superclass:</code> field, enter
363 <code>android.app.Activity</code> (you can also just type Activity and hit
364 Ctrl-Space on Windows and Linux or Cmd-Space on the Mac, to invoke code
365 assist and find the right package and class).</li>
366 <li>Click <strong>Finish</strong>.</li>
367 <li>In the resulting <code>NoteEdit</code> class, right click in the editor
368 window and select <strong>Source</strong> &gt; <strong>Override/Implement Methods...</strong></li>
369 <li>Scroll down through the checklist in the dialog until you see
370 <code>onCreate(Bundle)</code> &mdash; and check the box next to it.</li>
371 <li>Click <strong>OK</strong>.<p>The method should now appear in your class.</p></li>
372 </ol>
Tom O'Neillc88f13e2009-12-17 12:24:05 -0800373
Dirk Dougherty22558d02009-12-10 16:25:06 -0800374<h2>Step 9</h2>
375
376<p>Fill in the body of the <code>onCreate()</code> method for <code>NoteEdit</code>.</p>
377
378<p>This will set the title of our new Activity to say "Edit Note" (one
379 of the strings defined in <code>strings.xml</code>). It will also set the
380 content view to use our <code>note_edit.xml</code> layout file. We can then
381 grab handles to the title and body text edit views, and the confirm button,
382 so that our class can use them to set and get the note title and body,
383 and attach an event to the confirm button for when it is pressed by the
384 user.</p>
385 <p>We can then unbundle the values that were passed in to the Activity
386 with the extras Bundle attached to the calling Intent. We'll use them to pre-populate
387 the title and body text edit views so that the user can edit them.
Tom O'Neillc88f13e2009-12-17 12:24:05 -0800388 Then we will grab and store the <code>mRowId</code> so we can keep
Dirk Dougherty22558d02009-12-10 16:25:06 -0800389 track of what note the user is editing.</p>
390
391 <ol>
392 <li>
393 Inside <code>onCreate()</code>, set up the layout:<br>
394 <pre>setContentView(R.layout.note_edit);</pre>
395 </li>
396 <li>
Scott Maina99839c2010-06-17 10:07:25 -0700397 Change the Activity title to the "Edit Note" string:
398 <pre>setTitle(R.string.edit_note);</pre>
399 </li>
400 <li>
401 Find the {@link android.widget.EditText} and {@link android.widget.Button} components we need:
Dirk Dougherty22558d02009-12-10 16:25:06 -0800402 <p>These are found by the
403 IDs associated to them in the R class, and need to be cast to the right
404 type of <code>View</code> (<code>EditText</code> for the two text views,
405 and <code>Button</code> for the confirm button):</p>
406 <pre>
407mTitleText = (EditText) findViewById(R.id.title);
408mBodyText = (EditText) findViewById(R.id.body);
409Button confirmButton = (Button) findViewById(R.id.confirm);</pre>
Tom O'Neillc88f13e2009-12-17 12:24:05 -0800410 <p>Note that <code>mTitleText</code> and <code>mBodyText</code> are member
Dirk Dougherty22558d02009-12-10 16:25:06 -0800411 fields (you need to declare them at the top of the class definition).</p>
412 </li>
413 <li>At the top of the class, declare a <code>Long mRowId</code> private field to store
414 the current <code>mRowId</code> being edited (if any).
415 </li>
Tom O'Neillc88f13e2009-12-17 12:24:05 -0800416 <li>Continuing inside <code>onCreate()</code>,
417 add code to initialize the <code>title</code>, <code>body</code> and
Dirk Dougherty22558d02009-12-10 16:25:06 -0800418 <code>mRowId</code> from the extras Bundle in
419 the Intent (if it is present):<br>
420 <pre>
421mRowId = null;
422Bundle extras = getIntent().getExtras();
423if (extras != null) {
424 String title = extras.getString(NotesDbAdapter.KEY_TITLE);
425 String body = extras.getString(NotesDbAdapter.KEY_BODY);
426 mRowId = extras.getLong(NotesDbAdapter.KEY_ROWID);
Tom O'Neillc88f13e2009-12-17 12:24:05 -0800427
Dirk Dougherty22558d02009-12-10 16:25:06 -0800428 if (title != null) {
429 mTitleText.setText(title);
430 }
431 if (body != null) {
432 mBodyText.setText(body);
433 }
434}</pre>
435 <ul>
436 <li>
437 We are pulling the <code>title</code> and
438 <code>body</code> out of the
439 <code>extras</code> Bundle that was set from the
440 Intent invocation.
441 </li><li>
442 We also null-protect the text field setting (i.e., we don't want to set
443 the text fields to null accidentally).</li>
444 </ul>
445 </li>
446 <li>
447 Create an <code>onClickListener()</code> for the button:
448 <p>Listeners can be one of the more confusing aspects of UI
449 implementation, but
450 what we are trying to achieve in this case is simple. We want an
451 <code>onClick()</code> method to be called when the user presses the
452 confirm button, and use that to do some work and return the values
453 of the edited note to the Intent caller. We do this using something called
454 an anonymous inner class. This is a bit confusing to look at unless you
455 have seen them before, but all you really need to take away from this is
456 that you can refer to this code in the future to see how to create a
457 listener and attach it to a button. (Listeners are a common idiom
458 in Java development, particularly for user interfaces.) Here's the empty listener:<br>
459 <pre>
460confirmButton.setOnClickListener(new View.OnClickListener() {
461
462 public void onClick(View view) {
Tom O'Neillc88f13e2009-12-17 12:24:05 -0800463
Dirk Dougherty22558d02009-12-10 16:25:06 -0800464 }
Tom O'Neillc88f13e2009-12-17 12:24:05 -0800465
Dirk Dougherty22558d02009-12-10 16:25:06 -0800466});</pre>
467 </li>
468 </ol>
469<h2>Step 10</h2>
470
471<p>Fill in the body of the <code>onClick()</code> method of the <code>OnClickListener</code> created in the last step.</p>
Tom O'Neillc88f13e2009-12-17 12:24:05 -0800472
Dirk Dougherty22558d02009-12-10 16:25:06 -0800473 <p>This is the code that will be run when the user clicks on the
474 confirm button. We want this to grab the title and body text from the edit
475 text fields, and put them into the return Bundle so that they can be passed
476 back to the Activity that invoked this <code>NoteEdit</code> Activity. If the
477 operation is an edit rather than a create, we also want to put the
478 <code>mRowId</code> into the Bundle so that the
479 <code>Notepadv2</code> class can save the changes back to the correct
480 note.</p>
481 <ol>
482 <li>
483 Create a <code>Bundle</code> and put the title and body text into it using the
484 constants defined in Notepadv2 as keys:<br>
485 <pre>
486Bundle bundle = new Bundle();
Tom O'Neillc88f13e2009-12-17 12:24:05 -0800487
Dirk Dougherty22558d02009-12-10 16:25:06 -0800488bundle.putString(NotesDbAdapter.KEY_TITLE, mTitleText.getText().toString());
489bundle.putString(NotesDbAdapter.KEY_BODY, mBodyText.getText().toString());
490if (mRowId != null) {
491 bundle.putLong(NotesDbAdapter.KEY_ROWID, mRowId);
492}</pre>
493 </li>
494 <li>
495 Set the result information (the Bundle) in a new Intent and finish the Activity:
496 <pre>
497Intent mIntent = new Intent();
498mIntent.putExtras(bundle);
499setResult(RESULT_OK, mIntent);
500finish();</pre>
501 <ul>
Tom O'Neillc88f13e2009-12-17 12:24:05 -0800502 <li>The Intent is simply our data carrier that carries our Bundle
Dirk Dougherty22558d02009-12-10 16:25:06 -0800503 (with the title, body and mRowId).</li>
504 <li>The <code>setResult()</code> method is used to set the result
505 code and return Intent to be passed back to the
506 Intent caller. In this case everything worked, so we return RESULT_OK for the
507 result code.</li>
508 <li>The <code>finish()</code> call is used to signal that the Activity
509 is done (like a return call). Anything set in the Result will then be
510 returned to the caller, along with execution control.</li>
511 </ul>
512 </li>
513 </ol>
514 <p>The full <code>onCreate()</code> method (plus supporting class fields) should
515 now look like this:</p>
516 <pre>
517private EditText mTitleText;
518private EditText mBodyText;
519private Long mRowId;
520
521&#64;Override
522protected void onCreate(Bundle savedInstanceState) {
523 super.onCreate(savedInstanceState);
524 setContentView(R.layout.note_edit);
Tom O'Neillc88f13e2009-12-17 12:24:05 -0800525
Dirk Dougherty22558d02009-12-10 16:25:06 -0800526 mTitleText = (EditText) findViewById(R.id.title);
527 mBodyText = (EditText) findViewById(R.id.body);
Tom O'Neillc88f13e2009-12-17 12:24:05 -0800528
Dirk Dougherty22558d02009-12-10 16:25:06 -0800529 Button confirmButton = (Button) findViewById(R.id.confirm);
Tom O'Neillc88f13e2009-12-17 12:24:05 -0800530
Dirk Dougherty22558d02009-12-10 16:25:06 -0800531 mRowId = null;
532 Bundle extras = getIntent().getExtras();
533 if (extras != null) {
534 String title = extras.getString(NotesDbAdapter.KEY_TITLE);
535 String body = extras.getString(NotesDbAdapter.KEY_BODY);
536 mRowId = extras.getLong(NotesDbAdapter.KEY_ROWID);
Tom O'Neillc88f13e2009-12-17 12:24:05 -0800537
Dirk Dougherty22558d02009-12-10 16:25:06 -0800538 if (title != null) {
539 mTitleText.setText(title);
540 }
541 if (body != null) {
542 mBodyText.setText(body);
543 }
544 }
Tom O'Neillc88f13e2009-12-17 12:24:05 -0800545
Dirk Dougherty22558d02009-12-10 16:25:06 -0800546 confirmButton.setOnClickListener(new View.OnClickListener() {
547
548 public void onClick(View view) {
549 Bundle bundle = new Bundle();
Tom O'Neillc88f13e2009-12-17 12:24:05 -0800550
Dirk Dougherty22558d02009-12-10 16:25:06 -0800551 bundle.putString(NotesDbAdapter.KEY_TITLE, mTitleText.getText().toString());
552 bundle.putString(NotesDbAdapter.KEY_BODY, mBodyText.getText().toString());
553 if (mRowId != null) {
554 bundle.putLong(NotesDbAdapter.KEY_ROWID, mRowId);
555 }
556
557 Intent mIntent = new Intent();
558 mIntent.putExtras(bundle);
559 setResult(RESULT_OK, mIntent);
560 finish();
561 }
562 });
563}</pre>
564 </li>
565 </ol>
Tom O'Neillc88f13e2009-12-17 12:24:05 -0800566
Dirk Dougherty22558d02009-12-10 16:25:06 -0800567<h2>Step 11</h2>
568
Scott Main3b3145e2010-03-17 12:39:51 -0700569 <div class="sidebox-wrapper">
570 <div class="sidebox">
571 <h2>The All-Important Android Manifest File</h2>
572 <p>The AndroidManifest.xml file is the way in which Android sees your
Dirk Dougherty22558d02009-12-10 16:25:06 -0800573 application. This file defines the category of the application, where
574 it shows up (or even if it shows up) in the launcher or settings, what
575 activities, services, and content providers it defines, what intents it can
576 receive, and more. </p>
Scott Main3b3145e2010-03-17 12:39:51 -0700577 <p>For more information, see the reference document
578 <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">The AndroidManifest.xml
579File</a></p>
580 </div>
Tom O'Neillc88f13e2009-12-17 12:24:05 -0800581 </div>
Dirk Dougherty22558d02009-12-10 16:25:06 -0800582
583<p>Finally, the new Activity has to be defined in the manifest file:</p>
584 <p>Before the new Activity can be seen by Android, it needs its own
585 Activity entry in the <code>AndroidManifest.xml</code> file. This is to let
586 the system know that it is there and can be called. We could also specify
587 which IntentFilters the activity implements here, but we are going to skip
588 this for now and just let Android know that the Activity is
589 defined.</p>
590 <p>There is a Manifest editor included in the Eclipse plugin that makes it much easier
591 to edit the AndroidManifest file, and we will use this. If you prefer to edit the file directly
592 or are not using the Eclipse plugin, see the box at the end for information on how to do this
593 without using the new Manifest editor.<p>
594 <ol>
595 <li>Double click on the <code>AndroidManifest.xml</code> file in the package explorer to open it.
596 </li>
597 <li>Click the <strong>Application</strong> tab at the bottom of the Manifest editor.</li>
598 <li>Click <strong>Add...</strong> in the Application Nodes section.
Tom O'Neillc88f13e2009-12-17 12:24:05 -0800599 <p>If you see a dialog with radiobuttons at the top, select the top radio button:
Dirk Dougherty22558d02009-12-10 16:25:06 -0800600 "Create a new element at the top level, in Application".</p></li>
601 <li>Make sure "(A) Activity" is selected in the selection pane of the dialog, and click <strong>OK</strong>.</li>
Tom O'Neillc88f13e2009-12-17 12:24:05 -0800602 <li>Click on the new "Activity" node, in the Application Nodes section, then
Dirk Dougherty22558d02009-12-10 16:25:06 -0800603 type <code>.NoteEdit</code> into the <em>Name*</em>
604 field to the right. Press Return/Enter.</li>
605 </ol>
606 <p>The Android Manifest editor helps you add more complex entries into the AndroidManifest.xml
607 file, have a look around at some of the other options available (but be careful not to select
608 them otherwise they will be added to your Manifest). This editor should help you understand
609 and alter the AndroidManifest.xml file as you move on to more advanced Android applications.</p>
Tom O'Neillc88f13e2009-12-17 12:24:05 -0800610
Dirk Dougherty22558d02009-12-10 16:25:06 -0800611 <p class="note">If you prefer to edit this file directly, simply open the
612 <code>AndroidManifest.xml</code> file and look at the source (use the
613 <code>AndroidManifest.xml</code> tab in the eclipse editor to see the source code directly).
614 Then edit the file as follows:<br>
Tom O'Neillc88f13e2009-12-17 12:24:05 -0800615 <code>&lt;activity android:name=".NoteEdit" /&gt;</code><br><br>
Dirk Dougherty22558d02009-12-10 16:25:06 -0800616 This should be placed just below the line that reads:<br>
617 <code>&lt;/activity&gt;</code> for the <code>.Notepadv2</code> activity.</p>
618
619<h2 style="clear:right;">Step 12</h2>
620
621<p>Now Run it!</p>
Tom O'Neillc88f13e2009-12-17 12:24:05 -0800622<p>You should now be able to add real notes from
623the menu, as well as delete an existing one. Notice that in order to delete, you must
Dirk Dougherty22558d02009-12-10 16:25:06 -0800624first use the directional controls on the device to highlight the note.
Tom O'Neillc88f13e2009-12-17 12:24:05 -0800625Furthermore, selecting a note title from the list should bring up the note
626editor to let you edit it. Press confirm when finished to save the changes
Dirk Dougherty22558d02009-12-10 16:25:06 -0800627back to the database.
628
629<h2>Solution and Next Steps</h2>
630
Tom O'Neillc88f13e2009-12-17 12:24:05 -0800631<p>You can see the solution to this exercise in <code>Notepadv2Solution</code>
Dirk Dougherty22558d02009-12-10 16:25:06 -0800632from the zip file to compare with your own.</p>
633<p>Now try editing a note, and then hitting the back button on the emulator
634instead of the confirm button (the back button is below the menu button). You
635will see an error come up. Clearly our application still has some problems.
636Worse still, if you did make some changes and hit the back button, when you go
637back into the notepad to look at the note you changed, you will find that all
638your changes have been lost. In the next exercise we will fix these
639problems.</p>
640
641<p>
642Once you are ready, move on to <a href="notepad-ex3.html">Tutorial
643Exercise 3</a> where you will fix the problems with the back button and lost
644edits by introducing a proper life cycle into the NoteEdit Activity.</p>
645
646