In this exercise, you will construct a simple notes list that lets the user add new notes but not edit them. The exercise demonstrates:
- The basics of
ListActivities and creating and handling menu options.
- How to use a SQLite database to store the notes.
- How to bind data from a database cursor into a ListView using a SimpleCursorAdapter.
- The basics of screen layouts, including how to lay out a list view, how you can add items to the activity menu, and how the activity handles those menu selections.
通过这个练习,你将会构造一个简单的只能让用户写东西,但不能编辑的记事本。
这个练习演示了:
ListActivities 基础和创建处理菜单项
- 使用SQLite保存数据
- 使用一个简单的指针适配器把数据从一个数据库指针转换成一个列表
- 屏幕排版的基本原理,包括排版一个列表数据,如何添加一个条目到activity(指当前活动的窗口啦)的菜单里,和activity如何控制那些菜单选项的
下面的懒得翻译了,自己慢慢看。。。。
md。。还是翻译下。。。认真做一遍。。。
Step 1
Open up the Notepadv1 project in Eclipse.
Notepadv1 is a project that is provided as a starting point. It takes care of some of the boilerplate work that you have already seen if you followed the Hello Android tutorial.
- Start a new Android Project by clicking File > New > Android Project.
- In the New Android Project dialog, select Create project from existing source.
- Click Browse and navigate to where you copied the
NotepadCodeLab (downloaded during setup). Select Notepadv1 and click Choose.
- You should see
Notepadv1 in the Project name and also see the Location filled in with the path you selected.
- Click Finish. The
Notepadv1 project should open and be visible in your Eclipse package explorer.
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. (The project is looking in the wrong location for the library file, this will fix it for you.)
Step 1
在eclipse里打开 Notepadv1项目
Notepadv1是一个起点,如果你已经看过Hello Android教程,那你应该知道他处理了类似吃饭前洗碗的事情(做准备工作啦)。
- 通过点击 File > New > Android Project.开始一个新的android项目
- 选择 Create project from existing source.
- 点击Browse ,然后导航到你放着
NotepadCodeLab ( setup阶段下载的文件)的地方. 选择 Notepadv1 .
- 你将看到Project name是 Notepadv1,然后看到Location字段填这你选择的路径(废话)
- 点击 Finish.
Notepadv1 将被打开并在你的eclipse包管理器里可见。
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. (The project is looking in the wrong location for the library file, this will fix it for you.)(这段说如果有错误该怎么办啦,貌似他提供的方法没用,我的错误是重复定义了R类,直接把其中一个文件删掉就可以了)
Step 2
Accessing and modifying data
For this exercise, we are using a SQLite database to store our data. This is useful if only your application will need to access or modify the data. If you wish for other activities to access or modify the data, you have to expose the data using a ContentProvider.
If you are interested, you can find out more about content providers or the whole subject of Storing, Retrieving, and Exposing Data. The NotePad sample in the samples/ folder of the SDK also has an example of how to create a ContentProvider.
Take a look at the NotesDbAdapter class — this class is provided to encapsulate data access to a SQLite database that will hold our notes data and allow us to update it.
At the top of the class are some constant definitions that will be used in the application to look up data from the proper field names in the database. There is also a database creation string defined, which is used to create a new database schema if one doesn’t exist already.
Our database will have the name data, and have a single table called notes, which in turn has three fields: _id,title and body. The _id is named with an underscore convention used in a number of places inside the Android SDK and helps keep a track of state. The _id usually has to be specified when querying or updating the database (in the column projections and so on). The other two fields are simple text fields that will store data.
The constructor for NotesDbAdapter takes a Context, which allows it to communicate with aspects of the Android operating system. This is quite common for classes that need to touch the Android system in some way. The Activity class implements the Context class, so usually you will just pass this from your Activity, when needing a Context.
The open() method calls up an instance of DatabaseHelper, which is our local implementation of the SQLiteOpenHelper class. It calls getWritableDatabase(), which handles creating/opening a database for us.
close() just closes the database, releasing resources related to the connection.
createNote() takes strings for the title and body of a new note, then creates that note in the database. Assuming the new note is created successfully, the method also returns the row _id value for the newly created note.
deleteNote() takes a rowId for a particular note, and deletes that note from the database.
fetchAllNotes() issues a query to return a Cursor over all notes in the database. The query() call is worth examination and understanding. The first field is the name of the database table to query (in this case DATABASE_TABLE is “notes”). The next is the list of columns we want returned, in this case we want the _id,title and body columns so these are specified in the String array. The remaining fields are, in order: selection, selectionArgs, groupBy, having and orderBy. Having these all null means we want all data, need no grouping, and will take the default order. See SQLiteDatabase for more details.
Note: A Cursor is returned rather than a collection of rows. This allows Android to use resources efficiently — instead of putting lots of data straight into memory the cursor will retrieve and release data as it is needed, which is much more efficient for tables with lots of rows.
fetchNote() is similar to fetchAllNotes() but just gets one note with the rowId we specify. It uses a slightly different version of the SQLiteDatabase query()method. The first parameter (set true) indicates that we are interested in one distinct result. The selection parameter (the fourth parameter) has been specified to search only for the row “where _id =” the rowId we passed in. So we are returned a Cursor on the one row.
And finally, updateNote() takes a rowId, title and body, and uses a ContentValues instance to update the note of the given rowId.
Step 2
访问并修改数据
在这个例子里我们将使用SQLite来保存我们的数据。如果你的应用程序需要访问并修改数据那这个很有用哦 .如果你想要让别的 activities 来访问或修改我们的数据,你需要通过一个ContentProvider曝光(公布)他。
如果你对这方面兴趣,你可以搜索跟contentprovider和 Storing, Retrieving, and Exposing Data有关的信息。 NotePad 例子里在SDK目录的samples/目录里也有个例子关于如何创建一个lso has an example of how to create a ContentProvider.
我们研究下 NotesDbAdapter 类—这个类提供了一个对保存着我们的记事本数据的SQLite数据库访问的封装,并提供给我们更新数据的接口。
在类的最开头是一些我们在应用程序里用到的常量定义,这些是用来从数据库里查找数据的属性定义(其实就是数据库字段啦). 同时还要一个数据库创建字符串定义,如果数据库还不存在的话,就会用来创建咯(废话).
我们的数据库名是 data, 然后只有一个表叫 notes, 这个表有3个字段: _id,title and body. _id是一个有下滑线(其实是在AndroidSDK里使用了很多次的习惯啦,就是你也可以不用下划线,不过还是用比较好,这样比较统一)的用来帮忙跟踪数据的(其实就是针对每条数据,有个唯一的标记啦)。 当查询或更新数据库时通常会用到,另外2个字段用来保存数据的文本字段.
NotesDbAdapter构造器有一个上下文参数,这样使得他可以跟Android操作系统的某些相关方面联系到一起. 通过某种方法与Android操作系统取得联系在类里很常见. Activity 类实现了Context 类,所以你在你的Activity里需要上下文时,你只要调用this就行了(其实这段就是说某个类如何跟操作系统级别数据进行通信啦,就是通过context参数,因为Activity类是context类的实现,所以在activity类里调用this指针就ok了)
open()方法为我们创建并打开一个数据库,他首先调用了DatabaseHelper的一个实例,DatabaseHelper是SQLiteOpenHelper类在我们本地的一个实现。 然后调用了 getWritableDatabase()。
close() 方法关闭数据库,释放跟这个数据库连接有关的资源。
createNote()方法 需要输入新记录的title和body的字符串这2个参数,然后在数据库里创建该记录。并且臆断这个新记录创建成功,这个方法返回新建记录的rowId,不成功返回-1。
deleteNote()方法 输入指定记录的rowId然后删除该记录。
fetchAllNotes()发出一个返回数据库里所有数据的指针的数据库查询语句。query()方法很有必要好好解释理解下。第一个字段是要查询的数据库表名(在这个例子里DATABASE_TABLE是"notes").下一个参数是我们想要的字段列表,在这个例子里我们需要the _id,title 和body 列,所以我们在string数组里指定了。剩下的参数依次是: selection, selectionArgs, groupBy, having and orderBy. 使得这些都是null意思是,我们需要所有的数据,不要分组,使用默认顺序。查阅 SQLiteDatabase 获得更详细的信息。
注意:返回一个指针比返回一个数据集好。这样使得Android能更有效的使用资源–不是直接扔一大堆数据到内存里而是在必要的时候获取和释放,当数据库表有很多数据时,这样做效率相对高很多。
fetchNote()方法类似 fetchAllNotes()方法,但是只获取一条我们输入的rowId对应的记录.他使用一个稍微有点不同的版本(指输入不同参数)的SQLiteDatabase query()方法。第一关参数(设置成true的)是指我们只要一个完全不重复的结果.selection 参数(第4个参数)被指定说只搜索”where _id =” 我们传入的 rowId 的行. 所以我们返回一个只有一行的指针。
最后,updateNote() 方法输入 rowId, title and body参数,然后使用一个ContentValues 实例来更新与我们指定的rowId对应的记录。
Step 3
Layouts and activities
Most Activity classes will have a layout associated with them. The layout will be the “face” of the Activity to the user. In this case our layout will take over the whole screen and provide a list of notes.
Full screen layouts are not the only option for an Activity however. You might also want to use a floating layout (for example, a dialog or alert), or perhaps you don’t need a layout at all (the Activity will be invisible to the user unless you specify some kind of layout for it to use).
Open the notepad_list.xml file in res/layout and take a look at it. (You may have to hit the xml tab, at the bottom, in order to view the XML markup.)
This is a mostly-empty layout definition file. Here are some things you should know about a layout file:
- All Android layout files must start with the XML header line:
<?xml version="1.0" encoding="utf-8"?>.
- The next definition will often (but not always) be a layout definition of some kind, in this case a
LinearLayout.
- The XML namespace of Android should always be defined in the top level component or layout in the XML so that
android: tags can be used through the rest of the file:xmlns:android="http://schemas.android.com/apk/res/android"
Step 3
布局和 activities
大多数的Activity类都会有一个布局跟他们相关。布局是activity给用户的界面。在这个例子里,我们的布局将使用整个屏幕并提供记录列表。
全屏布局并不是一个activity的唯一选择。你也可能使用浮动布局(比如一个对话框或提示),或者你根本不需要布局(activity将不可见除非你指定了某种布局给他用)。
打开在res/layout里的 notepad_list.xml 文件研究下他(你需要点击底部的xml标签才能查看xml的代码)。
这是一个几乎是空的布局定义文件。这里有些你需要知道关于布局文件的概念:
- 所有的Android布局文件必须以
<?xml version="1.0" encoding="utf-8"?>开始。
- 下一行一般是(但不一定啦,可能是别的)某种布局定义,在这里是一个
LinearLayout.
- 这个android的xml命名空间必须在最上层组件或布局里定义,这样android:tags才能在文件接下来的部分使用:
xmlns:android="http://schemas.android.com/apk/res/android"
Step 4
We need to create the layout to hold our list. Add code inside of the LinearLayout element so the whole file looks like this:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ListView android:id="@android:id/list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView android:id="@android:id/empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/no_notes"/>
</LinearLayout>
- The @ symbol in the id strings of the
ListView and TextView tags means that the XML parser should parse and expand the rest of the id string and use an ID resource.
- The
ListView and TextView can be thought as two alternative views, only one of which will be displayed at once. ListView will be used when there are notes to be shown, while the TextView (which has a default value of “No Notes Yet!” defined as a string resource in res/values/strings.xml) will be displayed if there aren’t any notes to display.
- The
list and empty IDs are provided for us by the Android platform, so, we must prefix the id with android: (e.g., @android:id/list).
- The View with the
empty id is used automatically when the ListAdapter has no data for the ListView. The ListAdapter knows to look for this name by default. Alternatively, you could change the default empty view by using setEmptyView(View) on the ListView.More broadly, the android.R class is a set of predefined resources provided for you by the platform, while your project’s R class is the set of resources your project has defined. Resources found in the android.R resource class can be used in the XML files by using the android: name space prefix (as we see here).
Step 4
我们需要创建一个布局来放我们的列表。添加代码到 LinearLayout元素里,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ListView android:id="@android:id/list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView android:id="@android:id/empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/no_notes"/>
</LinearLayout>
- 在
ListView 和TextView标签里的id字符串的@ 符号意思是XML解析器可以解释和扩展接下来的id字符串并当做ID资源来使用。
-
ListView 和TextView可以被认为是2个可选的视图,每次只有一个可以被现实。当记录被显示的时候使用ListView, 如果没有记录来显示的时候,textView将会被显示(在 res/values/strings.xml里定义了字符串资源 “No Notes Yet!” )
- 这个列表和空的IDs是由android平台提供给我们的,所以,我们必须给id加上android:前缀(比如,
@android:id/list).
- 当list适配器没有数据给listview时,这个空的id视图会被自动使用。list适配器知道查找默认的名字。你也使用 setEmptyView(View) 可以改变默认的空视图.另外,当你的项目的R类已经定义时,android.R类是平台预先定义好给你用的资源(??不知道啥意思).在android.R资源类里找到的资源可以在xml文件里通过android:name 前缀使用。
Step 5
Resources and the R class
The folders under res/ in the Eclipse project are for resources. There is aspecific structure to the folders and files under res/.
Resources defined in these folders and files will have corresponding entries in the R class allowing them to be easily accessed and used from your application. The R class is automatically generated using the contents of the res/ folder by the eclipse plugin (or by aapt if you use the command line tools). Furthermore, they will be bundled and deployed for you as part of the application.
To make the list of notes in the ListView, we also need to define a View for each row:
- Create a new file under
res/layout called notes_row.xml.
- Add the following contents (note: again the XML header is used, and the first node defines the Android XML namespace)
<?xml version="1.0" encoding="utf-8"?>
<TextView android:id="@+id/text1"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
This is the View that will be used for each notes title row — it has only one text field in it.
In this case we create a new id called text1. The + after the @ in the id string indicates that the id should be automatically created as a resource if it does not already exist, so we are defining text1 on the fly and then using it.
- Save the file.
Open the R.java class in the project and look at it, you should see new definitions for notes_row and text1 (our new definitions) meaning we can now gain access to these from the our code.
Step 5
资源和R类
在exlipse项目的res/目录下是资源。在此目录下目录和文件是一个有特定结构的.
在这些目录很文件下定义的资源将会与R类有相应的实体,同时让他们可以被你的应用程序很容易的访问和使用.这个R类是通过使用res/目录下内容被eclipse插件(或者aapt,如果你用命令行工具的话)自动产生的将来,他们会被绑定和部署到你的应用程序里的。
为了在listview里创建记录列表,我们需要为每一行定义一个视图:
- 在res/layout目录下创建一个文件
notes_row.xml.
- 添加以下内容 (注意: 这个xml头部再次被使用,第一个节点定义了android xml命名空间)
<?xml version="1.0" encoding="utf-8"?>
<TextView android:id="@+id/text1"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
这是每个记录标题行的视图,他只有一个字段。
在这里我们创建了一个新的id叫text1. id字符串里那个在@后面的加号表示这个id如果不存在可以被自动创建为一个资源所以我们很匆忙的创建并使用了他
- 保存文件
打开项目里的R.java类研究下他,你将会看到notes_row 和 text1的新定义,同时我们可以在我们的代码里有了访问的权限。
Step 6
Next, open the Notepadv1 class in the source. In the following steps, we are going to alter this class to become a list adapter and display our notes, and also allow us to add new notes.
Notepadv1 will inherit from a subclass of Activity called a ListActivity, which has extra functionality to accommodate the kinds of things you might want to do with a list, for example: displaying an arbitrary number of list items in rows on the screen, moving through the list items, and allowing them to be selected.
Take a look through the existing code in Notepadv1 class. There is a currently an unused private field called mNoteNumber that we will use to create numbered note titles.
There are also three override methods defined: onCreate, onCreateOptionsMenu and onOptionsItemSelected; we need to fill these out:
onCreate() is called when the activity is started — it is a little like the “main” method for an Activity. We use this to set up resources and state for the activity when it is running.
onCreateOptionsMenu() is used to populate the menu for the Activity. This is shown when the user hits the menu button, and has a list of options they can select (like “Create Note”).
onOptionsItemSelected() is the other half of the menu equation, it is used to handle events generated from the menu (e.g., when the user selects the “Create Note” item).
Step 6
下一步,打开源代码里的Notepadv1类. 在接下来的步骤里,我们将要改变这个类,让他变成一个使我们可以添加、显示我们的记录的list适配器
Notepadv1将会继承 Activity的一个子类 ListActivity,他包含关于处理list的额外的功能,比如:在屏幕上显示任意数量的list条目,移动条目,并使条目可选中。
查看下已经在Notepadv1类里的代码.有一个现在还没使用的私有字段mNoteNumber,我们会用它来创建数字化的记录标题。
也有3个覆盖的方法: onCreate, onCreateOptionsMenu 和onOptionsItemSelected; 我们研究下他们:
onCreate() 方法在一个activity开始时被调用—有点像一个activity的 “main”方法 .在一个activity运行时,我们用这个来设置 他的资源和状态。
onCreateOptionsMenu()方法用来为activity创建弹出式菜单,当用户点击菜单按钮时,他会显示一个选项列表(比如“创建记录”).
onOptionsItemSelected()方法是另一个菜单反应,他用来处理菜单产生的事件(比如,当用户选择“创建记录”时)。
Step 7
Change the inheritance of Notepadv1 from Activity to ListActivity:
public class Notepadv1 extends ListActivity
Note: you will have to import ListActivity into the Notepadv1 class using Eclipse, ctrl-shift-O on Windows or Linux, or cmd-shift-O on the Mac (organize imports) will do this for you after you’ve written the above change.
Step 8
Fill out the body of the onCreate() method.
Here we will set the title for the Activity (shown at the top of the screen), use the notepad_list layout we created in XML, set up the NotesDbAdapter instance that will access notes data, and populate the list with the available note titles:
- In the
onCreate method, call super() with the savedInstanceState parameter that’s passed in.
- Call
setContentView() and pass R.layout.notepad_list.
- At the top of the class, create a new private class field called
mDbHelper of class NotesDbAdapter.
- Back in the
onCreate method, construct a new NotesDbAdapter instance and assign it to the mDbHelper field (pass this into the constructor for DBHelper)
- Call the
open() method on mDbHelper to open (or create) the database.
- Finally, call a new method
fillData(), which will get the data and populate the ListView using the helper — we haven’t defined this method yet.
onCreate() should now look like this:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.notepad_list);
mDbHelper = new NotesDbAdapter(this);
mDbHelper.open();
fillData();
}
And be sure you have the mDbHelper field definition (right under the mNoteNumber definition):
private NotesDbAdapter mDbHelper;
Step 7
把 Notepadv1的继承类从 Activity改成 ListActivity:
public class Notepadv1 extends ListActivity
注意:你必须通过eclipse导入 ListActivity类到 Notepadv1 类,当你写上以上代码时用 快捷键ctrl-shift-O (win,linux), 或者cmd-shift-O (mac) (组织导入命令)将会为你做这些。
Step 8
研究下onCreate() 方法。
在这里我们要设置 Activity的标题 (显示在屏幕最上面), 用我们在xml文件里创建的notepad_list布局,设置 NotesDbAdapter实例来访问记录数据然后从列表里弹出可用的记录标题 :
- 在
onCreate方法里,传入savedInstanceState 参数调用super()
输入参数R.layout.notepad_list调用setContentView()
- 在类的最前面,创建了一个私有类字段NotesDbAdapter类
mDbHelper。
- 回到onCreate方法,创建一个新的
NotesDbAdapter 实例然后把它赋给mDbHelper字段(传递this 到DBHelper的构造器里)
- 调用
mDbHelper 的open() 方法来打开(或建立)数据库。
- 最后调用
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;
Step 9
Fill out the body of the onCreateOptionsMenu() method.
We will now create the “Add Item” button that can be accessed by pressing the menu button on the device. We’ll specify that it occupy the first position in the menu.
- In
strings.xml resource (under res/values), add a new string named “menu_insert” with its value set to Add Item:
<string name="menu_insert">Add Item</string>
Then save the file and return to Notepadv1.
- Create a menu position constant at the top of the class:
public static final int INSERT_ID = Menu.FIRST;
- In the
onCreateOptionsMenu() method, change the super call so we capture the boolean return as result. We’ll return this value at the end.
- Then add the menu item with
menu.add().
The whole method should now look like this:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
boolean result = super.onCreateOptionsMenu(menu);
menu.add(0, INSERT_ID, 0, R.string.menu_insert);
return result;
}
The arguments passed to add() indicate: a group identifier for this menu (none, in this case), a unique ID (defined above), the order of the item (zero indicates no preference), and the resource of the string to use for the item.
Step 9
研究下 onCreateOptionsMenu() 方法:
我们现在要创建当按下菜单按钮时显示的 “Add Item” 按钮.我们指定他显示在菜单第一个位置。
在strings.xml 资源(在res/values目录下),添加一个字符串”menu_insert”字段同时指定值为 to Add Item:
<string name="menu_insert">Add Item</string>
保存文件回到 Notepadv1.
- 在类最前面创建一个菜单位置常量:
public static final int INSERT_ID = Menu.FIRST;
- 在
onCreateOptionsMenu() 方法里,改下 super调用,我们就可以获得 boolean 返回值当做结果. 我们将会返回在最后返回这个值。
- 然后通过
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方法的参数说明: a group identifier for this menu (none, in this case), a unique ID (defined above), the order of the item (zero indicates no preference), and the resource of the string to use for the item.
Step 10
Fill out the body of the onOptionsItemSelected() method:
This is going to handle our new “Add Note” menu item. When this is selected, the onOptionsItemSelected() method will be called with the item.getId() set toINSERT_ID (the constant we used to identify the menu item). We can detect this, and take the appropriate actions:
- The
super.onOptionsItemSelected(item) method call goes at the end of this method — we want to catch our events first!
- Write a switch statement on
item.getItemId().In the case of INSERT_ID, call a new method, createNote(), and return true, because we have handled this event and do not want to propagate it through the system.
- Return the result of the superclass’
onOptionsItemSelected() method at the end.
The whole onOptionsItemSelect() method should now look like this:
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case INSERT_ID:
createNote();
return true;
}
return super.onOptionsItemSelected(item);
}
Step 10
研究下 onOptionsItemSelected() 方法:
这个是要处理我们的新 “Add Note”菜单项。当这个被选中onOptionsItemSelected() 方法将被item.getId()设置为INSERT_ID (我们用来标识菜单项的常量). 我们可以检测到这个,然后采取适当的动作:
-
super.onOptionsItemSelected(item) 方法在这个方法最后调用—我们得捕获我们的事件先!
- 在
item.getItemId()写一个 switch 语句.在 INSERT_ID分支,调用一个新方法,createNote(), 然后返回 true, 因为我们已经捕获这个事件,也不需要在返回给系统了。
- 在最后返回父类的
onOptionsItemSelected() 方法调用结果
整个 onOptionsItemSelect() 方法大致是这样滴
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case INSERT_ID:
createNote();
return true;
}
return super.onOptionsItemSelected(item);
}
Step 11
Add a new createNote() method:
In this first version of our application, createNote() is not going to be very useful. We will simply create a new note with a title assigned to it based on a counter (“Note 1″, “Note 2″…) and with an empty body. At present we have no way of editing the contents of a note, so for now we will have to be content making one with some default values:
- Construct the name using “Note” and the counter we defined in the class:
String noteName = "Note " + mNoteNumber++
- Call
mDbHelper.createNote() using noteName as the title and "" for the body
- Call
fillData() to populate the list of notes (inefficient but simple) — we’ll create this method next.
The whole createNote() method should look like this:
private void createNote() {
String noteName = "Note " + mNoteNumber++;
mDbHelper.createNote(noteName, "");
fillData();
}
Step 11
添加一个新的createNote()方法:
在我们的应用程序第一个版本, createNote() 不会很有用啦。我们只会简单的创建一个新的记录,记录有个基于计数器的标题("Note 1", "Note 2"...),和空的body
目前我们还没有办法编辑记录的内容, 到目前为止我们 will have to be content making one with some default values(不知道啥):
- 用"note"和我们定义在类里的计数器构造名字:
String noteName = "Note " + mNoteNumber++
用noteName和空的body参数调用mDbHelper.createNote()方法
调用fillData()方法来弹出记录列表 (效率低但很简单啦) — 我们等下会建立这个方法。
整个 createNote() 方法大概是这样滴:
private void createNote() {
String noteName = "Note " + mNoteNumber++;
mDbHelper.createNote(noteName, "");
fillData();
}
Step 12
List adapters
Our example uses aSimpleCursorAdapter to bind a database Cursor into a ListView, and this is a common way to use aListAdapter. Other options exist likeArrayAdapter which can be used to take a List or Array of in-memory data and bind it in to a list as well.
Define the fillData() method:
This method uses SimpleCursorAdapter, which takes a database Cursor and binds it to fields provided in the layout. These fields define the row elements of our list (in this case we use the text1 field in our notes_row.xmllayout), so this allows us to easily populate the list with entries from our database.
To do this we have to provide a mapping from the title field in the returned Cursor, to our text1 TextView, which is done by defining two arrays: the first a string array with the list of columns to map from (just “title” in this case, from the constant NotesDbAdapter.KEY_TITLE) and, the second, an int array containing references to the views that we’ll bind the data into (the R.id.text1 TextView).
This is a bigger chunk of code, so let’s first take a look at it:
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);
}
Here’s what we’ve done:
- After obtaining the Cursor from
mDbHelper.fetchAllNotes(), we use an Activity method called startManagingCursor() that allows Android to take care of the Cursor lifecycle instead of us needing to worry about it. (We will cover the implications of the lifecycle in exercise 3, but for now just know that this allows Android to do some of our resource management work for us.)
- Then we create a string array in which we declare the column(s) we want (just the title, in this case), and an int array that defines the View(s) to which we’d like to bind the columns (these should be in order, respective to the string array, but here we only have one for each).
- Next is the SimpleCursorAdapter instantiation. Like many classes in Android, the SimpleCursorAdapter needs a Context in order to do its work, so we pass in
this for the context (since subclasses of Activity implement Context). We pass the notes_row View we created as the receptacle for the data, the Cursor we just created, and then our arrays.
In the future, remember that the mapping between the from columns and to resources is done using the respective ordering of the two arrays. If we had more columns we wanted to bind, and more Views to bind them in to, we would specify them in order, for example we might use { NotesDbAdapter.KEY_TITLE, NotesDbAdapter.KEY_BODY } and { R.id.text1, R.id.text2 } to bind two fields into the row (and we would also need to define text2 in the notes_row.xml, for the body text). This is how you can bind multiple fields into a single row (and get a custom row layout as well).
If you get compiler errors about classes not being found, ctrl-shift-O or (cmd-shift-O on the mac) to organize imports.
Step 12
List adapters
在我们的例子里我们用一个SimpleCursorAdapter 来绑定数据库指针到一个listview里,这是一个使用listAdapter方法很常用的方式 . 也可以用别的方式啦,比如ArrayAdapter ,也是常用来获得一个内存里的list或数组的数据并绑定他到一个list里.
定义 fillData() 方法:
这个方法使用 SimpleCursorAdapter,传入一个数据库指针参数,然后绑定他到layout里提供的字段里. 这些字段定义行元素到我们的list里(在这个例子里我们使用在 notes_row.xml 布局里的text1字段), 所以这允许我们很容易的获得数据库里的含有实体的list。
为了做这个我们需要提供一个在返回的指针里title字段的映射到我们的 text1 TextView, 定义2个数组来完成他: 第一个是一个列映射list的字符串数组(在这里只有”title”, 来自常量NotesDbAdapter.KEY_TITLE) ,第二个,是一个整数数组包含views的引用,我们将会绑定数据到这个引用(R.id.text1 TextView).
这里有比较多的代码,所以我们一起来分析下:
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);
}
在上面的代码里,我们做了这些事啦:
- 在我们获得来自
mDbHelper.fetchAllNotes()的指针后,我们调用一个activity方法startManagingCursor()来使得Android 处理这个指针的生命周期这样我们就不要担心他咯~。 (我们会在练习3里谈到这个生命周期的具体实现, 我们现在只要知道这会使得Android为我们做一些资源管理的事就够了)
- 然后我们创建一个我们声明需要的列(在这里,只有title啦)字符串数组, 和一个定义了我们想要绑定到列上的view整数数组(这将会按照各自的字符串数组按顺序排列,不过我们现在只有一个啦).
- 下一步就是 SimpleCursorAdapter 实例.和大多数Android里的类类似,SimpleCursorAdapter 需要一个context来让他运行, 所以我们传入了
this (因为activity的子类实现了 Context).我们传入 notes_row 视图,指针,和我们的数组。
将来,一定要记住各自从列到资源的映射的顺序。如果我们有更多的列要绑定,也同时要有更多的view绑定到to里,我们按顺序指定他们,比如我们可能会用{ NotesDbAdapter.KEY_TITLE, NotesDbAdapter.KEY_BODY } 和{ R.id.text1, R.id.text2 } 来绑定2个字段到行里 (当然我们也得为body文本在 notes_row.xml里定义text2 ).这就是你如何绑定多字段数据到一个行里(同时也使用一个定制的layout)方法。
如果你有类未找到的编译错误,使用 ctrl-shift-O or (cmd-shift-O on the mac) 来组织导入。
Step 13
Run it!
- Right click on the
Notepadv1 project.
- From the popup menu, select Run As > Android Application.
- If you see a dialog come up, select Android Launcher as the way of running the application (you can also use the link near the top of the dialog to set this as your default for the workspace; this is recommended as it will stop the plugin from asking you this every time).
- Add new notes by hitting the menu button and selecting Add Item from the menu.
Solution and Next Steps
You can see the solution to this class in Notepadv1Solution from the zip file to compare with your own.
Once you are ready, move on to Tutorial Exercise 2 to add the ability to create, edit and delete notes.
Step 13
运行它咯!
- 右击
Notepadv1 项目。
- 选择 Run As > Android Application.
- 如果你看到一个对话框出现,选择 Android Launcher作为运行这个应用程序的方法(你也可以使用在对话框最顶端的链接来设置这个作为你的默认工作空间,这样插件就不会每次都这样问你了).
- 通过点击菜单按钮并选择添加选项来添加新的记录。
Solution and Next Steps
你可以在Notepadv1Solution 看到这个类的答案
当你都搞定了,你就可以到下一个练习了,来添加创建编辑和删除记录的功能了。