在这个练习中,你学会如何构建一个简单的文本记录列表,一个可以让用户添加但不能编辑的列表。这个练习将演示:


1,基于ListActivities的创建和控制菜单选项。
2,如何访问和存储在一个SQLite数据库中的文本记录。
3,如何使用ArrayAdapter绑定数据到ListView中。(一种最简单的方式)
4,基础的屏幕布局,包含如何布局一个列表视图,如何添加Item到activity菜单,和如何交互控制这些菜单选择。





步骤一



打开Eclipse中的 Notepadv1 项目


Notepadv1项目提供了一个出发点,它使用的样式在之前的Hello Android教程中已经见过了。


a, 在 package Explorer上点鼠标右键, 选择import.../General/Existing Projects into


Workspace.
b, 点击"browse"按钮,浏览您拷贝的三个 exercise 文件夹,选择 Notepadv1 ,然后点OK
c, 然后你会看到Notepadv1在项目列表中,下面带有个选择按钮next
d, 点击 "Finisd";
e, 这个练习项目将会被打开在你的Ecplise中的包浏览模式(package explorer)
f, 如果你看到一个关于AndroidMainfest.xml的错误,或者其他与Android.zip文件相关的错误,请右键点击项目,打开 Android Tools-> Fix Project Properties 从弹出菜单中,(然后项目将会显示错误的位置,然后你可以解决它)



步骤二



看一下 DBHelper类, 这是一个提供封装了对SQLite的数据访问的类,并且将控制我们的记录并且允许我们更新记录。


典型的实现,你可以实现一个ContentProvider,实际上,完整版的Notepad程序包含了SDK的像ContentProvider的实现,无论如何,没有理由让你不能像我们一样直接使用你的SQListe数据库。关键事情是注意到这类个对于我们在SQLite中存储,提取和更新数据的细节有更好的优化。这里有很多方法为了像提取所有的记录行,提取记录行基于记录行ID,建立新记录行, 删除已有记录行和更新记录行这些操作。如果你像快速的理解如何使用SQLite数据库在你的程序中,并且也想对其进行更好的使用优化,你可以看包含在SDK samples/ 文件夹下的Notepad 应用程序实例,它作为一个实现了ContentProvide的例子。


步骤三


打开 notepad_list.xml文件 在 res/layout 文件夹中。


这是一个布局定义文件,带有一个默认的出发点,我们可以把它作为一个快捷的实现。


a, 所有的Android布局文件必须使用 <?xml version="1.0" encoding="utf-8"?> 作为 XML文件头的


开始。


b, 并且,下面的定义也是经常有的(但不是总有)一些类别的布局定义, 在这里是LinearLayout.


c, 也要注意Androi的xml命名空间总是被定义在顶部组件或在XML中的布局,所以android:标签能够通过其余的文件被使用:
xmlns:android="http://schemas.android.com/apk/res/android"


步骤四
我们需要创建这个布局来控制我们的表格,添加代码在LinerLayout标签中,那么这个文件将会看起来像下面这样(你可以点击Source标签去编辑XML文件)



<?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 id="@id/android:list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView id="@id/android:empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/no_notes"/>


</LinearLayout>



a, 这个 ListView和TextView可以想象成两个则二选一的界面,同一时间只能显示一个。ListView在在显示记事记录时被使用,在TextView中。(TextView有一个默认的字符串"No Notes Yet!",将会在没有任何记录时被显示)。



b, 在ListView和TextView中的id字符串中的@表示XML解析器应该解析和展开剩下的id字符串,并且使用一个ID资源



c, 并且,这个 android:list 和 android:empty是在Android平台已经提供的ID。
当在list适配中没有数据时会自动为空,这个List适配会去寻找这些特定的名字作为默认, 你可以选择使用setEmptyView()改变默认的空view做List适配.
更广泛些说,这个android.R类是一组平台为你预先定义好的资源。 当你项目的R类已经作为一组资源被定义好时,资源管理器使用在XML文件中的android: 名称空间前缀找到在 android.R(就像我们在这里看到的)。



步骤五:



要创建一个列表视图,我们需要定义一个view,为了列表的每一行。


a, 创建一个新文件在 res/layout中,取名为notes_row.xml。


b, 添加下面的内容(注意:xml头被再一次用到,并且第一个节点定义了Android xml的命名空间)


<?xml version="1.0" encoding="utf-8"?>
<TextView id="@+id/text1"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>


c, 这是一个被用到每一个记录标题行的view, 它只有一个文本域。


d, 在这里我们创建了一个新的id 叫做 text2, 在 id 字符串中的 @后的 + 表示如果这个id不存在那么应该自动创建它。所以我们定义一个不工作的text1,以后再使用它。


e, 在保存文件后,打开项目中的R.java类, 你可以看见为了 notes_row 和 text1的新定义,意思时我们现在可以从代码中访问。



步骤六


下面, 打开Notepadv1类, 我们要把它修改成一个list的样式来显示我们的记录,并且允许我们添加记录。


Notepadv1将会继承自Activity类 作为一个实现特定功能的就是你想要去实现的list的ListActivity。例如,在屏幕中显示一个任意行数的列表, 通过移动选择框,可以选择每个item。


让我们看一下 Notpadv1中的代码, 这里有些常量定义在顶部, 在private域后,我们将创建一定数量的记录标题和一些覆盖父类的方法。



步骤七


改变 Notepadv1的继承关系, 让它继承自 ListActivity.


public class Notepadv1 extends ListActivity


注意: 你需要导入 ListActivity 类, 使用Eclipse中的快捷键 ctrl-shift-O(Windows or Linux) 或cmd-shift-O (在Mac)。


步骤八


这里已有三个重载的方法: onCreate, onCreateOptionsMenu 和 onOptionsItemSelected, 我们需要按照下面的要求去填充他们。


(1) onCreate() 当activity开始被调用 — 它有点像在activity中用的"main"方法. 我们在它里面设置一些程序的资源和状态。


(2) onCreateOptionsMenu() 它在activity被用来放置菜单. 它在用户点击菜单时被显示出来, 并且像一个列表一样被上下选择。


(3) onOptionsItemSelected() 是菜单元素的另一半, 它被使用在创建菜单的事件 (例如: 当用户选择了 "Create Note" 项目).


步骤九


添写 onCreate() 方法:
这里我们将要设置activity的标题(显示在屏幕的顶部), 使用notepad_list布局, 我们已经创建的为了activity显示内容。 设置DBHelper的实例,我们将用它访问记录数据, 然后放置在列表上使用可用的标题。


a, 调用 super() 传入 icicle 参数。
b, 使用setContentView,传入 R.layout.notepad_list
c, 声明一个新的私有类成员 名为 dbHelper, (在onCreate方法前)
d, 回到onCreate方法,初始化DBHelper的这个实例,(注意,你需要将this传入作为参数)
e, 最后,调用新方法,fillData() 通过dbHelper得到一些数据, 我们已经定义好的。
f, onCreate() 应该看起来像下面这样:


@Override
public void onCreate(Bundle icicle)
{
super.onCreate(icicle);
setContentView(R.layout.notepad_list);
dbHelper = new DBHelper(this);
fillData();
}


并且记得要添加 DBHelper的声明作为成员变量。(放在noteNumber定义下面)


步骤十


填写 onCreateOptionsMenu() 方法体:


我们准备仅添加一个"Add item"选项,使用一个字符串,我们将在strings.xml创建, 并且定义一个常量在类顶部去标识添加项目的操作。


a, 在 strings.xml资源类中(在 res/values下面), 添加一个新的字符串名为 menu_insert 并且赋值为 "Add Item"。
<string name="menu_insert">Add Item</string>, 然后保存



b, 你需要定义菜单的坐标在 Notepadv1类顶部 (放在 KEY_BODY 定义下面)
public static final int INSERT_ID = Menu.FIRST;


c, 在onCreateOptionsMenu() 方法, 添加菜单项目, 需要注意一下要给父类返回boolean, 下面是整个方法:


@Override
public boolean onCreateOptionsMenu(Menu menu) {
boolean result = super.onCreateOptionsMenu(menu);
menu.add(0, INSERT_ID, R.string.menu_insert);
return result;
}



步骤十一



填写 onOptionsItemSelected() 方法体:


它将控制我们新的"Add Note"选项, 当它被选择时, onOptionsItemSelected()方法将会调用item.getId() 去 匹配 INSER_ID(我们所使用的用来标识菜单项目的常量), 当匹配上时,再进行一些适当的操作。


a, super.onOptionsItemSelected(item) 放在方法最后, 我们首先要捕获事件。


b, switch case 在 item.getId()


c, case 到 INSERT_ID:


d, 调用新方法 createNote()


e, break到 swtich尾部


f, 返回给父类结果 使用 onOptionsItemSelected() 在最后


g, 整个代码像下面这样


@Override
public boolean onOptionsItemSelected(Item item) {
switch (item.getId()) {
case INSERT_ID:
createNote();
break;
}

return super.onOptionsItemSelected(item);
}



步骤十二


添加新方法 createNote()


在我们程序的第一个版本, createNote() 不是很有用, 我们将简单的创建一个新记录带一个标题,后面带标号("Note 1", "Note 2"...), 并且带有一个空的记录体, 目前, 我们还没有途径去编辑记录的内容,所以我们要用一些默认的内容来填充。


a, String noteName = "Note " + noteNumber++; (构建名称 + 增量数字表示)


b, 调用 dbHelper.createRow() 方法 使用 noteName来创建


c, 调用 fillData() 方法在添加之后 (简单高效)


d, 方法代码像下面这样:


private void createNote() {
String noteName = "Note " + noteNumber++;
dbHelper.createRow(noteName, "");
fillData();
}



步骤十三


定义 fillData() 方法,这部分有点长:


这个方法使用 ArrayAdapter, 一个最简单的途径将数据放入一个ListView, ArrayAdapter可以是list字符串,或者是数组字符串, 然后绑定他们到文本视图的列表行的布局中(这里是text1 在我们的 notes_row.xml布局文件), 这个方法简单的使用dbHelper得到了记录列表。构造一个字符串的List使用每一个行的字符串标题,然后创建一个ArrayAdapter放入定义好的 notes_row


private void fillData() {
// We need a list of strings for the list items
List<String> items = new ArrayList<String>();


// Get all of the rows from the database and create the item list
List<Row> rows = dbHelper.fetchAllRows();
for (Row row : rows) {
items.add(row.title);
}

// Now create an array adapter and set it to display using our row
ArrayAdapter<String> notes =
new ArrayAdapter<String>(this, R.layout.notes_row, items);
setListAdapter(notes);

}



a, ArrayAdapter 需要字符串的List(List<String>)包含所有项目,然后显示出来


b, 数据作为记录行从数据库中读出来,并且标题域被作为list字符串被放置。


c, 我们指定 notes_row 视图,我们已创建的来放置数据的。


d, 如果你的编译器报告找不到类, 请按 ctrl-shift-O 或 (cmd-shift-O 苹果系统) 去导入需要的类。



步骤十四


运行程序!


a, 右键点击 Notepadv1 项目


b, 在弹出的菜单中,选择 Run As -> Android Application


c, 如果你看到一个对话框, 选择 Android加载作为程序运行(你也可以使用靠近对话框顶部的连接,去设置这次运行作为你的工作空间的默认)


d, 添加新记录, 使用菜单中的 Add Item 选项。