2008年12月28日星期日

教程:Notepad练习2

Tutorial: Notepad Exercise 2
教程:Notepad练习2

In this exercise, you will add a second Activity to your notepad application, to let the user create, edit, and delete notes. The new Activity assumes responsibility for creating new notes by collecting user input and packing it into a return Bundle provided by the intent. This exercise demonstrates:
在这个练习之中,你会增加第二个活动进入你的notepad程序,让用户创建、编辑、删除便签。新的Activity呈现的职责是创建新的便签来收集用户输入、打包进入一个返回的由intent提供的Bundle,这个练习证明了:
* Constructing a new Activity and adding it to the Android manifest
1、构造新Activity,增加进入Android manifext
* Invoking another Activity asynchronously using startActivityForResult()
2、使用startActivityForResult()异步调用另外一个Activity
* Passing data between Activity in Bundle objects
3、在Activity间使用Bundle对象传递数据
* How to use a more advanced screen layout
4、怎么使用更多的高级界面布局

Step 1
第一步

Create a new Android project using the sources from Notepadv2 under the NotepadCodeLab folder, just like you did for the first exercise. If you see an error about AndroidManifest.xml, or some problems related to an android.zip file, right click on the project and select Android Tools > Fix Project Properties.
创建新Android项目,使用NotepadCodeLab文件夹的Notepadv2源代码,和练习1的做法相同。如果你看到AndroidManifest.xml的错误,或者android.zip的问题,在project上右击,选择Android Tools > Fix Project Properties
Open the Notepadv2 project and take a look around:
打开Notepadv2项目,全部看一看:
* Open and look at the strings.xml file under res/values — there are several new strings which we will use for our new functionality
1、打开strings.xml 并查看——新功能会用到几个新的字符串
* Also, open and take a look at the top of the Notepadv2 class, you will notice several new constants have been defined along with a new mNotesCursor field used to hold the cursor we are using.
2、打开 Notepadv2类,看看顶部的代码,你会注意到定义了几个新常量,和新的mNotesCursor属性一起用于存储我们正在使用的游标。
* Note also that the fillData() method has a few more comments and now uses the new field to store the notes Cursor. The onCreate() method is unchanged from the first exercise. Also notice that the member field used to store the notes Cursor is now called mNotesCursor. The m denotes a member field and is part of the Android coding style standards.
3、fillData()方法有更多的注释,使用新的字段来保存便签游标。onCreate()方法没有改变。用于保存便签游标的成员变量现在称为mNotesCursor。m指示一个成员属性,是Android代码风格标准的一部分。
* There are also a couple of new overridden methods (onListItemClick() and onActivityResult()) which we will be filling in below.
4、还有一对新的重载方法:onListItemClick() 和onActivityResult(),下面我们会填充它们。

Step 2
第二步

Add an entry to the menu for deleting a note:
增加删除便签的菜单入口:
1. In the onCreateOptionsMenu() method, add a new line:
1、在方法onCreateOptionsMenu()中增加新行:
menu.add(0, DELETE_ID, 0, R.string.menu_delete);

2. The whole method should now look like this:
2、整个方法的代码如下:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
menu.add(0, INSERT_ID, 0, R.string.menu_insert);
menu.add(0, DELETE_ID, 0, R.string.menu_delete);
return true;
}

Step 3
第三步

In the onMenuItemSelected() method, add a new case for DELETE_ID:
onMenuItemSelected()增加新代码,关联DELETE_ID:
mDbHelper.deleteNote(getListView().getSelectedItemId());
fillData();
return true;

1. Here, we use the deleteNote method to remove the note specified by ID. In order to get the ID, we call getListView().getSelectedItemId().
1、使用deleteNote方法,通过ID移除便签。为了获取ID,我们调用getListView().getSelectedItemId()
2. Then we fill the data to keep everything up to date.
2、我们填充数据以保证每件事情最新
The whole method should now look like this:
整个方法的代码:
@Override
public boolean onMenuItemSelected(int featureId, MenuItem item) {
switch(item.getItemId()) {
case INSERT_ID:
createNote();
return true;
case DELETE_ID:
mDbHelper.deleteNote(getListView().getSelectedItemId());
fillData();
return true;
}

return super.onMenuItemSelected(featureId, item);
}

Step 4
第四步

Starting Other Activities
启动其他的Activity
In this example our Intent uses a class name specifically. As well as starting intents 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.
这个例子中,Intent使用一个特定的类名。又在类中启动intent,正如我们知道的那样,是他们在我们自己的程序或者其他程序中,我们也能创建Intent而不知道哪个程序处理它。
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 android.content.Intent for more information.
例如,我们可能想在浏览器中打开一个页面,为了这个目的,我们人就使用Intent。但不是指定一个类来处理它,我们使用一个预定义的Intent常量,一个内容URI(描述我们想做什么)。欲知更多信息,请看“android.content.Intent ”。

Fill in the body of the createNote() method:
填写createNode()方法的代码:
Create a new Intent to create a note (ACTIVITY_CREATE) using the NoteEdit class. Then fire the Intent using the startActivityForResult() method call:
创建新的Intent来创建一个便签(ACTIVITY_CREATE),使用NoteEdit类。然后,调用startActivityForResult(),触发Intent:
Intent i = new Intent(this, NoteEdit.class);
startActivityForResult(i, ACTIVITY_CREATE);

This form of the Intent call targets a specific class in our Activity, in this case NoteEdit. Since the Intent class will need to communicate with the Android operating system to route requests, we also have to provide a Context (this).
这种Intent调用的形式瞄准Activity中的特定类,这个例子中指NoteEdit。因为Intent类会需要与Android操作系统通讯,目的是路由请求消息,我们也不得不提供Context(this)。
The startActivityForResult() 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 onActivityResult() and we will implement it in a later step. The other way to call an Activity is using startActivity() 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 startActivity().
startActivityForResult()方法触发Intent,这种方法促使Activity中的方法在新Activity完成时被调用。Activity中接受回调的方法是onActivityResult(),在稍后步骤中会实现它。调用Activity的其他方式是使用startActivity(),但是这种方法是"fire-and-forget"(发射后忘记)——使用这种方法,Activity完成时Activity消息并不灵通,startActivity()调用的Activity没有办法返回结果信息。
Don't worry about the fact that NoteEdit doesn't exist yet, we will fix that soon.
不要担心NoteEdit,我们会校正这个问题。

Step 5
第五步

Fill in the body of the onListItemClick() override.
填写onListItemClick()重载代码。
onListItemClick() 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 ListView object it was invoked from, the View inside the ListView that was clicked on, the position in the list that was clicked, and the mRowId of the item that was clicked. In this instance we can ignore the first two parameters (we only have one ListView it could be), and we ignore the mRowId as well. All we are interested in is the position that the user selected. We use this to get the data from the correct row, and bundle it up to send to the NoteEdit Activity.
onListItemClick()是一个回调函数,我们将会重载它。当用户从列表中选择一项时该函数自动被系统调用。四个参数:ListView对象、View(点击的ListView里面)、点击的列表位置、nRowId(点击的条目的记录ID)。这个例子中,我们忽略前面两个参数,也忽略mRowId。我们感兴趣的是用户选择的位置。我们使用这个方法获得正确行的数据,捆扎在一起送到NoteEdit活动。
In our implementation of the callback, the method creates an Intent to edit the note using the NoteEdit 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 mRowId for the note we are editing. Finally, it will fire the Intent using the startActivityForResult() method call. Here's the code that belongs in onListItemClick():
在回调函数的实现中,创建Intent,以便使用NoteEdit类编辑便签。然后,增加数据进入Intent的附加Bundle中。新建的Intent对象传送给被调用的活动。我们使用它传入标题和内容文本,以及我们编辑的便签的mRowId。最后,使用startActivityForResult()方法发射Intent。下面的代码是onListItemClick()的实现:
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);

* putExtra() 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.
1、putExtra()方法增加条目到附加的Bundle中,传入参数intent的引用。此处,我们使用Bundle来传入标题、内容、我们想编辑的便签的记录ID
* 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 moveToPosition() method.
2、从查询游标中读出便签的详细信息,游标的位置移动到列表中选中的元素的位置,使用moveToPosition()方法移动。
* With the extras added to the Intent, we invoke the Intent on the NoteEdit class by passing startActivityForResult() the Intent and the request code. (The request code will be returned to onActivityResult as the requestCode parameter.)
3、附加信息加入Intent中,我们在NoteEdit类型中调用Intent,吧Intent和请求代码传递给startActivityForResult()。(请求代码作为requestCode参数在onActivityResult中返回。)
Note: 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.
注意:我们把mNotesCursor属性赋值到本地变量,这是对Android代码的优化。访问本地变量比访问DalvikVM的一个属性更有效率,因此,这样做了后我们访问了这个属性一次,访问本地变量5次,使程序有效得多。推荐尽可能的使用这种优化方法。

Step 6
第六步

The above createNote() and onListItemClick() methods use an asynchronous Intent invocation. We need a handler for the callback, so here we fill in the body of the onActivityResult().
上面提到的createNote()和onListItemClick()方法使用了异步Intent引用。我们需要处理回调行数,因此,此处我们填充onActivityResult()代码。
onActivityResult() 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 startActivityForResult.) The parameters provided to the callback are:
onActivityResult()是一个重载方法,当Activity返回结果的时候被调用。(记住,如果使用startActivityForResult触发,则Activity仅仅会返回一个结果。) 回调函数提供的参数如下:
* requestCode — the original request code specified in the Intent invocation (either ACTIVITY_CREATE or ACTIVITY_EDIT for us).
1、requestCode——在Intent引用中指定的原始请求代码(要么是ACTIVITY_CREATE,要么是ACTIVITY_EDIT)
* resultCode — 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.
2、resultCode——调用的结果或者错误代码,如果一切都没有问题,应该是0,但是可以是非0值,则表示有错误发生。有标准的结果代码获得,你也可以创建你自己的常量来指示特定错误。
* intent — this is an Intent created by the Activity returning results. It can be used to return data in the Intent "extras."
3、intent——通过有返回结果的Activty创建的Intent。他能用于返回数据,数据存放在Intent的“extras”中。
The combination of startActivityForResult() and onActivityResult() 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.
startActivityForResult()和onActivityResult()的结合能作为异步RPC,提供活动相互调用和共享服务的推荐方法。
Here's the code that belongs in your onActivityResult():
下面是onActivityResult()的代码:
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;
}

* We are handling both the ACTIVITY_CREATE and ACTIVITY_EDIT activity results in this method.
1、在这个方法中,我们处理 ACTIVITY_CREATE 和 ACTIVITY_EDIT两种活动返回结果。
* 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.
2、在创建的代码中,我们从extras获取标题和内容,然后用来创建新的便签。
* In the case of an edit, we pull the mRowId as well, and use that to update the note in the database.
3、在编辑的代码中,我们还是获取mRowId,然后用来修改数据库的便签数据。
* fillData() at the end ensures everything is up to date .
4、fillData()保证所有信息都是最新的。

Step 7
第七步

The Art of Layout
布局的艺术
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.
note_edit.xml布局文件是我们建立的应用程序中最成熟的一个文件,但是那不并意味着接近你在真实Android程序中可能要求的那种成熟。
Creating a good UI is part art and part science, and the rest is work. Mastering Android layout is an essential part of creating a good looking Android application.
创建好的UI部分(参见“Android layout”)是艺术部分是科学,剩余的就是工作。掌握Android布局是创建好看的Android程序的基本部分。
Take a look at the View Gallery 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.
仔细看看一些例子的布局,研究怎么使用他们,参见“View Gallery”。ApiDemos例子项目也是好资源,可以学习了解怎么创建不懂的布局。

Open the file note_edit.xml that has been provided and take a look at it. This is the UI code for the Note Editor.
打开已经提供的note_edit.xml文件,仔细看看。这是Note Editor的UI代码。
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.)
这是我们已经处理的最成熟的UI。提供这个文件为了避免问题,这些问题可能由于键入代码而悄悄潜入。(XML是非常严格的,在大小写和结构等方面的错误是布局问题的最常见原因。)
There is a new parameter used here that we haven't seen before: android:layout_weight (in this case set to use the value 1 in each case).
这里有一个以前没有见过的新参数:android:layout_weight(本例中在所有情况下设置为值1)
layout_weight is used in LinearLayouts to assign "importance" to Views within the layout. All Views have a default layout_weight 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 layout_weight and its ratio to the overall layout_weight specified in the current layout for this and other View elements.
layout_weight用在LinearLayouts中,在布局中赋值"importance"给View。所有View默认layout_weight值为0,意味着他们仅仅占据他们需要的显示空间。赋值大于0将会在父View中分割可获得的剩余空间,分割空间的依据是每个View的layout_weight的值,还有在View元素的现有布局中指定的全部layout_weight的比率。
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 layout_weight specified, so it takes up the minimum space required to render. If the layout_weight 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 layout_weight of 1 and the second has a layout_weight 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).
举一个例子:假设我们在一行中有一个文本标签和两个文本编辑框。标签没有指定layout_weight,因此他占据必须的最小空间。如果两个文本编辑框的layout_weight都设置为1,可在父窗口中的剩余宽度被他们平分(因为我们声称他们同等重要)。如果第一个是1,第二个是2,那么剩余空间的三分之一分配给第一个,三分之二分配给第二个(因为我们要求第二个更重要)
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.
这个布局文件也证明怎么互相嵌套多个布局元素,以便获得更多复杂和合适的布局界面。本例中,水平线布局嵌套进入垂直线,以便允许标题标签和文本字段相互并排。

Step 8
第八步

Create a NoteEdit class that extends android.app.Activity.
创建NoteEdit类,继承android.app.Activity
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 onCreate() method is not automatically overridden for you. It is hard to imagine an Activity that doesn't override the onCreate() method, so this should be the first thing you do.
我们第一次没有在ADT帮助下创建Activity。onCreate()方法没有自动重载。想像没有重载onCreate()方法的Activity是困难的,因此,首先要做的事情是重载onCreate()
1. Right click on the com.android.demo.notepad2 package in the Package Explorer, and select New > Class from the popup menu.
1、右击com.android.demo.notepad2包,从弹出菜单中选择 New > Class
2. Fill in NoteEdit for the Name: field in the dialog.
2、在对话框的“Name:”标签后填入“NoteEdit”
3. In the Superclass: field, enter android.app.Activity (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).
3、在“Superclass: ”标签中,键入“android.app.Activity”(你也能输入Activity,按 Ctrl-Space,启用代码助手找到正确的包和类)
4. Click Finish.
4、点击Finish
5. In the resulting NoteEdit class, right click in the editor window and select Source > Override/Implement Methods...
5、在NoteEdit类中,右击编辑窗口,选择菜单Source > Override/ImplementMethods...
6. Scroll down through the checklist in the dialog until you see onCreate(Bundle) — and check the box next to it.
6、滚动对话框中的清单,找到onCreate(Bundle)——选中前面的检查框
7. Click OK.
7、点击OK
The method should now appear in your class.
这个方法应该出现在你的类中。

Step 9
第九步

Fill in the body of the onCreate() method for NoteEdit.
填写onCreate()的代码
This will set the title of our new Activity to say "Edit Note" (one of the strings defined in strings.xml). It will also set the content view to use our note_edit.xml 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.
这里将会设置新活动的题目为“Edit Note”(在strings.xml文件中定义的一个字符串)。也会设置内容视图以便使用note_edit.xml布局文件。然后,处理标题和内容文本编辑框、确认按钮,因此,我们的类能使用他们来获取或者设置便签的标题和内容,附着事件到确认按钮,这个事件在用户按确认按钮时触发。
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 mRowId so we can keep track of what note the user is editing.
然后,我们对传入到Activity的值进行分类,传入值使用Activity的extras Bundle,extras Bundle附着到调用的Intent上。我们会使用他们来预先填充标题和内容文本编辑框,因此,用户能编辑他们。然后,我们会获取和存储mRowId,因此我们能留意用户编辑的什么便签。
1. Inside onCreate(), set up the layout:
1、在onCreate()内部,设置布局:
setContentView(R.layout.note_edit);

2. Find the edit and button components we need:
2、找到我们需要的编辑框和按钮组件:
These are found by the IDs associated to them in the R class, and need to be cast to the right type of View (EditText for the two text views, and Button for the confirm button):
在R类中,通过ID找到他们,需要强制转换到正确的视图类型(两个文本视图是EditText,确认按钮时Button)
mTitleText = (EditText) findViewById(R.id.title);
mBodyText = (EditText) findViewById(R.id.body);
Button confirmButton = (Button) findViewById(R.id.confirm);

Note that mTitleText and mBodyText are member fields (you need to declare them at the top of the class definition).
注意:mTitleText 和 mBodyText是成员变量(你需要在顶部声明他们)
3. At the top of the class, declare a Long mRowId private field to store the current mRowId being edited (if any).
3、在类的顶部,生命一个Long类型的mRowId私有变量,用来存储当前编辑的mRowId
4. Continuing inside onCreate(), add code to initialize the title, body and mRowId from the extras Bundle in the Intent (if it is present):
4、继续在onCreate()中增加代码来初始化标题、内容、mRowId,从Intent的extras Bundle(附加包)中获取初始化数据。
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);
}
}

* We are pulling the title and body out of the extras Bundle that was set from the Intent invocation.
(1)从来自Intent引用的extras Bundle中获取标题和内容
* We also null-protect the text field setting (i.e., we don't want to set the text fields to null accidentally).
(2)零保护文本框设置(例如,我们不想意外地设置文本框为null)
5. Create an onClickListener() for the button:
5、针对按钮创建onClickListener()
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 onClick() 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:
Listener使UI实现更混乱的一个方面,但是,本例中试图完成的事情是简单的。当用户按确认按钮时,调用onClieck()方法,用来完成一些工作并且然会被编辑的便签的值给Intent调用者。我们利用称为匿名内嵌类的东西来做这些事情。如果你以前没有见到过,那么看起来这有点混乱,但是,你实际需要取走(完成)的所有事情是可以在未来引用这里的代码,注意怎样创建一个Listener,并附着到一个按钮(Listener是Java开发的一个习惯用语,尤其是在UI方面)。下面是空的Listener:
confirmButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
}
});

Step 10
第十步

Fill in the body of the onClick() method in our listener.
填充侦听器里面的onClick()代码。
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 NoteEdit Activity. If the operation is an edit rather than a create, we also want to put the mRowId into the Bundle so that the Notepadv2 class can save the changes back to the correct note.
这些代码在用户点击确认按钮时运行。我们使用这些代码从编辑框中获取标题和内容文本串,然后把它们送给Bundle,以便他们能传回到Activity(NoteEdit Activity调用)。如果操作是编辑而不是创建,那么传送mRowId进入Bundle,以便Notepav2类能保存变化到正确的便签。
1. Create a Bundle and put the title and body text into it using the constants defined in Notepadv2 as keys:
1、创建Bundle,传递标题和内容文本到Bundle,使用在Notepadv2中定义的常量作为关键码:
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);
}

2. Set the result information (the Bundle) in a new Intent and finish the Activity:
2、在新的Intent中设置结果信息(Bundle),完成Activity:
Intent mIntent = new Intent();
mIntent.putExtras(bundle);
setResult(RESULT_OK, mIntent);
finish();

* The Intent is simply our data carrier that carries our Bundle (with the title, body and mRowId).
(1)Intent是简单的数据运载工具,运载Bundle(携带标题、内容、mRowId)
* The setResult() 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.
(2)setResult()方法用于设置结果代码,返回Intent为了传回到Intent调用者。本例中,每件事情都正常工作,因此我们返回RESULT_OK作为结果代码
* The finish() 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.
(3)finish()调用用于发信号表明Activity被激发(像一个回调)。然后,在Result中设置的所有东西会返回给调用者,连同执行控制一起

The full onCreate() method (plus supporting class fields) should now look like this:
完整的onCreate()如下:
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();
}
});
}

Step 11
第11步

The All-Important Android Manifest File
非常重要的Android Manifest文件
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.
AndroidManifest.xml文件是Android领会你的程序的方式。这个文件定义了程序的类型,在发射器和设置里面露面的地方,定义了活动、服务、内容提供器是什么,能接收的intent是什么,等等。
For more information, see the reference document AndroidManifest.xml
欲知更多信息参见文档“AndroidManifest.xml”

Finally, the new Activity has to be defined in the manifest file:
最后,新Activity必须定义在manifest文件中:
Before the new Activity can be seen by Android, it needs its own Activity entry in the AndroidManifest.xml 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.
在新Activity能被Android了解之前,需要在AndroidManifest.xml中定义自己的Activity入口。这是让系统知道它在那儿且能被调用。我们也能指定Activity实现哪个IntentFilters,但是我们打算暂时跳过这个,只是让Android知道Activity被定义了。
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.
在Eclipse插件中包含了一个Manifest编辑器,使AndroidManifest文件编辑更加容易,我们将会使用这个编辑器。
1. Double click on the AndroidManifest.xml file in the package explorer to open it.
1、在AndroidManifest.xml文件上双击打开它
2. Click the Application tab at the bottom of the Manifest editor.
2、在Manifest编辑器底部点击Application便签
3. Click Add... in the Application Nodes section.
3、在Application Nodes段中点击 Add...
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".
如果你在顶部看到一个带有单选按钮的对话框,那么选择顶部的单选按钮"Create a new element at the top level, in Application"
4. Make sure "(A) Activity" is selected in the selection pane of the dialog, and click OK.
4、保证 "(A) Activity"在对话框的选择面板中被选中,然后点击OK
5. Click on the new "Activity" node, in the Application Nodes section, then type .NoteEdit into the Name* field to the right. Press Return/Enter.
5、在 Application Nodes段中,点击新的"Activity"节点,然后在“Name*”标签右边输入.NoteEdit,按Return/Enter

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.
Android Manifest编辑器帮助你增加更多复杂的条目到AndroidManifest.xml文件中,仔细看看可获得的其他选项(但是小心不要选择他们否则他们会被加入到你的Manifest文件中)。这个编辑器应该帮助你理解和修改AndroidManifest.xml文件,当你移到更多高级的Android程序时会很有用处。
If you prefer to edit this file directly, simply open the AndroidManifest.xml file and look at the source (use the AndroidManifest.xml tab in the eclipse editor to see the source code directly). Then edit the file as follows:
如果你宁愿直接编辑这个文件,简单打开 AndroidManifest.xml文件,然后了解资源(使用eclipse编辑器的AndroidManifest.xml标签直接领悟源代码)。然后,按照下面这样编辑文件:

This should be placed just below the line that reads:
这应该表示:
for the .Notepadv2 activity.
针对.Notepadv2活动

Step 12
第12步

Now Run it!
现在运行。
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.
现在,你应该能从菜单增加真实的便签,也能删除存在的便签。注意:为了删除,你必须首先使用设备方向控制来选中便签。此外,从列表中选择便签标题应该被提出到便签编辑器,以便你能编辑他。按确认按钮保证保存修改到数据库中。

2008年12月16日星期二

教程:Notepad练习1

教程:Notepad练习1

在这个练习中,你将会构建一个简单的便签列表,让用户增加新便签,而不能编辑。这个练习展示了:
* ListActivities的基本用法,创建和处理菜单选项
* 怎样使用SQLite数据库来存储便签
* 怎样使用SimpleCursorAdapter来绑定数据库游标上的数据到ListView上
* 界面布局的基本用法,包括怎样摆放一个列表视图,怎样增加条目到活动菜单中,活动怎样处理菜单选择事件。

第一步

在Eclipse中打开Notepadv1项目

Notepadv1是作为起始点提供的项目。该工程项目关注一些样板工作,正如你在“Hello Android tutorial”中看到的一样。
1、通过点击菜单File > New > Android Project进入一个新的Android项目
2、在New Android Project对话框中,选择Create project from existing source
3、点击Browse,然后选择你存放NotepadCodeLab的目录。选择Notepadv1,然后点击Choose。
4、你会在Project name中看到Notepadv1,也会看到填充了你选择的路径的Location。
5、点击Finish。Notepadv1项目应该打开了,并在Eclipse package explorer(包浏览器)中可见

如果你看到关于AndroidManifest.xml的错误,或者关于Android zip文件的问题,在项目上右键,选择菜单Android Tools > Fix Project Properties。(这个项目看起来库文件处于错误的位置,这种办法会替你纠正它。)

第二步

看看NotesDbAdapter类——这个类提供SQLite数据库访问的封装,将会保存我们的便签数据,允许我们修改它。
这个类顶部是一些常量定义,程序使用这些常量来查找数据库中合适字段的数据。也定义了一个数据库创建字串,用于创建新数据库表格,如果不存在的话。
我们的数据库的名称为data,还有一个成为notes的表,按次序有三个字段:_id,title,body。_id命名使用下划线规则,这个规则用于许多地方,包括AndroidSDK,有助于保持状态跟踪。通常,当查询或者修改数据库(在列预测等等)时_id必须定义。其他两个字段是存储数据的简单的文本字段。
NotesDbAdapter的构造函数使用了Context,Context允许和Android操作系统的各个方面通讯。对于需要以某种方式和Android系统接触的类,这是很常见的。Activity类重新实现了Context类,因此,当你需要Context时,通常你只需要从Activity传递Context。
open()方法调用一个DatabaseHelper实例,DatabaseHelper是SQLiteOpenHelper类的本地实现。它调用getWritableDatabase()来处理创建、打开数据库的操作。
close()仅仅关闭数据库,释放和连接相关的资源。
createNote()携带标题文本字串和新便签的内容,然后再数据库中创建那个便签。假如新便签成功创建,这个方法返回新创建的便签所在行的_id的值。
deleteNote()携带指向一个特定便签的rowId,然后从数据库中删除那条便签。
fetchAllNotes()发出一个查询,返回数据库中所有便签的游标。query()调用是值得研究和理解。第一个字段是查询的数据表的名称(这个例子DATABASE_TABLE是“notes”)。下面是我们想返回的列的列表,这个例子就是_id、title和列的数据体,因此被指定放到String数组中。剩下的字段依次是:selection、selectionArgs、groupBy、having和orderBy。这些字段都允许空意味著我们允许存放所有的数据,不必分组,采用默认的排序方式。更多细节请参见“SQLiteDatabase”。

注意:返回的是游标而不是记录集。这允许Android有效使用资源——避免直接加载大量数据到内容中,当需要的时候,游标会获取和释放数据,这对于大表是非常有效的。

fectchNote()类似于fectchAllNotes(),仅仅通过我们指定的rowId获取一条便签。他使用SQLiteDatabase的query()方法的轻量级版本。第一个参数(设置为true)表示我们对一个明显的结果感兴趣(只返回唯一的一行)。selection参数(第四个)有值表示仅仅返回符合“where _id=我们传送的rowId”的记录。因此,我们获得指向某行的游标。
最后,updateNote()针对rowId、title和body,使用ContentValue实例修改对应rowId的便签。

第三步

打开res/layout目录下的notepad_list.xml文件,仔细看看。(为了浏览XML标记你可能必须点击XML便签页,在底部)
这是一个大部分空着的界面定义文件。下面是你应该知道的关于界面文件的一些事情:
*所有界面文件必须以作为第一行
*下一个定义通常但不总是某种界面元素定义,比如LinearLayout
*XML名称空间应该总是定义在XML文件中的顶级组件或者界面,因此文件的其余部分总是能看到android标签:
xmlns:android="http://schemas.android.com/apk/res/android"

界面与活动
多数活动类会有一个与之联系的界面。界面是活动的呈现给用户的脸面,这个例子中,我们的界面占据整个屏幕,显示一个便签列表。
但是,对于一个活动来讲,全屏幕界面不是唯一选项。你也可能想使用一个浮动的界面(例如对话框和警告提示框),或者也许你根本不需要一个界面(如果你不指定活动使用的某种界面,则活动将是用户不可见的)。

第四步

我们需要创建界面来容纳我们的列表。增加包含LinearLayout元素,整个文件看起来像这样:

android:layout_width="wrap_content"
android:layout_height="wrap_content">
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/no_notes"/>

*ListView和TextView标签的id串中的@符号意思是:XML语法分析器应该解析和扩展id串的剩余部分,使用一个ID资源扩展。
*ListView和TextView可以作为两个可互相替换的视图,仅仅其中一个会被立即显示。
*通过Android平台列表和空的ID提供给我们,因此,我们必须使用android作为id的前缀。例如,@android:id/list
*当ListAdapter没有数据提供给ListView时,系统自动使用携带空id的视图。默认情况下,ListAdapter知道寻找这个名字。另外,你应该使用ListView的serEmptyView(VIew)方法来改变默认的空视图。
基本上说,android.R类是平台提供的一组预定义的资源,而你的项目的R类是你自己项目已经定义的一组资源。在android.R资源类找到的资源能被用于XML文件,通过使用android:名称空间前缀。

第五步

为了在ListView中创建便签列表,我们也需要为每一行定义一个VIew:
1、在res/layout目录下创建一个新文件,取名为notes_row.xml
2、增加如下的内容(注意:XML头部又被使用,第一个节点定义了android XML名称空间)

xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
这是用于每个便签标题行的View——仅有一个文本字段。
在这个例子中,我们创建一个新的id,称为text1.@号后面的+表示如果id不存在那么自动作为资源创建,因此我们在text1,然后使用它。
3、保存文件
在项目中打开R.java类看看它,你将会看到新的定义:notes_row和text1,意味着我们现在能在代码中访问这些。

资源和R类
res/文件夹用于资源。res/下面有对于文件夹和文件的特别结构。
这些文件夹和文件中定义的资源将会对应R类中的条目,以便允许他们容易被访问和使用。R类由eclipse插件根据res/的内容自动产生(或者,在命令行工具中使用aapt命令)。作为应用程序的一部分,他们可展开和收集。

第六步
下面,在源代码中打开Noteadv1类。下面的步骤中,我们打算替换这个类,让其变成列表适配器,显示我们的便签,也会允许增加新的便签。
Notepadv1将会继承Activity的子类ListActivity,ListActivity有额外的功能以容纳多种可能加入的列表功能,例如:在每行的列表条目上显示任意的数字、移动列表条目、选择列表条目。
仔细查看Notepadv1类的已存在的代码。有一个还没有使用的私有字段叫做mNoteNumber,我们将会用来创建编号了的便签的标题。
还有三个重载方法:onCreate,OnCreateOptionsMenu和OnOptionsItemSelected,我们需要填充他们。
* onCreate()在活动启动的时候调用——有点像活动的“main”方法。运行时使用这个方法来设置资源和状态。
*onCreateOptionsMenu()用于增加活动的菜单。当用户点击菜单按钮时将会表现出来,有一个选项列表能选择。
*onOptionsItemSelected()是菜单等式的另外一半,用于处理菜单事件(例如,在用户选择“Create NOte”子菜单时)

第七步

修改Notepadv1的继承关系,从Activity改为ListActivity:
public class Notepadv1 extends ListActivity
注意:你必须使用Eclipse导入ListActivity到Notepadv1类中,Windows 和Linux的热键为ctrl-shift-O,Mac的热键cmd-shift-O。

第八步

填充onCreate()方法的代码。
这里我们会设置活动的标题(显示在屏幕的顶部),使用我们在XML文件中创建的notepad_list界面,设置NotesDbAdapter实例以便将来访问便签数据,使用可获取的便签标题来增加列表:
1、在onCreate方法中,携带savedInstanceState参数调用super()
2、调用setContentView(),传递R.layout.notepad_list
3、在类的顶部,创建一个NotesDbAdapter类的私有属性,称为mDbHelper
4、在onCreate方法中,构建一个新NotesDbAdapter实例,绑定到mDbHelper字段(传递给DBHelper的构造函数)
5、在mDbHelper上调用open()方法,以打开数据库
6、最后,调用新方法fillData(),作用是:获取数据,使用helper增加ListView——我们还没有定义这个方法
onCreate() 应该看起来这样:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.notepad_list);
mDbHelper = new NotesDbAdapter(this);
mDbHelper.open();
fillData();
}
并且保证你已经定义了mDbHelper域(正好在mNoteNumber下)
private NotesDbAdapter mDbHelper;

第九步

填写onCreateOptionsMenu()的代码。
现在我们会创建“Add Item”按钮,这个按钮能在设备上点击。我们会指定他在菜单上占据的位置.
1、strings.xml中增加一个名叫“menu_insert”的新字串,该字串的值为Add Item:
Add Item
然后保存文件并返回到Notepadv1.
2、在类的顶部创建菜单位置常量
public static final int INSERT_ID = Menu.FIRST;
3、OnCreateOptionsMenu()中修改super的调用,因此我们捕获布尔变量作为结果返回。我们将会在最后返回这个值。
4、然后,使用menu.add()方法增加菜单。
整个方法应该看起来这样:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
boolean result = super.onCreateOptionsMenu(menu);
menu.add(0, INSERT_ID, 0, R.string.menu_insert);
return result;
}
传给add()的参数是:这个菜单的上级菜单标识(这个例子无),一个唯一ID(如上的定义),菜单项的顺序(o表示没有优先级),用于这个条目的资源串。

关于菜单的更多知识


我们构造的notepad程序仅仅接触了菜单的表面。
你还能“增加菜单项的快捷键”、“创建子菜单”,甚至增加菜单项到其他应用。

第十步

填充onOptionsItemSelected()方法的代码。
打算处理我们新的Add Note菜单项。当菜单被选择时,onOptionsItemSelected方法携带item.getId(),设置到INSERT_ID(这个常数我们用来识别菜单项)。我们能检测这个选择事件,作出适当的动作:
1、super.onOptionsItemSelected(item)方法的调用发生在这个方法的最后——我们想首先捕获我们的事件。
2、在item.getItemID()中写一个转换状态。
INSERT_ID的例子中,调用一个新的方法,createNote(),返回true,因为我们已经处理了这个时间,不想通过系统传递它。
3、最后,返回超类的onOptionsItemSelected方法的结果
onOptionsItemSelected方法应该看起来如下:
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case INSERT_ID:
createNote();
return true;
}

return super.onOptionsItemSelected(item);
}

第11步

增加一个新的createNote方法:
我们的程序的第一个版本,createNote()不会很有用。我们将会简单地创建新的带有标题的便签,假定标题基于计数器、便签内容是空的。目前,我们无法编辑便签的内容,因此现在我们毫无疑问是设置默认值的内容:
1、使用Note和计数器来构造名称,我们在类中定义String noteName = "Note " + mNoteNumber++
2、调用mDbHelper.createNote(),使用noteName作为标题,内容就写为""
3、调用fillData(),组装便签列表(低效但是简单)——我们将会创建这个方法
createNote方法看起来如下:
private void createNote() {
String noteName = "Note " + mNoteNumber++;
mDbHelper.createNote(noteName, "");
fillData();
}

第12步

定义fillData方法:
这个方法使用SimpleCursorAdapter,SimpleCursorAdapter获取一个数据库游标,绑定到界面中提供的字段上。这个字段定义了我们列表的行元素(这个例子中,我们使用notes_row.xml界面的text1字段),因此允许我们轻易地使用数据库中的数据项来组装列表。
我们必须提供一个映射,从返回的游标的标题字段映射到我们的text1文本视图中,定义了两个数组:第一个,字串数组,映射到列(这个例子中,仅仅标题,来自常量NotesDbAdapter.KEY_TITLE),第二个,整型数组,包含到绑定了数据的视图(R.id.text1文本视图)的引用
这是一个较大块的代码,因此让我们首先看看:
private void fillData() {
// Get all of the notes from the database and create the item list
Cursor c = mDbHelper.fetchAllNotes();
startManagingCursor(c);

String[] from = new String[] { NotesDbAdapter.KEY_TITLE };
int[] to = new int[] { R.id.text1 };

// Now create an array adapter and set it to display using our row
SimpleCursorAdapter notes =
new SimpleCursorAdapter(this, R.layout.notes_row, c, from, to);
setListAdapter(notes);
}
以下是我们已经做得事情:
1、当从mDbHelper.fetchAllNotes()获得游标后,我们使用一个称为startManagingCursor()的活动的方法,该方法允许Android照看游标的生命周期,而不需要我们担心她。(我们将会在练习3中牵涉生命周期,但是现在仅仅知道:这让Android为我们做一些资源管理的事情)
2、然后,我们创建一个字符串数组映射到标题,一个整型数组映射到视图
3、下一步是实例化SimpleCursorAdapter。象Android的许多类一样,SimpleCursorAdapter需要Context,因此,我们传入上下文环境参数(因为Activity的子类实现了Context)。我们传递notes_row视图(作为数据容器创建),游标(刚刚创建的),数组。
将来记住,从列到资源的映射用于决定两个数组的顺序。如果我们想绑定有更多的列,对应的有更多的视图绑定,那么我们会按顺序指定它们,例如:我们可能使用{ NotesDbAdapter.KEY_TITLE, NotesDbAdapter.KEY_BODY }和{ R.id.text1, R.id.text2 }来绑定两个字段进入记录行(而且,我们也会需要在notes_row.xml中定义text2,针对内容字符串)。这是绑定多个字段到单记录的方法(而且,也获得一个自定义的记录行的界面)。
If you get compiler errors about classes not being found, ctrl-shift-O or (cmd-shift-O on the mac) to organize imports.
如果你看到这个类的编译错误是没有找到,那么按ctrl-shift-O 来组织import。

第13步
运行你的程序
1、右击Notepadv1项目
2、从弹出菜单中选择Run As > Android Application.
3、如果你看到一个对话框出现,选择Android发射器作为运行程序的方法(你也能使用对话框顶部附近的链接,设置你的工作区的默认启动方式:这种方式是推荐的因为每次回询问你是否暂停插件)
4、通过点击menu按钮选择Add Item菜单,来增加新的便签

2008年12月12日星期五

教程:Notepad程序


教程:Notepad程序


本章的教程手把手介绍Android框架和工具,以便用之建立应用程序。从预先配置的项目文件开始,本教程引导你深入一下过程:简单的Notepad程序开发、提供设置项目的具体例子、开发程序逻辑和UI、编译运行程序。
教程作为一套练习介绍notepad程序的开发,每个联系由几个步骤组成。你可以按照每个联系中的步骤逐步建立和完善你的程序。这些联系详细解释每个步骤,并提供所有你需要的例子代码以完成程序。
当你完成教程时,你会成功创建正常运行的Android程序,并深入了解Android开发的更多重要概念。如果你想计入更多复杂功能到你的程序,你能通过notepad程序的可替换实现来检查代码,notepad程序在“ Sample Code”文档中提及。


谁应该使用这个教程

这个教程为有经验的开发人员设计,尤其是那些拥有JAVA编程知识的人。如果你没有编写过JAVA程序,你仍旧能使用这个教程,但是,你可能需要花费较多的时间,速度较慢。
这个教程假设你已经熟悉基本的Android程序概念和术语。如果你还不熟悉这些,你应该再继续以前阅读“Overview of an Android Application ”
也要注意:这个教程使用Eclipse开发环境,并安装有ADT插件。如果没有使用Eclipse,你能按照练习建立程序,但是你将会决定怎样在你的环境中完成和Eclipse相关的步骤。

准备练习

这个教程建立在“Installing the SDK ”、“Hello Android ”两篇文章的信息之上,这两篇文章详细解释了怎样设置你的开发环境。在你开始这个教程之前,你应该阅读这两篇文章,安装SDK,设置工作环境。
为了这个课程,准备如下:
1.下载项目练习数据包(zip)
2.解压到合适的位置
3.打开NotepadCodeLab文件夹

在NotepadCodeLab文件之内,你应该找到六个项目文件:Notepadv1, Notepadv2, Notepadv3, Notepadv1Solution, Notepadv2Solution 和Notepadv3Solution。Notepadv#项目是每个练习的起点,而Notepadv#Solutio项目是练习的解决方案。如果你在某个练习中有麻烦,你能通过比较你目前的工作和练习解决方案来解决。


练习

下表列举了教程练习,描述了每一条的发展领域。每个联系假定你已经所有前面的联系。

1、练习1
这里还是。构造简单的便签列表,让用户增加新的便签但是不能编辑。展示基本的ListActivity以及创建处理菜单选项。使用SQLite数据库存储便签。
2、练习2
增加第二个活动到程序中。展示构建新活动,增加活动到Android manifest文件,在两个活动之间传递数据,还有使用更高级的屏幕布局。还展示了怎么使用startActivityForResult()调用另外一个活动并返回一个结果.
3、练习3
增加生命周期事件处理到程序中,并让该事件维持生命周期内的程序状态。
4、练习4
展示怎样使用Eclipse调试器,怎样使用调试器来查看生命周期事件。本章是可选的但是强烈推荐。

其他资源以及进一步的学习

* 针对概念的更浅显更广泛的介绍并没有在教程中覆盖,请参考“Common Android Tasks”
* AndroidSDK包含各种各样具有完备功能的例子程序,有极好的机会进一步学习。你能在SDK的samples/目录下找到例子程序。
* 这个教程引入了完整Notepad应用程序,包含在SDK的samples/目录中,尽管它不能完全匹配。当你按照教程做的时候,强烈推荐你仔细看看Notepad程序的这个版本,它展示了多种有趣的补充,比如:
o 针对便签列表,设置一个自定义条纹列表
o 创建一个自定义文本视图,重载draw()方法以便使便签看起来像一个内忖的便签。
针对便签实现一个完整的ContentProvider。
o 还原、丢弃编辑,而不只是自动保存

2008年12月9日星期二

Android程序的解剖

Android程序的解剖

* Activity,即活动
* Broadcast Intent Receiver,即广播接收器
* Service,即服务
* Content Provider,即内容提供商

并不是每个程序都需要所有的这四个组件,但是你的程序将会使用这四个方面的某些部分组合而成。
一旦你已经决定你的应用程序需要的组件,你应该在AndroidManifest.xml文件中列举出来。AndroidManifest.xml是一个XML文件,你在其中声明你的程序的组件以及他们的能力和需求。请阅读“Android manifest file documentation”以了解更详细的资料。

Activity


活动(Activity)是Android生成块的最常用组件。一个活动通常是程序中的一个单一的屏幕。每个活动作为Activity基类的单个的继承类来实现。你的类将会显示一个UI,这个UI由Views(视图)以及对应的事件(event)组成。大多数程序由多个界面组成。例如,一个文本消息程序可能有一个展示发送消息目标的通讯录列表的界面,以及一个写消息到选中的通讯录的界面,还有查询旧消息或者修改设置的其他界面。这些界面的每一个都会被作为一个活动实现。移动到另外一个界面通过启动一个新的活动来完成。一些情况下,一个活动可能返回一个值给上一个活动——例如:一个让用户挑选图片的活动将会返回所选图片给呼叫者。
当一个新界面打开时,前一个界面暂停,放入一个历史堆栈中。用户能回退到历史堆栈中的前一个打开的界面。当不适合保留的时候,界面能从历史堆栈中选出来删除。Android为每个程序从主界面开始的所有界面保留历史堆栈。

Intent(计划)和计划过滤

Android使用一个特殊类调用Intent来从一个屏幕移动到另外一个界面。一个Intent描述了一个应用程序想做什么。intent数据结构的两个最重要部分是动作以及起作用的数据。动作的典型是MAIN(应用程序的入口)、VIEW、PICK、EDIT等。数据表达为一个URI。例如,为了浏览个人的通讯录信息,你会创建一个intent,该intent携带VIEW动作以及描述那个人的URI指向的数据。
有一个相关类称为IntentFilter。当意图(intent)是做什么事情的有效请求时,intent filter是说明活动(或者BroadcastReceiver)能处理的意图(intent),能显示个人通信录信息的活动将会发布一个IntentFilter,以说明活动知道当应用数据描述一个人时怎么处理动作VIEW。活动在AndroidManifest.xml文件中发布他们的IntentFilter。
从一个界面导航切换到另外一个界面是通过解析intent来完成的。为了向前导航,活动调用startActivity(myIntent)。然后系统搜索所有已安装程序的intent filter(意图过滤器),选择intent filter和myIntent最匹配的活动。新活动接到intent的通知,从而导致他启动。解析intent过程发生在运行时,此时startActivity被调用,提供了两个关键好处:
* 活动对于其他组件可以功能重用,只需简单的以Intent的形式发出请求。
* 活动能在任何时候被携带同等的IntentFilter的新活动替换。

广播接收器


当你想程序的代码用于外部事件的反馈时,你可以使用BroadcastReceiver,例如,电话铃声响时,或者,数据网络可获得时,或者午夜时分。BroadcstReceivers不显示UI,尽管他们可以使用NotificationManager来警告用户其感兴趣的事情是否已经发生。BroadcastReceivers在AndroidManifest.xml中注册,但是你也能使用Context.registerReceiver()从代码中注册。你的程序不一定在运行,因为BroadcastReceiver被调用;系统将会启动你的程序,如果必要的话,当BroadcastReceiver被触发的时候。程序也能通过Context.sendBroadcst()发送他们自己的intent广播到其他应用程序。


服务


服务是长期存活的没有UI运行的代码。好例子是从播放列表播放歌曲的媒体播放器。在媒体播放程序中,可能有一到多个活动供用户选择歌曲并开始播放。但是,音乐回放自己不应该被一个活动处理,因为用户希望音乐继续播放即使切换到一个新的界面。这种情况下,媒体播放器活动可使用Context.startService()启动一个后台运行的服务来保持音乐播放。然后,系统会继续运行音乐回放服务直到音乐播放完成。你可以通过阅读“Life Cycle of an Android Application”来了解系统服务的优先级。注意:你可以连接到服务(如果没有运行就启动它),使用方法Context.bindService()。当连接到服务时,你能通过服务暴露的接口和服务通信。对于音乐服务,这可能允许你暂停、重放等。


内容提供者


程序能保存数据到文件,一个SQLite数据库,或者,合理的其他任何机制。但是,内容提供者是有用的,如果你想你的程序的数据和其他程序共享。内容提供者是一个类,实现一个标准方法集,以便其他程序存取内容提供者能处理的的数据类型。
To get more details on content providers, see Accessing Content Providers.
为了了解内容提供者的详细情况,请参见“ Accessing Content Providers”

2008年12月3日星期三

升级UI到XML布局

升级UI到XML布局

你刚完成的“Hello,World”例子使用叫作“programmatic”(编码)UI布局,也就是你直接在源代码中构造和建立你的应用程序UI。如果你做过UI编程,你可能了解道路有时可能非常不容易的:布局小变动可能导致代码方面的大头痛。忘记恰当地融入VIew也是非常容易的,这将导致布局错误,并浪费时间来调试你的代码。
这就是Android提供另外一种UI构造模式:XML布局文件的原因。解释这种模式的最容易的方法是展示一个例子。下面是一个XML布局文件,其行为等同于你刚完成的基于编码构造的例子:

android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:text="Hello, Android"/>

一个AndroidXML布局文件的普通结构是简单的。它是一棵标记树,每个标记是View类的名称。这个例子是包含一个元素,TextView,的非常简单的树。你可以使用在XML布局文件中View扩展类的名称,包括你在自己代码中定义的自定义View类。这种结构使快速建立UI变得非常容易,相比你在代码中使用的结构和语法,更简单。这种模式对web开发模式更有吸引力,你能隔离应用程序的表示层(UI)和逻辑层(存取数据)。
这个例子中,有四个XML属性。下面总结他们的意思:
(1)xmlns:android
这是一个XML名称空间声明,告诉Android工具:你打算引用在Android名称空间定义的通用属性。在每个Android布局文件中的外层标签必须有这个属性。
(2)android:layout_width
这个属性定义了View使用的屏幕的有效宽度是多少。这个例子中,他是唯一的VIew,所以我们想它占用整个屏幕,“fill_parent”就是这个意思。
(3)android:layout_height
这个属性很像android:layout_width,只是用于规定有效高度。
(4)android:text
这个属性设置TextView应该包含的文本内容。在这个例子中,他是我们司空见惯的"Hello, Android" 信息。

所以,那就是XML布局文件看起来的样子,但是你把它放在哪里呢?放到你的项目下/res/layout目录下。“res”是资源单词的缩写,这个目录存放你的程序需要的非代码资源,包括图片、本地化字符、XML布局文件。
ADT插件为你创建一个XML文件。在我们的上面的例子中,我们简直从来没有使用它。在Package浏览器总,展开文件夹/res/layout,编辑文件main.xml,使用上面的文字提供main.xml的内容然后保存。
马上打开文件r.java,这个文件在Package浏览器的源代码文件夹中。你将会发现它现在看起来像这样:
public final class R {
public static final class attr {
};
public static final class drawable {
public static final int icon=0x7f020000;
};
public static final class layout {
public static final int main=0x7f030000;
};
public static final class string {
public static final int app_name=0x7f040000;
};
};

项目的R.java文件是一个指向文件中定义的所有资源的索引。你在你的源代码中使用这个类,作为一种快捷引用你项目中的资源的方式。这是特别强大的代码完成功能,该功能存在于诸如Eclipse的集成开发环境中,之所以强大是因为他让你快速地、互动地定位你正在寻找的具体引用。
现在要注意的重要事情是名叫“layout”的内嵌类,以及他的成员“main”。ADT插件注意到你增加了一个新的XML布局文件,然后产生了这个r.java文件。当你加入其他资源进入你的项目时,你会发现r.java会跟着改变。
你需要做的最后的事情是使用你的UI的新XML版本而不是硬编码版本来修改HelloAndroid源代码。下面是你的新类看起来的样子,正如你看到的一样,源代码变得非常简单:
package com.android.hello;

import android.app.Activity;
import android.os.Bundle;

public class HelloAndroid extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}

当你做这个修改的时候,不要仅仅拷贝粘贴进入。在那个R类上试试代码完成功能。你可能会发现它会帮助你许多。
既然你已经做了修改,进一步重新运行你的程序,你需要做的所有事情是点击绿色的Run箭头图标,或者选择菜单Run > Run History > Hello, Android。你应该看到...好,和你以前看到的是完全相同的界面!毕竟,这一点是要表明,两种不同的布局方式产生了相同的结果。
创建XML布局文件有更多的知识,但是我们在这里只介绍这么多了。想要知道这种强力方式的更多信息请参见“Implementing a User Interface”。

调试你的工程项目

ADT插件也和Eclipse调试器进行了完美集成。为了证明这点,让我们介绍我们代码的一个BUG。像这样修改你的代码:
package com.android.hello;

import android.app.Activity;
import android.os.Bundle;

public class HelloAndroid extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Object o = null;
o.toString();
setContentView(R.layout.main);
}
}

这个修改引入了一个NullPointerException异常。如果你再次运行你的程序,你最后会在模拟器上看到:


按"Force Quit"来终止应用程序,并关闭模拟器窗口。

为了找出更多的错误,在Object o = null;行上设置断点,设置断点的操作是双击紧靠源代码行的标记栏。从主菜单中选择菜单Run > Debug History > Hello, Android进入调试模式。你的应用程序会在模拟器中重新启动,但是这次她会挂起在你设置的断点处。你能在Eclipse调试视图中单步调试你的代码,正如你为其他任何程序做的事情一样。


没有Eclipse时创建工程项目

如果你没有使用Eclipse,比如如果你喜欢其他IDE,或者简单地使用文本编辑器和命令行工具,那么Eclipse插件不能帮助你。但是,不要太担心,你不会因为不使用Eclipse而损失任何功能。
Android插件实际只是对包含在AndroidSDK内的一套工具的封装,这套工具包括模拟器、AAPT、ADB、DDMS,以及其他在别的地方提到的工具。因此,使用诸如ant建立文件之类的别的工具封装这些工具是可能的。
AndroidSDK包含一个称为“activitycreator.py”的Python描述脚本,这个脚本用于为你的工程项目创建所有的源代码和目录桩,又兼容ant的build.xml。这允许你从命令行建立你的工程项目,或者和你选择的IDE集成。
例如,为了创建一个HelloAndroid项目,和你在Eclipse中创建的一样,你使用如下的命令:
activitycreator.py --out HelloAndroid com.android.hello.HelloAndroid

为了建立项目工程,你运行命令“ant”。当命令成功完成时,你会放置一个文件helloAndroid.apk到bin目录下。.apk文件是一个Android包,能使用adb工具在模拟器中安装和运行。
欲知更多信息,请阅读上面引用的文档。

2008年11月26日星期三

转:从《西游记》和《魔戒》看东西方项目管理

打开电视,电视里正在播放《魔戒第一部.护戒使者》。面对墨多的黑君主索伦无以伦比的可怕力量,一个义无反顾的护戒小队成立了,他们的目标就是墨多的核心地带——末日火山。这看起来就像是一个不可能完成的任务,一开始就面临着难以逾越的劫难。这让我想起了我们的神怪小说《西游记》,同样是一个团队,同样是面临千难万险的征途,但是,中西方文化的差别却在这里表现得淋漓尽致。

首先,我们看看这两个团队的成立

“你挑着担,我牵着马,迎来旭日送走晚霞。。。。”这首歌好像描绘了一个非常和谐的,分工明确、责任清晰的最佳合作团队。但是,这个团队起初只有唐僧一个人,只有唐僧才真正明白此行的目的,一行四人中最坚决的取经者仅此一人而已。其他几位包括白龙马、孙悟空、猪八戒、沙和尚等基本上都是连哄带骗外加紧箍咒强行拉壮丁拉进来的,这几个人各打各的主义,各有各的心思,可以这样说:他们怀着不同的目的,为了同一个目标,走到了一起。

而护戒使者小分队的成立,则完全是另外一个样子。首先在精灵国的林谷会议上,大家对面临的问题非常清晰,索伦要找回魔戒,得到魔戒的索伦将成为不可战胜的力量。那样的话,人类、精灵、矮人、侏儒等一切爱好和平的种族将会面临灭顶之灾,解决问题的唯一办法就是将魔戒带到墨多的核心末日火山,然后将魔戒丢到火山中将其毁灭。但是,达到这个目的的困难是完全不可想象的,其难度完全不亚于去西天取经。这时候,英勇无畏的勇士们站了出来。游侠阿拉贡、战士博罗米尔、精灵莱格拉斯、矮人金利、巫师甘道夫以及几个以弗罗多为首的霍比特人,九个人的护戒使者可以说目的非常明确(皮平除外),而且完全是自发的主动的建立起这个团队。这个团队没有明确的队长,只有带戒人和护戒使者,遇到事情基本上采取协商解决。

从团队建设来看,东方文化带有明显的阶级因素。最高阶级是神,如来佛、观音菩萨、玉皇大帝等,神是法力无边的,与神斗,绝没有好下场,齐天大圣够厉害了吧?最后还不是逃不出如来佛的手心,如来佛一翻手掌就将他压在五界山下永世不得脱身。第二个阶级是师傅唐僧,唐僧作为神的代言人,具有管辖和掌握手下四个徒弟的特权,他忠实地行使神赋予他的权力和义务。但是神给他的权力是明确的,有限的,他只能管理这几个徒儿,除此之外,即便是面对狐狸兔子等最下等的小妖,他也无能为力。再下面是三个人形的徒弟,尽管他们个个长的实在没个人样,但是与连人形也没有修得的白龙马相比,他们显然要高一个阶级。在这个第三阶级里面,每个人的地位也是不平等的,因为唐僧给他们排了座次,大师兄悟空,二师兄八戒,三师弟沙僧,人人都有自己的位置安排,遇到事情大家依据自己的位置发表议论。如果谁违背了这个顺序,后果将不堪设想。唐僧曾经听信了八戒的谗言,忽视了大师兄悟空的权利,孙悟空越想越气,最后一怒之下撂挑子不干了。可见,在西游团队中,需要论资排辈,每个人既定的地位、座次都是非常重要、不可动摇地。最后一个阶级就是大家的奴隶——白龙马。白龙马说起来也是龙的传人,可惜他被神定位为马,从此只能负重行走,没有说话的权利,连吃饭也没份,是一个活脱脱的奴隶。

而西方文化,更看重个人的尊严。在护戒使者这个团队中,每个人都有他存在的价值,他们的地位是平等的。开始时,山姆是弗罗多的仆人,但是弗罗多以及其他小组成员从没有把他当奴隶看,最后,随着大家一起克服重重困难,弗罗多完全把山姆当成了自己的亲兄弟。因为没有不可逾越的阶级,人与神的界限就没有那么明显,黑君主索伦是强悍无比的黑暗势力,但是,只要你有足够的勇气,即便你是一个普通人,一样有可能打败他。

其次,我们来看一看两个团队如何处理所面对的困难

西游团队碰到困难时,师傅是不出面的。就像大话西游里唱得那样:“背黑锅我来,送死你去!”唐僧作为领导,当然是养尊处优的,碰到难处理的事情,你们三个去想办法。这时候,这三个人的责任便很明确,大师兄地位高,自然承担的责任也最大,降妖除怪一般都是大师兄责无旁贷。孙悟空也从来不推托,总是第一个冲了上去,这时候东方文化的良好秩序发挥了效率。由于地位清晰,责任明确,大家该干什么干什么,碰到妖怪,唐僧只要说一句:“悟空,你去看看!”接下来降妖机器便自动运转起来,来一个妖怪是这个打法,来一百个妖怪,也是这个打法。每个人对自己的工作都驾轻就熟,孙悟空自然是打妖的主力,八戒主要保护师傅,有时候是机动力量,由于能力有限,常常充当第二梯队打打二流小妖;沙僧地位最低,责任也最小,只要看好行李物品不要丢失就行了。白龙马是奴隶,没有地位,自然也没有责任,打不打妖和他没有任何关系,谁也不会把责任推到他的头上。

而护戒使者队伍因为大家地位相同,因此碰到怪物的时候,只要喊一声:“大家并肩子上啊!”便人不分老幼,地不分南北一拥而上,打得个不亦乐乎。虽然大家的能力是不一样的,但是杀敌的勇气却是一样的,面对蜂拥而至的妖怪,谁也不能够独善其身,只能拿起武器战斗。这种同进同退的战斗生活,能够加深团队成员之间的友谊,实际上这个团队最终大家都成了生死兄弟。一个兄弟般的团队,其战斗力自然会更强,正所谓:“兄弟同心,其利断金!”正是这个互相关心,共同努力的团队,面对一次次令人绝望的危险,都能够涉险过关。

这两种不同的态度,所造成的后果也是各有千秋。东方人讲秩序,讲责、权、利分工明确,“各人自扫门前雪,莫管他人瓦上霜”这句话便是东方人处世态度的最好写照。作为团队的管理者,最核心的问题就是赏罚分明,只有纪律严明赏罚分明的队伍才是一个有能力的团队。而西方人鼓动人的主动性、积极发挥个人的创造力,要求团队更主动,更合作,团队管理的核心是激发每个人的主观能动性与创造力。


第三,我们再看看两个团队处理问题的方式

西游团队碰到危险,总是按照一定的思维方式来处理。首先,孙悟空会看到一股妖气,或者孙悟空的火眼金睛看出对方是妖怪,然后孙悟空先上去斗法。如果斗法成功,则为民除害,大家继续上路。如果斗法不成功,十有八九唐僧师徒被捉,这时候悟空总是有法子脱身,然后便会去找更厉害的帮手。通常帮手都是是天上位列仙班的大罗神仙,或者观世音菩萨的同道中人。而那些妖怪十有八九都是这些人的家眷、门人或者家禽家畜等等,因为有了这一层关系,便下界为祸人间。对待这些妖怪,孙悟空是搞不定的,因为他们出身高贵,必须找到 他们的上级才能够把问题解决。

西游记的九九八十一难,十有八九是因为高层领导的裙带关系,家属腐败等等原因造成的。所以,尽管取经路上危险重重,但是大家心里并不担心,为什么?因为一切尽在掌握中。所有的磨难或者危险,都是有因有果,都逃不出这四大阶级的天罗地网。只要这个阶级秩序能 够保持稳定,一切困难都会迎刃而解。

因此,西游团队面对问题的时候,他们首先回来分辨这个妖怪到底是什么来历?也就是说,妖怪的出身很重要,知道了妖怪的出身,问题也就解决了一大半了,接下来无非是怎样起出妖怪背后的最大靠山而已。这也符合东方文化的谋定而后动的操作要领,最后收拾妖怪,不过是整个行为的收关之作,整个降妖过程,很多工作都在幕后进行。所谓“功夫在诗外”,诗写道好的关键是你能够有足够的素材。通过自己的人情脉络找出那个真正的谋后主使,才是孙悟空能够降妖除魔的看家本领。

护戒使者所碰到的磨难,往往是突发的,没有任何征兆的,也绝不可能通过外援来调解的。他们没有一个掌控全局的神来主持大局,有的只是正义和邪恶两个完全不可调和的对立面,要么杀死敌人,要么被敌人杀死。水怪、炎魔、布鲁克武士、狼骑士,所有的敌人都是呼啸而来。应付的办法也是出奇的简单,拿起武器拼个你死我活。西方文化当中的正义与邪恶观点相对来说要幼稚的多,与西游团队不同,护戒使者面对敌人时,采取最直接也最有效的解决办法。在这种情况下,一切的运筹帷幄、一切的人情世故都是没用的,取胜只能靠自己的 力量、勇气、信心和敏捷的反应能力。

在应急处理问题的方式上,东西方文化截然不同的反应,给我们很大的启示。面对困难,西方人总是鼓励你主动地、勇敢地去尝试,或许你的方法完全不对,但是只要你有勇气尝试去解决问题,你就会得到尊重。西方文化更看重你解决问题的态度。与此相反,东方人在面临巨大困难的时候,他们首先考虑的是如何解决,或者如何选择最恰当的方式来解决问题。而不是贸然动手,如果你贸然动手,一旦你失败了,可能会招致大量的批评。

这种解决问题的思维方式,已经深深的烙在每一个人的心里。我们在教育方面,会花费大量的精力来告诉学生事物的原理,我们有十万个为什么?我们有格物致知的祖训,我们每个人都在探索事情的规律和论证最佳的解决方案。我们唯独忘了鼓励小朋友大胆去尝试自己的想法;我们总是告诉小朋友正确的解决方法,但是却忘了告诉小朋友如何通过自己的尝试获得正确地解决方案。授人以鱼不如授人以渔,很多人已经发现了我们教育方面的问题,但是扭转局面谈何容易,因为我们的文化根源与西方文化是那么的格格不入。我们的高考制度,考的是你能否给出问题的正确答案,而不是你能否以正确的方式来回答问题。高考制度不改变,模仿西方的所谓素质教育将永远是一句空话。

扯远了,我想如果西游一行碰到得九九八十一难都是像护戒使者面对的那样的敌人的话,唐僧可能已经死了七八十回了。但是,你放心,东方的妖怪也是东方化的。当他们捉住了唐僧,通常都不会便吃,而是要弄清楚齐天大圣走了没有?危险过去了没有?有的妖怪更是可爱的很,他们会很有孝心的邀请自己的长辈来共食,这样的妖怪自然给悟空留下了足够做好后台工作的时间,足以让你找到解决问题的法门。东方的妖怪还是比较仁义的,抓住唐僧后会对悟空说:不是我没给你机会阿,你要抓紧时间来救你师傅啊,你不来救可就莫怪我嘴下不 留情了。

第四,两个故事的结局

西游的结局大家都知道,五个人取经成功修的正果,都脱离苦海成了佛。而护戒使者一行人,除了为此事业献身的波罗米尔,其他人奋斗的结果便是成功阻止了索伦的强大,其结局非常简单。

从两个故事的结局可以清楚地看到,东方人功利思想要重得多,尽管当初一行人各怀鬼胎走上去西天取经之路。但是,当取经成功之后,孙悟空也老是不客气地做起了佛爷,那花果山的猴头看样子到底没有做佛爷来的风光啊。

但是在西方人眼里,成功就是任务的完成,对成功者来说,这件事情本身就够他自豪一辈子了,其他虚名则可有可无。

近年来,西方拍了很多的灾难片,有火山爆发的,有龙卷风的,有地震海啸的,有暴风雪的。在这些片子里,主人公无一例外的尝试去对抗大自然的力量。人类还没有取得应付自然灾害的能力,这时候面对它和解决它的勇气非常重要。东西方文化各有所长,但是,在我们这一代以及我们的下一代,在面对突发事件的情况下,我们的应变能力显然已经落于人后。

从西游记与魔戒这两部神怪小说分析,东西方文化有他根源上的巨大差异。东方文化胜在秩序的稳定和对事物原理孜孜不倦的追求上。而西方文化在人性的尊重,个性化创造力的培养,以及鼓励主动尝试等方面略高一筹。二十一世纪是全球一体化时代,东西方文化的融合将成为大家关心的主题。为了我们的下一代,西方一些优秀的教育理念是值得我们研究和学习的。

Hello, Android!

Hello, Android!

作为一个开发人员,你得到一个开发框架的第一映像是编写“Hello, World!”程序是否容易,对于Android,他是非常容易的。

创建项目

创建项目能有多容易就多容易。获得Eclipse插件使Android开发非常容易。
你需要安装有EclipseIDE的开发计算机,你还需要安装ADT插件。一旦你准备好了,你现在就可以开始进入开发。
首先,下面是高度概括的建立"Hello, World!"程序的方法:
1)在菜单File > New > Project中创建一个新的"Android Project"
2)在New Android Project对话框中填写项目详细信息
3)编辑自动产生的源代码模板以便使程序显示一些输出信息
这就是全部!下一步,让我们详细进入每一步。

1、创建一个新的Android项目
从Eclipse中,选择菜单File > New > Project。如果ADT插件成功安装,结果对话框应该有一个标记为“Android”的文件夹,该文件夹包含一个简单入口:"Android Project"。
一旦你选择了"Android Project"文件夹,点击Next按钮。
2、填写项目详细信息
下一步的屏幕允许你填写项目详细信息。下面是例子:


以下列出这个屏幕的每个字段的意思:
(1)Project Name
这是存放该项目的目录或者文件夹名称。
(2)Package Name
这是包的名称空间(JAVA语言针对包使用同样的规则),你的所有代码均驻留在该命名空间中。同时也设置了即将产生的桩活动下面的包名称
你用在应用程序中的包名称必须在系统安装的所有包中唯一;基于这样的原因,你的应用程序使用标准域风格的包是非常必要的。上面的例子中,你使用了包域“com.android”;你应该使用一个不同的名字,根据你的组织做适当变化。
(3)Activity Name
这是类桩的名称,类桩(class stub)由插件产生。这是Android活动的子类。一个活动是一个能运行和完成一定工作的简单类。如果选择的话,能创建一个UI,但是不是必要的。
(4)Application Name
这是你的应用程序供使用者查看的标题。

复选项切换选项“Use default location”允许你改变磁盘位置,该位置用于项目文件产生和保存。

3、编辑自动产生的源代码

插件运行后,你将会有一个称为HelloAndroid的类,位于包HelloAndroid > src > com.android.hello。它看起来是这样的:
public class HelloAndroid extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}

现在,你能马上运行这些代码,但是让我们更进一步,我们应该理解到底发生了什么事情。下一步让我们修改一些代码!

建立UI

仔细看看下面的这些修改过的代码,在你的HelloAndroid.java文件中做同样的修改。我们会一行一行的剖析他们:
package com.android.hello;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class HelloAndroid extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
TextView tv = new TextView(this);
tv.setText("Hello, Android");
setContentView(tv);
}
}

提示:如果你忘记引入TextView包,请试着做:按Ctrl-Shift-O (Cmd-Shift-O, 在 Mac操作系统上),这是一个Eclipse组织引入操作的快捷键——他自动识别丢失的包并为你自动加入。

Android的UI由叫做Views类的体系构成。一个View是一个简化的绘图对象,比如单选按钮、动画、文本标签。处理文本的View子类的名称为TextView。
下面是你建立TextView的方法:
TextView tv = new TextView(this);

TextView的构造函数的入参是Android Context实例。Context是系统的句柄;它提供诸如资源解析、数据库访问、参数选取等服务。Activity类继承自Context。因此,我们的HelloAndroid类是Activity的子类,它也是一个Context,所以我们能把this引用传送给TextView。
一旦我们建立了TextView,我们需要告之显示什么:
tv.setText("Hello, Android");

这里没有什么太令人惊讶的。
在此处,我们建立了TextView,并告之显示什么文本内容。最后一步是连接这个TextView到屏幕显示,像这样:
setContentView(tv);

在Activity中的setContentView方法指示系统和Activity的UI联系起来。如果一个Activity没有调用这个方法,根本没有UI出现,系统将会显示一个空白屏幕。为了我们的目的,我们想的所有事情是现实一些文本,因此我们传送文本给才创建的TextView。
这就是Android的“Hello, World”!下一步我们来看看他怎样运行的。


运行代码:Hello, Android

ADT插件非常容易运行应用程序。选择菜单Run > Open Run Dialog开始,Eclipse3.4,选择菜单Run > Run Configurations。

下一步,选中"Android Application"条目,然后点击左上角的图标(在角上画有加号的一张纸),或者双击"Android Application" 入口。这样,你就有了一个叫“New_configuration”的新的入口条目。

修改名称来表达要做的事情,比如"Hello, Android",然后双击Browse按钮来选取你的项目。(如果你在Eclipse中打开了不止一个项目,保证你选取正确的一个。)
插件自动扫描你的项目以寻找活动子类,自动添加找到的每一个到"Activity:" 标签下的下拉列表中。因为,你的 "Hello, Android"仅仅有一个,所以它将会是默认的一个活动子类,你可以继续下面的步骤。
点击“Apply”按钮。

那就是他,大功告成!点击Run按钮,Android模拟器应该会启动。一旦模拟器启动,你的应用程序将会显示出来。当说过和做过这一切,你应该会看到这样的事情:
在Android中显示“Hello, World”。非常简单。教程的下一章提供更多的详细信息,你可以找到你能更多了解Android的有价值的内容。