保存To-Do List

   

在本章的前面,你增强了To-Do List例子的功能,能够在会话间保存Activity UI的状态。那只是一半的工作;在接下来的例子里,你将创建一个私有的数据库来保存那些to-do项:

 

1. 创建一个新的ToDoDBAdapter类。它将用于管理你的数据库交互。

 

创建私有的变量来储存SQLiteDatabase对象和调用程序的上下文。添加构造函数来得到所属程序的上下文,并且为数据库的名和版本以及to-do项目的表添加静态的数据成员。

 

package com.paad.todolist;

 

import android.content.ContentValues;

import android.content.Context;

import android.database.Cursor;

import android.database.SQLException;

import android.database.sqlite.SQLiteException;

import android.database.sqlite.SQLiteDatabase;

import android.database.sqlite.SQLiteOpenHelper;

import android.util.Log;

 

public class ToDoDBAdapter

{

private static final String DATABASE_NAME = “todoList.db”;

private static final String DATABASE_TABLE = “todoItems”;

private static final int DATABASE_VERSION = 1;

private SQLiteDatabase db;

private final Context context;

 

public ToDoDBAdapter(Context _context)

{

this.context = _context;

}

}

 

2. 创建公共的变量来定义列名和列索引;这使得在通过Cursor提取值时更加容易地找到正确的列。

 

public static final String KEY_ID = “_id”;

public static final String KEY_TASK = “task”;

public static final int TASK_COLUMN = 1;

public static final String KEY_CREATION_DATE = “creation_date”;

public static final int CREATION_DATE_COLUMN = 2;

 

3. 在toDoDBAdapter中创建SQLiteopenHelper类的扩展——toDoDBOpenHelper。它将用于简化数据库的版本管理。在其中,重写onCreateonUpgrade方法来处理数据库的创建和更新逻辑。

 

private static class toDoDBOpenHelper extends SQLiteOpenHelper

{

public toDoDBOpenHelper(Context context, String name, CursorFactory factory, int version)

{

super(context, name, factory, version);

}

 

// SQL Statement to create a new database.

private static final String DATABASE_CREATE = “create table “ +

DATABASE_TABLE + “ (“ + KEY_ID +

“ integer primary key autoincrement, “ +

KEY_TASK + “ text not null, “ + KEY_CREATION_DATE + “ long);”;

 

@Override

public void onCreate(SQLiteDatabase _db)

{

_db.execSQL(DATABASE_CREATE);

}

 

@Override

public void onUpgrade(SQLiteDatabase _db, int _oldVersion, int _newVersion)

{

Log.w(“TaskDBAdapter”, “Upgrading from version “ +

_oldVersion + “ to “ + _newVersion +

“, which will destroy all old data”);

 

// Drop the old table.

_db.execSQL(“DROP TABLE IF EXISTS “ + DATABASE_TABLE);

 

// Create a new one.

onCreate(_db);

}

}

 

4. 在toDoDBAdapter类中,添加一个私有变量来储存toDoDBOpenHelper类的实例,并在构造函数中创建它。

 

private toDoDBOpenHelper dbHelper;

public ToDoDBAdapter(Context _context)

{

this.context = _context;

dbHelper = new toDoDBOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION);

}

 

5. 还在toDoDBAdapter类中,创建openclose方法。在close方法中简单的调用数据库对象的close方法。

 

public void close() {

db.close();

}

 

6. open方法需要使用toDoDBOpenHelper类。调用getWriteableDatabase来让辅助类处理数据库的创建和版本检查。当可写的数据库不能被打开时,尝试提供一个可读数据库。

 

public void open() throws SQLiteException {

try

{

db = dbHelper.getWritableDatabase();

}

catch (SQLiteException ex)

{

db = dbHelper.getReadableDatabase();

}

}

 

7. 为添加、删除和更新项目添加强类型方法。

 

// Insert a new task

public long insertTask(ToDoItem _task)

{

// Create a new row of values to insert.

ContentValues newTaskValues = new ContentValues();

 

// Assign values for each row.

newTaskValues.put(KEY_TASK, _task.getTask());

newTaskValues.put(KEY_CREATION_DATE, _task.getCreated().getTime());

 

// Insert the row.

return db.insert(DATABASE_TABLE, null, newTaskValues);

}

 

// Remove a task based on its index

public boolean removeTask(long _rowIndex)

{

return db.delete(DATABASE_TABLE, KEY_ID + “=” + _rowIndex, null) > 0;

}

 

// Update a task

public boolean updateTask(long _rowIndex, String _task)

{

ContentValues newValue = new ContentValues();

newValue.put(KEY_TASK, _task);

return db.update(DATABASE_TABLE, newValue, KEY_ID + “=” + _rowIndex, null) > 0;

}

 

8. 现在,添加辅助方法来处理查询。写三个方法——第一个返回所有的项目,第二个返回指定行的Cursor,最后一个返回强类型的ToDoItem对象。

 

public Cursor getAllToDoItemsCursor()

{

return db.query(DATABASE_TABLE, new String[] { KEY_ID, KEY_TASK, KEY_CREATION_DATE},

null, null, null, null, null);

}

 

public Cursor setCursorToToDoItem(long _rowIndex) throws SQLException

{

Cursor result = db.query(true, DATABASE_TABLE, new String[] {KEY_ID, KEY_TASK},

KEY_ID + “=” + _rowIndex, null, null, null, null, null);

if ((result.getCount() == 0) || !result.moveToFirst())

{

throw new SQLException(“No to do items found for row: “ + _rowIndex);

}

return result;

}

 

public ToDoItem getToDoItem(long _rowIndex) throws SQLException

{

Cursor cursor = db.query(true, DATABASE_TABLE, new String[] {KEY_ID, KEY_TASK},

KEY_ID + “=” + _rowIndex, null, null, null, null, null);

if ((cursor.getCount() == 0) || !cursor.moveToFirst())

{

throw new SQLException(“No to do item found for row: “ + _rowIndex);

}

String task = cursor.getString(TASK_COLUMN);

long created = cursor.getLong(CREATION_DATE_COLUMN);

ToDoItem result = new ToDoItem(task, new Date(created));

return result;

}

 

9. 以上完成了数据库的辅助类。回到ToDoList Activity中,并且更新to-do list数组的内容。

 

onCreate方法中创建一个toDoDBAdapter的实例,并且打开一个数据库。同时,调用populateToDoList方法。

 

ToDoDBAdapter toDoDBAdapter;

public void onCreate(Bundle icicle)

{

[ ... existing onCreate logic ... ]

toDoDBAdapter = new ToDoDBAdapter(this);

// Open or create the database

toDoDBAdapter.open();

populateTodoList();

}

 

private void populateTodoList() { }

 

10. 创建一个变量来储存指向数据库中所有的to-do项目的Cursor。更新populateTodoList方法,使用toDoDBAdapter实例来查询数据库,并调用startManagingCursor来让Activity管理Cursor。它还应该调用updateArray,该方法使用Cursor重新填入to-do list数组。

 

Cursor toDoListCursor;

private void populateTodoList()

{

// Get all the todo list items from the database.

toDoListCursor = toDoDBAdapter.getAllToDoItemsCursor();

startManagingCursor(toDoListCursor);

// Update the array.

updateArray();

}

private void updateArray() { }

 

11. 现在,实现updateArray方法来更新当前的to-dolist数组。调用requery来保证数据库更新了,然后清除数组并枚举整个结果集。当完成时,调用notifyDataSetChanged方法。

 

private void updateArray()

{

toDoListCursor.requery();

todoItems.clear();

if (toDoListCursor.moveToFirst())

do

{

String task = toDoListCursor.getString(ToDoDBAdapter.TASK_COLUMN);

long created = toDoListCursor.getLong(ToDoDBAdapter.CREATION_DATE_COLUMN);

ToDoItem newItem = new ToDoItem(task, new Date(created));

todoItems.add(0, newItem);

} while(toDoListCursor.moveToNext());

aa.notifyDataSetChanged();

}

 

12. 为了将各块粘合起来,修改OnKeyListener方法和更新removeItem方法。都使用toDoDBAdapter来在数据库中增加和删除项目,而不再直接修改to-do list数组。

 

12.1. 在OnKeyListener中,往数据库中插入新的项目,并更新数组。

 

public void onCreate(Bundle icicle)

{

super.onCreate(icicle);

setContentView(R.layout.main);

myListView = (ListView)findViewById(R.id.myListView);

myEditText = (EditText)findViewById(R.id.myEditText);

todoItems = new ArrayList<ToDoItem>();

int resID = R.layout.todolist_item;

aa = new ToDoItemAdapter(this, resID, todoItems);

myListView.setAdapter(aa);

myEditText.setOnKeyListener(new OnKeyListener() {

public boolean onKey(View v, int keyCode, KeyEvent event)

{

if (event.getAction() == KeyEvent.ACTION_DOWN)

if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER)

{

ToDoItem newItem;

newItem = new ToDoItem(myEditText.getText().toString());

toDoDBAdapter.insertTask(newItem);

updateArray();

myEditText.setText(“”);

aa.notifyDataSetChanged();

cancelAdd();

return true;

}

return false;

}

});

registerForContextMenu(myListView);

restoreUIState();

toDoDBAdapter = new ToDoDBAdapter(this);

// Open or create the database

toDoDBAdapter.open();

populateTodoList();

}

 

12.2. 然后修改removeItem来从数据库中删除项目,并更新数组列表。

 

private void removeItem(int _index) {

// Items are added to the listview in reverse order,

// so invert the index.

toDoDBAdapter.removeTask(todoItems.size()-_index);

updateArray();

}

 

13. 最后一步,重写onDestroy方法,关闭数据库的连接。

 

@Override

public void onDestroy() {

// Close the database

toDoDBAdapter.close();

super.onDestroy();

}

 

现在,你的to-do项目将在会话间保存了。作为下一步的提高,你可以改变ArrayAdapterCursorAdapter,并让ListView直接依据底层数据的变化动态更新。

 

通过使用私有的数据库,你的任务现在不能被别的应用程序看见或是添加。让了能够让其它应用程序来访问你的任务,你可以使用Content Provider来暴露它们。

你可能感兴趣的:(list)