|
1. Android Intents
1.1. Android中的SQLite
SQLite是一个嵌入在Android中的开源数据库。SQLite支持标准的关系型数据库特征,比如SQL语法,事务和预处理语句。另外它在运行时只需要很少的内存(大约250KByte)
在Android中使用SQLite不需要任何数据库的安装或管理(不需要数据库管理员)。你可以指定SQL来使用数据库工作,并且数据库会为你自动管理。
在Android中使用数据库工作可以减缓由于必要的I/O操作,因此它建议在AsyncTask中执行这项任务。
数据库支持的数据类型:
TEXT 类似JAVA中的String
INTEGER 类似JAVA中的long
REAL 类似JAVA中的double
在将这些数据保存到数据库之前,所有其他的类型都必须转换成这些类型。实际上SQLite本身不验证写入列中的类型定义,你可以写一个整数到字符串列上。
如果您的应用程序创建一个数据库,这个数据库时保存在目录
”data/data/<项目包名>/databases/数据库名”。这里的第一个data目录的路径就是
Environment.getDataDirectory()的返回值。Environment.getDataDirectory()通常返回SD卡中的位置
一个SQLite数据库相对于创建它的应用程序是私有的,如果你想将数据分享给其他应用程序,可以使用ContentProvider
1.2. SQLiteOpenHelper
在你的应用程序中通过使用子类SQLiteOpenHelper来创建和更新一个数据库,在这个类中,在数据库模式变化的情况下,你需要重写onCreate()方法来创建数据库和重写onUpdate()方法来更新数据库。这两种方法都需要接收一个SQLiteOpenHelper对象。
SQLiteOpenHelper提供方法getReadableDatabase()和getWriteableDatabase()方法来获得一个SQLiteDatabase对象,在对象可以允许访问数据库,而不管是在读或写的模式下。
对于数据库的主键,你应该一直使用标识符”_id”来作为数据库主键,在Android的一些方法中最好依赖这个标准。
SQLiteDatabase提供方法insert()、update()、delete()和可以直接执行SQL的
execSQL()方法,对象ContentValues允许为insert和update定义键值,键就是列,值就是该列对应的值。
1.3. SQLiteDatabase和Cursor(游标)
我们可以通过创建rawQuery()方法通过接收SQL查询数据,或者通过query()方法,该方法提供了一个指定动态数据或SQLiteQueryBuilder的接口。SQLiteBuilder类似内容提供者的接口,因此通常用于ContentProvider中。查询通常返回的是一个游标(Cursor)
查询方法有如下一些参数:
query(String
dbName,int[] columnNames,String whereClause, String[] valuesForWhereClause,
String[] groupBy, String[] having, String[] orderBy)。
如果所有的参数都要选择的话,可以将那些不需要的字句设置为null,比如where的地方设置为null,当然还可以在where的地方填入其他值(如果需要的话),比如
“_id=18 and summary=?”。如果需要几个值呢?你可以通过在valuesForWhereClause数据来查询。一般来说,如果不需要的话,可以填入null,比如groupby字句。
一个游标就代表查询结果。要过的元素的数量,可以使用getCount()方法。要一在单个数据行与行之间移动数据,可以通过moveToFirst()和MoveToNext()方法。通过isAfterLast()方法,可以检查下面是否还有数据。
在ListView中可以通过SimpleCursorAdapter适配器来直接使用游标。
2. 开始编写应用程序
2.1. 概览
下面,我们通过一个示例程序来测试,这个程序可以允许用户保持它应该做的事项。这些事项被存放在SQLite数据库中,这个应用程序包括两个Activity,一个是用来查看所有待办事项清单,一个是用户创建/维护一个特定的待办事项。这两个Activity都是通过Intent来进行沟通的。
2.2. 工程
创建功能,名字为SQLiteTest,这里有两个Activity,一个视图用来显示待办事件列表,另一个用来编辑待办事件
2.3.数据库处理
数据库帮手:MySQLiteOpenHelper.java
package com.loulijun.sqlitetest;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
public class MySQLiteOpenHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "hualang";
private static final int DATABASE_VERSION = 1;
//数据库创建语句
private static final String DATABASE_CREATE =
"create table test(_id integer primary key autoincrement, "+
"category text not null, summary text not null, description text not null);";
public MySQLiteOpenHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
//创建数据库时调用
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(DATABASE_CREATE);
}
//更新数据库时调用,比如更新数据库版本
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w(MySQLiteOpenHelper.class.getName(), "upgrading database from version "+
oldVersion + " to " + newVersion + ", which will destroy all old data");
db.execSQL("DROP TABLE IF EXISTS test");
onCreate(db);
}
}
在上面这个助手的基础上,我们可以写类"MyAdapter"。用之来提供查询,创建,更新等功能。通过助手类的open()方法打开数据库。在更新和创建值的时候,可以使用"android.content.ContentValues"类,这个类可以存储键值对,你可以使用列名作为ContentValues的键。
package com.loulijun.sqlitetest;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
public class MyAdapter {
public static final String KEY_ROWID = "_id";
public static final String KEY_CATEGORY = "category";
public static final String KEY_SUMMARY = "summary";
public static final String KEY_DESCRIPTION = "description";
public static final String DATABASE_TABLE = "test";
private Context context;
private SQLiteDatabase db;
private MySQLiteOpenHelper dbHelper;
public MyAdapter(Context context)
{
this.context = context;
}
public MyAdapter open() throws SQLException
{
dbHelper = new MySQLiteOpenHelper(context);
db = dbHelper.getWritableDatabase();
return this;
}
public void close()
{
dbHelper.close();
}
/**
* 创建一个新的标test,如果test成功创建则返回一个新的列id,否则返回-1来表示失败
*/
public long createTest(String category, String summary, String description)
{
ContentValues initialValues = createContentValues(category, summary, description);
return db.insert(DATABASE_TABLE, null, initialValues);
}
//更新表test
public boolean updateTest(long rowId, String category, String summary, String description)
{
ContentValues updateValues = createContentValues(category, summary, description);
return db.update(DATABASE_TABLE, updateValues, KEY_ROWID + "=" +rowId, null)>0;
}
//删除表test
public boolean deleteTest(long rowId)
{
return db.delete(DATABASE_TABLE, KEY_ROWID + "=" + rowId, null)>0;
}
//返回数据库中表test的所有游标列表
public Cursor fetchALlTests()
{
return db.query(DATABASE_TABLE, new String[]{KEY_ROWID,
KEY_CATEGORY,KEY_SUMMARY,KEY_DESCRIPTION}, null, null, null, null, null);
}
//返回在表test中被定义的某个位置的游标
public Cursor fetchTest(long rowId)throws SQLException
{
Cursor mCursor = db.query(true, DATABASE_TABLE, new String[]{KEY_ROWID,
KEY_CATEGORY, KEY_SUMMARY, KEY_DESCRIPTION},
KEY_ROWID + "=" +rowId, null, null, null,null,null);
if(mCursor != null)
{
mCursor.moveToFirst();
}
return mCursor;
}
private ContentValues createContentValues(String category, String summary,
String description) {
ContentValues values = new ContentValues();
values.put(KEY_CATEGORY, category);
values.put(KEY_SUMMARY, summary);
values.put(KEY_DESCRIPTION, description);
return values;
}
}
<!--[if !supportLists]-->2.4. <!--[endif]-->资源
在res/menu/目录下创建listment.xml
<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/insert"
android:title="Insert"
/>
</menu>
在res/values目录下,编写res/values/priority.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="priorities">
<item>紧急</item>
<item>提醒</item>
</string-array>
</resources>
strings.xml如下:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="hello">Hello World, SQLiteTestActivity!</string>
<string name="app_name">SQLiteTest</string>
<string name="no_tests">目前没有保存的待办事项</string>
<string name="menu_insert">增加事项</string>
<string name="menu_delete">删除待办</string>
<string name="test_summary">概要</string>
<string name="test_description">描述</string>
<string name="test_edit_summary">编辑概要</string>
<string name="test_edit_description">编辑描述</string>
<string name="test_edit_confirm">确认</string>
<color name="lisicolor">#FFE87C</color>
<color name="black">#000000</color>
</resources>
2.5. 视图
我定义了三个布局文件,一个是为列表,一个是为列表的行,一个是为记事本的主体
这是第一个布局,用户设置列表
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:baselineAligned="@color/lisicolor"
>
<ListView
android:id="@android:id/list"
android:layout_width="fill_parent"
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_tests"
/>
</LinearLayout>
这是第二个布局,用于设置列表的每一行的布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content" android:layout_width="fill_parent"
android:background="@color/lisicolor">
<ImageView android:id="@+id/icon" android:src="@drawable/hualang"
android:layout_marginLeft="4px" android:layout_marginRight="8px"
android:layout_height="40px" android:layout_marginTop="8px"
android:layout_width="30px">
</ImageView>
<TextView
android:text="@+id/TextView01"
android:layout_height="wrap_content"
android:id="@+id/label"
android:textSize="40px"
android:layout_marginTop="6px"
android:layout_width="fill_parent"
android:textColor="@color/black">
</TextView>
</LinearLayout>
下面是备忘录的编辑布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="fill_parent"
android:layout_height="fill_parent" android:background="@color/lisicolor">
<Spinner android:id="@+id/category" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:entries="@array/priorities"></Spinner>
<LinearLayout android:id="@+id/LinearLayout01"
android:layout_height="wrap_content" android:layout_width="fill_parent">
<EditText android:layout_height="wrap_content" android:id="@+id/todo_edit_summary"
android:layout_weight="1" android:layout_width="wrap_content"
android:hint="Summary"></EditText>
</LinearLayout>
<EditText android:layout_width="fill_parent"
android:layout_height="fill_parent" android:layout_weight="1"
android:id="@+id/todo_edit_description" android:hint="Description" android:gravity="top"></EditText>
<Button android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/todo_edit_button"
android:text="@string/test_edit_confirm"></Button>
</LinearLayout>
下面的是两个Activity的代码
SQLiteOverView.java
package com.loulijun.sqlitetest;
import android.app.ListActivity;
import android.content.Intent;
import android.database.Cursor;
import android.os.Bundle;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
public class SQLiteOverView extends ListActivity {
private MyAdapter dbHelper;
private static final int ACTIVITY_CREATE = 0;
private static final int ACTIVITY_EDIT = 1;
private static final int DELETE_ID = Menu.FIRST + 1;
private Cursor cursor;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test_list);
this.getListView().setDividerHeight(2);
dbHelper = new MyAdapter(this);
dbHelper.open();
fillData();
registerForContextMenu(getListView());
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.listmenu, menu);
return true;
}
@Override
public boolean onMenuItemSelected(int featureId, MenuItem item) {
switch(item.getItemId())
{
case R.id.insert:
createTest();
return true;
}
return super.onMenuItemSelected(featureId, item);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId())
{
case R.id.insert:
createTest();
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public boolean onContextItemSelected(MenuItem item) {
switch(item.getItemId())
{
case DELETE_ID:
AdapterContextMenuInfo info = (AdapterContextMenuInfo)item.getMenuInfo();
dbHelper.deleteTest(info.id);
fillData();
return true;
}
return super.onContextItemSelected(item);
}
public void createTest()
{
Intent i = new Intent(this,TestDetails.class);
startActivityForResult(i, ACTIVITY_CREATE);
}
// ListView 和 view (row) on 被点击
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
Intent i = new Intent(this, TestDetails.class);
i.putExtra(MyAdapter.KEY_ROWID, id);
// Activity返回一个结果给startActivityForResult
startActivityForResult(i, ACTIVITY_EDIT);
}
/**
* 根据其他的Activity的结果被调用
* requestCode是发送到Activity的原始码
* resutCode是返回码,0表示一切OK
*/
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
fillData();
}
private void fillData() {
cursor = dbHelper.fetchALlTests();
startManagingCursor(cursor);
String[] from = new String[] { MyAdapter.KEY_SUMMARY };
int[] to = new int[] { R.id.label };
//现在创建一个数组适配器并且设置它现在在我们的列中
SimpleCursorAdapter notes = new SimpleCursorAdapter(this,
R.layout.test_row, cursor, from, to);
setListAdapter(notes);
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
menu.add(0, DELETE_ID, 0, R.string.menu_delete);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (dbHelper != null) {
dbHelper.close();
}
}
}
TestDetails.java
package com.loulijun.sqlitetest;
import android.app.Activity;
import android.database.Cursor;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Spinner;
public class TestDetails extends Activity {
private EditText mTitleText;
private EditText mBodyText;
private Long mRowId;
private MyAdapter mDbHelper;
private Spinner mCategory;
@Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
mDbHelper = new MyAdapter(this);
mDbHelper.open();
setContentView(R.layout.test_edit);
mCategory = (Spinner) findViewById(R.id.category);
mTitleText = (EditText) findViewById(R.id.todo_edit_summary);
mBodyText = (EditText) findViewById(R.id.todo_edit_description);
Button confirmButton = (Button) findViewById(R.id.todo_edit_button);
mRowId = null;
Bundle extras = getIntent().getExtras();
mRowId = (bundle == null) ? null : (Long) bundle
.getSerializable(MyAdapter.KEY_ROWID);
if (extras != null) {
mRowId = extras.getLong(MyAdapter.KEY_ROWID);
}
populateFields();
confirmButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
setResult(RESULT_OK);
finish();
}
});
}
private void populateFields() {
if (mRowId != null) {
Cursor todo = mDbHelper.fetchTest(mRowId);
startManagingCursor(todo);
String category = todo.getString(todo
.getColumnIndexOrThrow(MyAdapter.KEY_CATEGORY));
for (int i=0; i<mCategory.getCount();i++){
String s = (String) mCategory.getItemAtPosition(i);
Log.e(null, s +" " + category);
if (s.equalsIgnoreCase(category)){
mCategory.setSelection(i);
}
}
mTitleText.setText(todo.getString(todo
.getColumnIndexOrThrow(MyAdapter.KEY_SUMMARY)));
mBodyText.setText(todo.getString(todo
.getColumnIndexOrThrow(MyAdapter.KEY_DESCRIPTION)));
}
}
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
saveState();
outState.putSerializable(MyAdapter.KEY_ROWID, mRowId);
}
@Override
protected void onPause() {
super.onPause();
saveState();
}
@Override
protected void onResume() {
super.onResume();
populateFields();
}
private void saveState() {
String category = (String) mCategory.getSelectedItem();
String summary = mTitleText.getText().toString();
String description = mBodyText.getText().toString();
if (mRowId == null) {
long id = mDbHelper.createTest(category, summary, description);
if (id > 0) {
mRowId = id;
}
} else {
mDbHelper.updateTest(mRowId, category, summary, description);
}
}
}
还有最后一步,注册TestDetails.java
<activity android:name=".TestDetails"
android:windowSoftInputMode="stateVisible|adjustResize"/>
2.6. 运行应用程序
注意:项目是UTF-8编码的,在windows系统中用eclipse需要将其设置为UTF-8编码 |
|