646 lines
30 KiB
Plaintext
646 lines
30 KiB
Plaintext
|
Rpage.title=Notepad Exercise 2
|
||
|
parent.title=Notepad Tutorial
|
||
|
parent.link=index.html
|
||
|
@jd:body
|
||
|
|
||
|
|
||
|
<p><em>In this exercise, you will add a second Activity to your notepad application, to let the user
|
||
|
create and edit notes. You will also allow the user to delete existing notes through a context menu.
|
||
|
The new Activity assumes responsibility for creating new notes by
|
||
|
collecting user input and packing it into a return Bundle provided by the intent. This exercise
|
||
|
demonstrates:</em></p>
|
||
|
<ul>
|
||
|
<li><em>Constructing a new Activity and adding it to the Android manifest</em></li>
|
||
|
<li><em>Invoking another Activity asynchronously using <code>startActivityForResult()</code></em></li>
|
||
|
<li><em>Passing data between Activity in Bundle objects</em></li>
|
||
|
<li><em>How to use a more advanced screen layout</em></li>
|
||
|
<li><em>How to create a context menu</em></li>
|
||
|
</ul>
|
||
|
|
||
|
<div style="float:right;white-space:nowrap">
|
||
|
[<a href="notepad-ex1.html">Exercise 1</a>]
|
||
|
<span style="color:#BBB;">
|
||
|
[<a href="notepad-ex2.html" style="color:#DDD;">Exercise 2</a>]
|
||
|
</span>
|
||
|
[<a href="notepad-ex3.html">Exercise 3</a>]
|
||
|
[<a href="notepad-extra-credit.html">Extra Credit</a>]
|
||
|
</div>
|
||
|
|
||
|
<h2>Step 1</h2>
|
||
|
|
||
|
<p>Create a new Android project using the sources from <code>Notepadv2</code> under the
|
||
|
<code>NotepadCodeLab</code> folder, just like you did for the first exercise. If you see an error about
|
||
|
<code>AndroidManifest.xml</code>, or some problems related to an
|
||
|
<code>android.zip</code> file, right click on the project and select <strong>Android
|
||
|
Tools</strong> > <strong>Fix Project Properties</strong>.</p>
|
||
|
|
||
|
<p>Open the <code>Notepadv2</code> project and take a look around:</p>
|
||
|
<ul>
|
||
|
<li>
|
||
|
Open and look at the <code>strings.xml</code> file under
|
||
|
<code>res/values</code> — there are several new strings which we will use
|
||
|
for our new functionality
|
||
|
</li>
|
||
|
<li>
|
||
|
Also, open and take a look at the top of the <code>Notepadv2</code> class,
|
||
|
you will notice several new constants have been defined along with a new <code>mNotesCursor</code>
|
||
|
field used to hold the cursor we are using.
|
||
|
</li>
|
||
|
<li>
|
||
|
Note also that the <code>fillData()</code> method has a few more comments and now uses
|
||
|
the new field to store the notes Cursor. The <code>onCreate()</code> method is
|
||
|
unchanged from the first exercise. Also notice that the member field used to store the
|
||
|
notes Cursor is now called <code>mNotesCursor</code>. The <code>m</code> denotes a member
|
||
|
field and is part of the Android coding style standards.
|
||
|
</li>
|
||
|
<li>
|
||
|
There are also a couple of new overridden methods
|
||
|
(<code>onCreateContextMenu()</code>, <code>onContextItemSelected()</code>,
|
||
|
<code>onListItemClick()</code> and <code>onActivityResult()</code>)
|
||
|
which we will be filling in below.
|
||
|
</li>
|
||
|
</ul>
|
||
|
|
||
|
|
||
|
<h2>Step 2</h2>
|
||
|
<div class="sidebox-wrapper">
|
||
|
<div class="sidebox">
|
||
|
<p>Context menus should always be used when performing actions upon specific elements in the UI.
|
||
|
When you register a View to a context menu, the context menu is revealed by performing a "long-click"
|
||
|
on the UI component (press and hold the touchscreen or highlight and hold down the selection key for about two seconds).</p>
|
||
|
</div>
|
||
|
</div>
|
||
|
|
||
|
<p>First, let's create the context menu that will allow users to delete individual notes.
|
||
|
Open the Notepadv2 class.</p>
|
||
|
|
||
|
<ol>
|
||
|
<li>In order for each list item in the ListView to register for the context menu, we call
|
||
|
<code>registerForContextMenu()</code> and pass it our ListView. So, at the very end of
|
||
|
the <code>onCreate()</code> method add this line:
|
||
|
<pre>registerForContextMenu(getListView());</pre>
|
||
|
<p>Because our Activity extends the ListActivity class, <code>getListView()</code> will return us
|
||
|
the local ListView object for the Activity. Now, each list item in this ListView will activate the
|
||
|
context menu.
|
||
|
<li>
|
||
|
Now fill in the <code>onCreateContextMenu()</code> method. This callback is similar to the other
|
||
|
menu callback used for the options menu. Here, we add just one line, which will add a menu item
|
||
|
to delete a note. Call <code>menu.add()</code> like so:
|
||
|
<pre>
|
||
|
public boolean onCreateContextMenu(Menu menu, View v
|
||
|
ContextMenuInfo menuInfo) {
|
||
|
super.onCreateContextMenu(menu, v, menuInfo);
|
||
|
menu.add(0, DELETE_ID, 0, R.string.menu_delete);
|
||
|
}</pre>
|
||
|
<p>The <code>onCreateContextMenu()</code> callback passes some other information in addition to the Menu object,
|
||
|
such as the View that has been triggered for the menu and
|
||
|
an extra object that may contain additional information about the object selected. However, we don't care about
|
||
|
these here, because we only have one kind of object in the Activity that uses context menus. In the next
|
||
|
step, we'll handle the menu item selection.</p>
|
||
|
</li>
|
||
|
</ol>
|
||
|
|
||
|
<h2>Step 3</h2>
|
||
|
<p>Now that the we've registered our ListView for a context menu and defined our context menu item, we need
|
||
|
to handle the callback when it is selected. For this, we need to identify the list ID of the
|
||
|
selected item, then delete it. So fill in the
|
||
|
<code>onContextItemSelected()</code> method like this:</p>
|
||
|
<pre>
|
||
|
public boolean onContextItemSelected(MenuItem item) {
|
||
|
switch(item.getItemId()) {
|
||
|
case DELETE_ID:
|
||
|
AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
|
||
|
mDbHelper.deleteNote(info.id);
|
||
|
fillData();
|
||
|
return true;
|
||
|
}
|
||
|
return super.onContextItemSelected(item);
|
||
|
}</pre>
|
||
|
<p>Here, we retrieve the {@link android.widget.AdapterView.AdapterContextMenuInfo AdapterContextMenuInfo}
|
||
|
with {@link android.view.MenuItem#getMenuInfo()}. The <var>id</var> field of this object tells us
|
||
|
the position of the item in the ListView. We then pass this to the <code>deleteNote()</code>
|
||
|
method of our NotesDbAdapter and the note is deleted. That's it for the context menu — notes
|
||
|
can now be deleted.</p>
|
||
|
|
||
|
<h2 style="clear:right;">Step 4</h2>
|
||
|
<div class="sidebox-wrapper">
|
||
|
<div class="sidebox">
|
||
|
<h2>Starting Other Activities</h2>
|
||
|
<p>In this example our Intent uses a class name specifically.
|
||
|
As well as
|
||
|
<a href="{@docRoot}resources/faq/commontasks.html#intentexamples">starting intents</a> in
|
||
|
classes we already know about, be they in our own application or another
|
||
|
application, we can also create Intents without knowing exactly which
|
||
|
application will handle it.</p>
|
||
|
<p>For example, we might want to open a page in a
|
||
|
browser, and for this we still use
|
||
|
an Intent. But instead of specifying a class to handle it, we use
|
||
|
a predefined Intent constant, and a content URI that describes what we
|
||
|
want to do. See {@link android.content.Intent
|
||
|
android.content.Intent} for more information.</p>
|
||
|
</div>
|
||
|
</div>
|
||
|
|
||
|
<p>Fill in the body of the <code>createNote()</code> method:
|
||
|
<p>Create a new <code>Intent</code> to create a note
|
||
|
(<code>ACTIVITY_CREATE</code>) using the <code>NoteEdit</code> class.
|
||
|
Then fire the Intent using the <code>startActivityForResult()</code> method
|
||
|
call:</p>
|
||
|
<pre style="overflow:auto">
|
||
|
Intent i = new Intent(this, NoteEdit.class);
|
||
|
startActivityForResult(i, ACTIVITY_CREATE);</pre>
|
||
|
<p>This form of the Intent call targets a specific class in our Activity, in this case
|
||
|
<code>NoteEdit</code>. Since the Intent class will need to communicate with the Android
|
||
|
operating system to route requests, we also have to provide a Context (<code>this</code>).</p>
|
||
|
<p>The <code>startActivityForResult()</code> method fires the Intent in a way that causes a method
|
||
|
in our Activity to be called when the new Activity is completed. The method in our Activity
|
||
|
that receives the callback is called
|
||
|
<code>onActivityResult()</code> and we will implement it in a later step. The other way
|
||
|
to call an Activity is using <code>startActivity()</code> but this is a "fire-and-forget" way
|
||
|
of calling it — in this manner, our Activity is not informed when the Activity is completed, and there is
|
||
|
no way to return result information from the called Activity with <code>startActivity()</code>.
|
||
|
<p>Don't worry about the fact that <code>NoteEdit</code> doesn't exist yet,
|
||
|
we will fix that soon. </p>
|
||
|
</li>
|
||
|
|
||
|
|
||
|
<h2>Step 5</h2>
|
||
|
|
||
|
<p>Fill in the body of the <code>onListItemClick()</code> override.</p>
|
||
|
<p><code>onListItemClick()</code> is a callback method that we'll override. It is called when
|
||
|
the user selects an item from the list. It is passed four parameters: the
|
||
|
<code>ListView</code> object it was invoked from, the <code>View</code>
|
||
|
inside the <code>ListView</code> that was clicked on, the
|
||
|
<code>position</code> in the list that was clicked, and the
|
||
|
<code>mRowId</code> of the item that was clicked. In this instance we can
|
||
|
ignore the first two parameters (we only have one <code>ListView</code> it
|
||
|
could be), and we ignore the <code>mRowId</code> as well. All we are
|
||
|
interested in is the <code>position</code> that the user selected. We use
|
||
|
this to get the data from the correct row, and bundle it up to send to
|
||
|
the <code>NoteEdit</code> Activity.</p>
|
||
|
<p>In our implementation of the callback, the method creates an
|
||
|
<code>Intent</code> to edit the note using
|
||
|
the <code>NoteEdit</code> class. It then adds data into the extras Bundle of
|
||
|
the Intent, which will be passed to the called Activity. We use it
|
||
|
to pass in the title and body text, and the <code>mRowId</code> for the note we are
|
||
|
editing. Finally, it will fire the Intent using the
|
||
|
<code>startActivityForResult()</code> method call. Here's the code that
|
||
|
belongs in <code>onListItemClick()</code>:</p>
|
||
|
<pre>
|
||
|
super.onListItemClick(l, v, position, id);
|
||
|
Cursor c = mNotesCursor;
|
||
|
c.moveToPosition(position);
|
||
|
Intent i = new Intent(this, NoteEdit.class);
|
||
|
i.putExtra(NotesDbAdapter.KEY_ROWID, id);
|
||
|
i.putExtra(NotesDbAdapter.KEY_TITLE, c.getString(
|
||
|
c.getColumnIndexOrThrow(NotesDbAdapter.KEY_TITLE)));
|
||
|
i.putExtra(NotesDbAdapter.KEY_BODY, c.getString(
|
||
|
c.getColumnIndexOrThrow(NotesDbAdapter.KEY_BODY)));
|
||
|
startActivityForResult(i, ACTIVITY_EDIT);</pre>
|
||
|
<ul>
|
||
|
<li>
|
||
|
<code>putExtra()</code> is the method to add items into the extras Bundle
|
||
|
to pass in to intent invocations. Here, we are
|
||
|
using the Bundle to pass in the title, body and mRowId of the note we want to edit.
|
||
|
</li>
|
||
|
<li>
|
||
|
The details of the note are pulled out from our query Cursor, which we move to the
|
||
|
proper position for the element that was selected in the list, with
|
||
|
the <code>moveToPosition()</code> method.</li>
|
||
|
<li>With the extras added to the Intent, we invoke the Intent on the
|
||
|
<code>NoteEdit</code> class by passing <code>startActivityForResult()</code>
|
||
|
the Intent and the request code. (The request code will be
|
||
|
returned to <code>onActivityResult</code> as the <code>requestCode</code> parameter.)</li>
|
||
|
</ul>
|
||
|
<p class="note"><b>Note:</b> We assign the mNotesCursor field to a local variable at the
|
||
|
start of the method. This is done as an optimization of the Android code. Accessing a local
|
||
|
variable is much more efficient than accessing a field in the Dalvik VM, so by doing this
|
||
|
we make only one access to the field, and five accesses to the local variable, making the
|
||
|
routine much more efficient. It is recommended that you use this optimization when possible.</p>
|
||
|
|
||
|
|
||
|
<h2>Step 6</h2>
|
||
|
|
||
|
<p>The above <code>createNote()</code> and <code>onListItemClick()</code>
|
||
|
methods use an asynchronous Intent invocation. We need a handler for the callback, so here we fill
|
||
|
in the body of the <code>onActivityResult()</code>. </p>
|
||
|
<p><code>onActivityResult()</code> is the overridden method
|
||
|
which will be called when an Activity returns with a result. (Remember, an Activity
|
||
|
will only return a result if launched with <code>startActivityForResult</code>.) The parameters provided
|
||
|
to the callback are: </p>
|
||
|
<ul>
|
||
|
<li><code>requestCode</code> — the original request code
|
||
|
specified in the Intent invocation (either <code>ACTIVITY_CREATE</code> or
|
||
|
<code>ACTIVITY_EDIT</code> for us).
|
||
|
</li>
|
||
|
<li><code>resultCode</code> — the result (or error code) of the call, this
|
||
|
should be zero if everything was OK, but may have a non-zero code indicating
|
||
|
that something failed. There are standard result codes available, and you
|
||
|
can also create your own constants to indicate specific problems.
|
||
|
</li>
|
||
|
<li><code>intent</code> — this is an Intent created by the Activity returning
|
||
|
results. It can be used to return data in the Intent "extras."
|
||
|
</li>
|
||
|
</ul>
|
||
|
<p>The combination of <code>startActivityForResult()</code> and
|
||
|
<code>onActivityResult()</code> can be thought of as an asynchronous RPC
|
||
|
(remote procedure call) and forms the recommended way for an Activity to invoke
|
||
|
another and share services.</p>
|
||
|
<p>Here's the code that belongs in your <code>onActivityResult()</code>:</p>
|
||
|
<pre>
|
||
|
super.onActivityResult(requestCode, resultCode, intent);
|
||
|
Bundle extras = intent.getExtras();
|
||
|
|
||
|
switch(requestCode) {
|
||
|
case ACTIVITY_CREATE:
|
||
|
String title = extras.getString(NotesDbAdapter.KEY_TITLE);
|
||
|
String body = extras.getString(NotesDbAdapter.KEY_BODY);
|
||
|
mDbHelper.createNote(title, body);
|
||
|
fillData();
|
||
|
break;
|
||
|
case ACTIVITY_EDIT:
|
||
|
Long mRowId = extras.getLong(NotesDbAdapter.KEY_ROWID);
|
||
|
if (mRowId != null) {
|
||
|
String editTitle = extras.getString(NotesDbAdapter.KEY_TITLE);
|
||
|
String editBody = extras.getString(NotesDbAdapter.KEY_BODY);
|
||
|
mDbHelper.updateNote(mRowId, editTitle, editBody);
|
||
|
}
|
||
|
fillData();
|
||
|
break;
|
||
|
}</pre>
|
||
|
|
||
|
<ul>
|
||
|
<li>
|
||
|
We are handling both the <code>ACTIVITY_CREATE</code> and
|
||
|
<code>ACTIVITY_EDIT</code> activity results in this method.
|
||
|
</li>
|
||
|
<li>
|
||
|
In the case of a create, we pull the title and body from the extras (retrieved from the
|
||
|
returned Intent) and use them to create a new note.
|
||
|
</li>
|
||
|
<li>
|
||
|
In the case of an edit, we pull the mRowId as well, and use that to update
|
||
|
the note in the database.
|
||
|
</li>
|
||
|
<li>
|
||
|
<code>fillData()</code> at the end ensures everything is up to date .
|
||
|
</li>
|
||
|
</ul>
|
||
|
|
||
|
|
||
|
<h2>Step 7</h2>
|
||
|
|
||
|
<div class="sidebox-wrapper">
|
||
|
<div class="sidebox">
|
||
|
<h2>The Art of Layout</h2>
|
||
|
<p>The provided
|
||
|
note_edit.xml layout file is the most sophisticated one in the application we will be building,
|
||
|
but that doesn't mean it is even close to the kind of sophistication you will be likely to want
|
||
|
in real Android applications.</p>
|
||
|
<p>Creating a
|
||
|
good UI is part art and part science, and the rest is work. Mastery of <a
|
||
|
href="{@docRoot}guide/topics/ui/declaring-layout.html">Declaring Layout</a> is an essential part of creating
|
||
|
a good looking Android application.</p>
|
||
|
<p>Take a look at the
|
||
|
<a href="{@docRoot}resources/tutorials/views/index.html">Hello Views</a>
|
||
|
for some example layouts and how to use them. The ApiDemos sample project is also a
|
||
|
great resource from which to learn how to create different layouts.</p>
|
||
|
</div>
|
||
|
</div>
|
||
|
|
||
|
<p>Open the file <code>note_edit.xml</code> that has been provided and take a
|
||
|
look at it. This is the UI code for the Note Editor.</p>
|
||
|
<p>This is the most
|
||
|
sophisticated UI we have dealt with yet. The file is given to you to avoid
|
||
|
problems that may sneak in when typing the code. (The XML is very strict
|
||
|
about case sensitivity and structure, mistakes in these are the usual cause
|
||
|
of problems with layout.)</p>
|
||
|
<p>There is a new parameter used
|
||
|
here that we haven't seen before: <code>android:layout_weight</code> (in
|
||
|
this case set to use the value 1 in each case).</p>
|
||
|
<p><code>layout_weight</code> is used in LinearLayouts
|
||
|
to assign "importance" to Views within the layout. All Views have a default
|
||
|
<code>layout_weight</code> of zero, meaning they take up only as much room
|
||
|
on the screen as they need to be displayed. Assigning a value higher than
|
||
|
zero will split up the rest of the available space in the parent View, according
|
||
|
to the value of each View's <code>layout_weight</code> and its ratio to the
|
||
|
overall <code>layout_weight</code> specified in the current layout for this
|
||
|
and other View elements.</p>
|
||
|
<p>To give an example: let's say we have a text label
|
||
|
and two text edit elements in a horizontal row. The label has no
|
||
|
<code>layout_weight</code> specified, so it takes up the minimum space
|
||
|
required to render. If the <code>layout_weight</code> of each of the two
|
||
|
text edit elements is set to 1, the remaining width in the parent layout will
|
||
|
be split equally between them (because we claim they are equally important).
|
||
|
If the first one has a <code>layout_weight</code> of 1
|
||
|
and the second has a <code>layout_weight</code> of 2, then one third of the
|
||
|
remaining space will be given to the first, and two thirds to the
|
||
|
second (because we claim the second one is more important).</p>
|
||
|
<p>This layout also demonstrates how to nest multiple layouts
|
||
|
inside each other to achieve a more complex and pleasant layout. In this
|
||
|
example, a horizontal linear layout is nested inside the vertical one to
|
||
|
allow the title label and text field to be alongside each other,
|
||
|
horizontally.</p>
|
||
|
|
||
|
|
||
|
<h2 style="clear:right;">Step 8</h2>
|
||
|
|
||
|
<p>Create a <code>NoteEdit</code> class that extends
|
||
|
<code>android.app.Activity</code>.</p>
|
||
|
<p>This is the first time we will have
|
||
|
created an Activity without the Android Eclipse plugin doing it for us. When
|
||
|
you do so, the <code>onCreate()</code> method is not automatically
|
||
|
overridden for you. It is hard to imagine an Activity that doesn't override
|
||
|
the <code>onCreate()</code> method, so this should be the first thing you do.</p>
|
||
|
<ol>
|
||
|
<li>Right click on the <code>com.android.demo.notepad2</code> package
|
||
|
in the Package Explorer, and select <strong>New</strong> > <strong>Class</strong> from the popup
|
||
|
menu.</li>
|
||
|
<li>Fill in <code>NoteEdit</code> for the <code>Name:</code> field in the
|
||
|
dialog.</li>
|
||
|
<li>In the <code>Superclass:</code> field, enter
|
||
|
<code>android.app.Activity</code> (you can also just type Activity and hit
|
||
|
Ctrl-Space on Windows and Linux or Cmd-Space on the Mac, to invoke code
|
||
|
assist and find the right package and class).</li>
|
||
|
<li>Click <strong>Finish</strong>.</li>
|
||
|
<li>In the resulting <code>NoteEdit</code> class, right click in the editor
|
||
|
window and select <strong>Source</strong> > <strong>Override/Implement Methods...</strong></li>
|
||
|
<li>Scroll down through the checklist in the dialog until you see
|
||
|
<code>onCreate(Bundle)</code> — and check the box next to it.</li>
|
||
|
<li>Click <strong>OK</strong>.<p>The method should now appear in your class.</p></li>
|
||
|
</ol>
|
||
|
|
||
|
<h2>Step 9</h2>
|
||
|
|
||
|
<p>Fill in the body of the <code>onCreate()</code> method for <code>NoteEdit</code>.</p>
|
||
|
|
||
|
<p>This will set the title of our new Activity to say "Edit Note" (one
|
||
|
of the strings defined in <code>strings.xml</code>). It will also set the
|
||
|
content view to use our <code>note_edit.xml</code> layout file. We can then
|
||
|
grab handles to the title and body text edit views, and the confirm button,
|
||
|
so that our class can use them to set and get the note title and body,
|
||
|
and attach an event to the confirm button for when it is pressed by the
|
||
|
user.</p>
|
||
|
<p>We can then unbundle the values that were passed in to the Activity
|
||
|
with the extras Bundle attached to the calling Intent. We'll use them to pre-populate
|
||
|
the title and body text edit views so that the user can edit them.
|
||
|
Then we will grab and store the <code>mRowId</code> so we can keep
|
||
|
track of what note the user is editing.</p>
|
||
|
|
||
|
<ol>
|
||
|
<li>
|
||
|
Inside <code>onCreate()</code>, set up the layout:<br>
|
||
|
<pre>setContentView(R.layout.note_edit);</pre>
|
||
|
</li>
|
||
|
<li>
|
||
|
Change the Activity title to the "Edit Note" string:
|
||
|
<pre>setTitle(R.string.edit_note);</pre>
|
||
|
</li>
|
||
|
<li>
|
||
|
Find the {@link android.widget.EditText} and {@link android.widget.Button} components we need:
|
||
|
<p>These are found by the
|
||
|
IDs associated to them in the R class, and need to be cast to the right
|
||
|
type of <code>View</code> (<code>EditText</code> for the two text views,
|
||
|
and <code>Button</code> for the confirm button):</p>
|
||
|
<pre>
|
||
|
mTitleText = (EditText) findViewById(R.id.title);
|
||
|
mBodyText = (EditText) findViewById(R.id.body);
|
||
|
Button confirmButton = (Button) findViewById(R.id.confirm);</pre>
|
||
|
<p>Note that <code>mTitleText</code> and <code>mBodyText</code> are member
|
||
|
fields (you need to declare them at the top of the class definition).</p>
|
||
|
</li>
|
||
|
<li>At the top of the class, declare a <code>Long mRowId</code> private field to store
|
||
|
the current <code>mRowId</code> being edited (if any).
|
||
|
</li>
|
||
|
<li>Continuing inside <code>onCreate()</code>,
|
||
|
add code to initialize the <code>title</code>, <code>body</code> and
|
||
|
<code>mRowId</code> from the extras Bundle in
|
||
|
the Intent (if it is present):<br>
|
||
|
<pre>
|
||
|
mRowId = null;
|
||
|
Bundle extras = getIntent().getExtras();
|
||
|
if (extras != null) {
|
||
|
String title = extras.getString(NotesDbAdapter.KEY_TITLE);
|
||
|
String body = extras.getString(NotesDbAdapter.KEY_BODY);
|
||
|
mRowId = extras.getLong(NotesDbAdapter.KEY_ROWID);
|
||
|
|
||
|
if (title != null) {
|
||
|
mTitleText.setText(title);
|
||
|
}
|
||
|
if (body != null) {
|
||
|
mBodyText.setText(body);
|
||
|
}
|
||
|
}</pre>
|
||
|
<ul>
|
||
|
<li>
|
||
|
We are pulling the <code>title</code> and
|
||
|
<code>body</code> out of the
|
||
|
<code>extras</code> Bundle that was set from the
|
||
|
Intent invocation.
|
||
|
</li><li>
|
||
|
We also null-protect the text field setting (i.e., we don't want to set
|
||
|
the text fields to null accidentally).</li>
|
||
|
</ul>
|
||
|
</li>
|
||
|
<li>
|
||
|
Create an <code>onClickListener()</code> for the button:
|
||
|
<p>Listeners can be one of the more confusing aspects of UI
|
||
|
implementation, but
|
||
|
what we are trying to achieve in this case is simple. We want an
|
||
|
<code>onClick()</code> method to be called when the user presses the
|
||
|
confirm button, and use that to do some work and return the values
|
||
|
of the edited note to the Intent caller. We do this using something called
|
||
|
an anonymous inner class. This is a bit confusing to look at unless you
|
||
|
have seen them before, but all you really need to take away from this is
|
||
|
that you can refer to this code in the future to see how to create a
|
||
|
listener and attach it to a button. (Listeners are a common idiom
|
||
|
in Java development, particularly for user interfaces.) Here's the empty listener:<br>
|
||
|
<pre>
|
||
|
confirmButton.setOnClickListener(new View.OnClickListener() {
|
||
|
|
||
|
public void onClick(View view) {
|
||
|
|
||
|
}
|
||
|
|
||
|
});</pre>
|
||
|
</li>
|
||
|
</ol>
|
||
|
<h2>Step 10</h2>
|
||
|
|
||
|
<p>Fill in the body of the <code>onClick()</code> method of the <code>OnClickListener</code> created in the last step.</p>
|
||
|
|
||
|
<p>This is the code that will be run when the user clicks on the
|
||
|
confirm button. We want this to grab the title and body text from the edit
|
||
|
text fields, and put them into the return Bundle so that they can be passed
|
||
|
back to the Activity that invoked this <code>NoteEdit</code> Activity. If the
|
||
|
operation is an edit rather than a create, we also want to put the
|
||
|
<code>mRowId</code> into the Bundle so that the
|
||
|
<code>Notepadv2</code> class can save the changes back to the correct
|
||
|
note.</p>
|
||
|
<ol>
|
||
|
<li>
|
||
|
Create a <code>Bundle</code> and put the title and body text into it using the
|
||
|
constants defined in Notepadv2 as keys:<br>
|
||
|
<pre>
|
||
|
Bundle bundle = new Bundle();
|
||
|
|
||
|
bundle.putString(NotesDbAdapter.KEY_TITLE, mTitleText.getText().toString());
|
||
|
bundle.putString(NotesDbAdapter.KEY_BODY, mBodyText.getText().toString());
|
||
|
if (mRowId != null) {
|
||
|
bundle.putLong(NotesDbAdapter.KEY_ROWID, mRowId);
|
||
|
}</pre>
|
||
|
</li>
|
||
|
<li>
|
||
|
Set the result information (the Bundle) in a new Intent and finish the Activity:
|
||
|
<pre>
|
||
|
Intent mIntent = new Intent();
|
||
|
mIntent.putExtras(bundle);
|
||
|
setResult(RESULT_OK, mIntent);
|
||
|
finish();</pre>
|
||
|
<ul>
|
||
|
<li>The Intent is simply our data carrier that carries our Bundle
|
||
|
(with the title, body and mRowId).</li>
|
||
|
<li>The <code>setResult()</code> method is used to set the result
|
||
|
code and return Intent to be passed back to the
|
||
|
Intent caller. In this case everything worked, so we return RESULT_OK for the
|
||
|
result code.</li>
|
||
|
<li>The <code>finish()</code> call is used to signal that the Activity
|
||
|
is done (like a return call). Anything set in the Result will then be
|
||
|
returned to the caller, along with execution control.</li>
|
||
|
</ul>
|
||
|
</li>
|
||
|
</ol>
|
||
|
<p>The full <code>onCreate()</code> method (plus supporting class fields) should
|
||
|
now look like this:</p>
|
||
|
<pre>
|
||
|
private EditText mTitleText;
|
||
|
private EditText mBodyText;
|
||
|
private Long mRowId;
|
||
|
|
||
|
@Override
|
||
|
protected void onCreate(Bundle savedInstanceState) {
|
||
|
super.onCreate(savedInstanceState);
|
||
|
setContentView(R.layout.note_edit);
|
||
|
|
||
|
mTitleText = (EditText) findViewById(R.id.title);
|
||
|
mBodyText = (EditText) findViewById(R.id.body);
|
||
|
|
||
|
Button confirmButton = (Button) findViewById(R.id.confirm);
|
||
|
|
||
|
mRowId = null;
|
||
|
Bundle extras = getIntent().getExtras();
|
||
|
if (extras != null) {
|
||
|
String title = extras.getString(NotesDbAdapter.KEY_TITLE);
|
||
|
String body = extras.getString(NotesDbAdapter.KEY_BODY);
|
||
|
mRowId = extras.getLong(NotesDbAdapter.KEY_ROWID);
|
||
|
|
||
|
if (title != null) {
|
||
|
mTitleText.setText(title);
|
||
|
}
|
||
|
if (body != null) {
|
||
|
mBodyText.setText(body);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
confirmButton.setOnClickListener(new View.OnClickListener() {
|
||
|
|
||
|
public void onClick(View view) {
|
||
|
Bundle bundle = new Bundle();
|
||
|
|
||
|
bundle.putString(NotesDbAdapter.KEY_TITLE, mTitleText.getText().toString());
|
||
|
bundle.putString(NotesDbAdapter.KEY_BODY, mBodyText.getText().toString());
|
||
|
if (mRowId != null) {
|
||
|
bundle.putLong(NotesDbAdapter.KEY_ROWID, mRowId);
|
||
|
}
|
||
|
|
||
|
Intent mIntent = new Intent();
|
||
|
mIntent.putExtras(bundle);
|
||
|
setResult(RESULT_OK, mIntent);
|
||
|
finish();
|
||
|
}
|
||
|
});
|
||
|
}</pre>
|
||
|
</li>
|
||
|
</ol>
|
||
|
|
||
|
<h2>Step 11</h2>
|
||
|
|
||
|
<div class="sidebox-wrapper">
|
||
|
<div class="sidebox">
|
||
|
<h2>The All-Important Android Manifest File</h2>
|
||
|
<p>The AndroidManifest.xml file is the way in which Android sees your
|
||
|
application. This file defines the category of the application, where
|
||
|
it shows up (or even if it shows up) in the launcher or settings, what
|
||
|
activities, services, and content providers it defines, what intents it can
|
||
|
receive, and more. </p>
|
||
|
<p>For more information, see the reference document
|
||
|
<a href="{@docRoot}guide/topics/manifest/manifest-intro.html">The AndroidManifest.xml
|
||
|
File</a></p>
|
||
|
</div>
|
||
|
</div>
|
||
|
|
||
|
<p>Finally, the new Activity has to be defined in the manifest file:</p>
|
||
|
<p>Before the new Activity can be seen by Android, it needs its own
|
||
|
Activity entry in the <code>AndroidManifest.xml</code> file. This is to let
|
||
|
the system know that it is there and can be called. We could also specify
|
||
|
which IntentFilters the activity implements here, but we are going to skip
|
||
|
this for now and just let Android know that the Activity is
|
||
|
defined.</p>
|
||
|
<p>There is a Manifest editor included in the Eclipse plugin that makes it much easier
|
||
|
to edit the AndroidManifest file, and we will use this. If you prefer to edit the file directly
|
||
|
or are not using the Eclipse plugin, see the box at the end for information on how to do this
|
||
|
without using the new Manifest editor.<p>
|
||
|
<ol>
|
||
|
<li>Double click on the <code>AndroidManifest.xml</code> file in the package explorer to open it.
|
||
|
</li>
|
||
|
<li>Click the <strong>Application</strong> tab at the bottom of the Manifest editor.</li>
|
||
|
<li>Click <strong>Add...</strong> in the Application Nodes section.
|
||
|
<p>If you see a dialog with radiobuttons at the top, select the top radio button:
|
||
|
"Create a new element at the top level, in Application".</p></li>
|
||
|
<li>Make sure "(A) Activity" is selected in the selection pane of the dialog, and click <strong>OK</strong>.</li>
|
||
|
<li>Click on the new "Activity" node, in the Application Nodes section, then
|
||
|
type <code>.NoteEdit</code> into the <em>Name*</em>
|
||
|
field to the right. Press Return/Enter.</li>
|
||
|
</ol>
|
||
|
<p>The Android Manifest editor helps you add more complex entries into the AndroidManifest.xml
|
||
|
file, have a look around at some of the other options available (but be careful not to select
|
||
|
them otherwise they will be added to your Manifest). This editor should help you understand
|
||
|
and alter the AndroidManifest.xml file as you move on to more advanced Android applications.</p>
|
||
|
|
||
|
<p class="note">If you prefer to edit this file directly, simply open the
|
||
|
<code>AndroidManifest.xml</code> file and look at the source (use the
|
||
|
<code>AndroidManifest.xml</code> tab in the eclipse editor to see the source code directly).
|
||
|
Then edit the file as follows:<br>
|
||
|
<code><activity android:name=".NoteEdit" /></code><br><br>
|
||
|
This should be placed just below the line that reads:<br>
|
||
|
<code></activity></code> for the <code>.Notepadv2</code> activity.</p>
|
||
|
|
||
|
<h2 style="clear:right;">Step 12</h2>
|
||
|
|
||
|
<p>Now Run it!</p>
|
||
|
<p>You should now be able to add real notes from
|
||
|
the menu, as well as delete an existing one. Notice that in order to delete, you must
|
||
|
first use the directional controls on the device to highlight the note.
|
||
|
Furthermore, selecting a note title from the list should bring up the note
|
||
|
editor to let you edit it. Press confirm when finished to save the changes
|
||
|
back to the database.
|
||
|
|
||
|
<h2>Solution and Next Steps</h2>
|
||
|
|
||
|
<p>You can see the solution to this exercise in <code>Notepadv2Solution</code>
|
||
|
from the zip file to compare with your own.</p>
|
||
|
<p>Now try editing a note, and then hitting the back button on the emulator
|
||
|
instead of the confirm button (the back button is below the menu button). You
|
||
|
will see an error come up. Clearly our application still has some problems.
|
||
|
Worse still, if you did make some changes and hit the back button, when you go
|
||
|
back into the notepad to look at the note you changed, you will find that all
|
||
|
your changes have been lost. In the next exercise we will fix these
|
||
|
problems.</p>
|
||
|
|
||
|
<p>
|
||
|
Once you are ready, move on to <a href="notepad-ex3.html">Tutorial
|
||
|
Exercise 3</a> where you will fix the problems with the back button and lost
|
||
|
edits by introducing a proper life cycle into the NoteEdit Activity.</p>
|
||
|
|
||
|
|