实验目的
掌握SQLiteOpenHelper 、SQLiteDatabase 等类的常用方法;
掌握CursorAdapter 适配器类的使用方法;
实验要求
使用SQLiteOpenHelper 进行数据库创建及管理;
使用Cursor 、CursorAdapter**进行数据查询及展示;
实验内容
步骤一,打开Code07项目
使用Android Studio 打开Code07 项目。
本次项目的目的是将MainActivity 中ListView 的数据源转换为从SQLite数据库中读取。
步骤二,创建News的合约类——NewsContract
新建名为NewsContract 类,该类包含了News 表的所有原数据信息,代码如下所示。
将NewsContract 类的构造方法访问符设为private ,避免其他开发人员实例化该类。 在NesContract 类中定义了名为NewsEntry 的内部类,该类实现了BaseColumns 接口,主要作用是为NewsEntry 类新增一个名为_ID 的字段名。
Copy public final class NewsContract {
private NewsContract() {}
public static class NewsEntry implements BaseColumns {
public static final String TABLE_NAME = "tbl_news";
public static final String COLUMN_NAME_TITLE = "title";
public static final String COLUMN_NAME_AUTHOR = "author";
public static final String COLUMN_NAME_CONTENT = "content";
public static final String COLUMN_NAME_IMAGE = "image";
}
}
步骤三,自定义SQLiteOpenHelper子类
1. 自定义SQLiteOpenHelper子类
定义名为MySQLiteOpenHelper 类,该类继承于SQLiteOpenHelper ,主要用于对数据库的创建及版本管理。 在该类中首先定义两个字符串常量表示News 数据库表的创建和删除SQL 语句,如代码\ref{lst:code07_sql_statement}所示。
在MySQLiteOpenHelper 类中还定义了两个常量DATABASE_VERSION 和DATABASE_NAME 分别表示数据库的版本及数据库名。
Copy private static final String SQL_CREATE_ENTRIES =
"CREATE TABLE " + NewsContract.NewsEntry.TABLE_NAME + " (" +
NewsContract.NewsEntry._ID + " INTEGER PRIMARY KEY, " +
NewsContract.NewsEntry.COLUMN_NAME_TITLE + " VARCHAR(200), " +
NewsContract.NewsEntry.COLUMN_NAME_AUTHOR + " VARCHAR(100), " +
NewsContract.NewsEntry.COLUMN_NAME_CONTENT + " TEXT, " +
NewsContract.NewsEntry.COLUMN_NAME_IMAGE + " VARCHAR(100) " +
")" ;
private static final String SQL_DELETE_ENTRIES =
"DROP TABLE IF EXISTS " + NewsContract.NewsEntry.TABLE_NAME;
public static final int DATABASE_VERSION = 1;
public static final String DATABASE_NAME = "news.db";
private Context mContext;
2. 定义构造函数及重载onCreate()方法
SQLiteOpenHelper 基类构造函数接收四个参数:
SQLiteDatabase.CursorFactory factory ,创建Cursor 的工厂类,默认传递null 即可;
如果数据库结构发生了变化,则递增version 即可。
onCreate() 方法在数据库文件不存在时将被执行,通常只执行一次,因此在该方法中应该完成数据库表的创建以及数据初始化的操作,代码如下所示。
Copy public class MyDbOpenHelper extends SQLiteOpenHelper {
...
public MyDbOpenHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
mContext = context;
}
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
sqLiteDatabase.execSQL(SQL_CREATE_ENTRIES);
initDb(sqLiteDatabase);
}
3. 构造数据库初始数据
在实际开发中,数据库的初始数据可能通过网络进行获取或者通过第三方途径获取。 在本项目中为了简便起见,将原来从string.xml 构造的数据放入数据库中。 把初始化数据的功能代码放在initDb() 方法中。
initDb() 方法的参数是SQLiteDatabase 对象,在调用该方法时确保SQLiteDatabase 对象已经被初始化。 常见的做法是将initDb() 方法放入MySQLiteOpenHelper 类的onCreate() 方法中。
Copy private void initDb(SQLiteDatabase sqLiteDatabase) {
Resources resources = mContext.getResources();
String[] titles = resources.getStringArray(R.array.titles);
String[] authors = resources.getStringArray(R.array.authors);
String[] contents = resources.getStringArray(R.array.contents);
TypedArray images = resources.obtainTypedArray(R.array.images);
int length = 0;
length = Math.min(titles.length, authors.length);
length = Math.min(length, contents.length);
length = Math.min(length, images.length());
for (int i = 0; i < length; i++) {
ContentValues values = new ContentValues();
values.put(NewsContract.NewsEntry.COLUMN_NAME_TITLE,
titles[i]);
values.put(NewsContract.NewsEntry.COLUMN_NAME_AUTHOR,
authors[i]);
values.put(NewsContract.NewsEntry.COLUMN_NAME_CONTENT,
contents[i]);
values.put(NewsContract.NewsEntry.COLUMN_NAME_IMAGE,
images.getString(i));
long r = sqLiteDatabase.insert(
NewsContract.NewsEntry.TABLE_NAME,
null,
values);
}
}
4. 重载onUpgrade()方法
由于数据库文件存储于App 的私有目录内,当数据库结构发生变化时,通常意味着该数据库中的表结构需要进行调整。正确的做法是将DATABASE_VERSION 值递增,并重载onUpdate() 方法,在该方法中实现调整数据库表结构的逻辑。
在本项目中进将数据库表进行删除,并重新调用onCreate() 方法创建数据库表。
Copy public class MyDbOpenHelper extends SQLiteOpenHelper {
...
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase,
int oldVersion, int newVersion) {
sqLiteDatabase.execSQL(SQL_DELETE_ENTRIES);
onCreate(sqLiteDatabase);
}
}
5. 读取数据库数据
通过SQLiteOpenHelper 类的getReadableDatabase() 方法获得SQLiteDatabase 对象,并通过该对象执行query() 方法查询News 表中的数据。
通常数据库的查询操作比较耗时,需要结合Loader 一起使用,在本项目中为了简便起见,直接在MainActivity 的onCreate() 方法中完成查询操作。
Copy @Override
protected void onCreate(Bundle savedInstanceState) {
...
myDbHelper = new MyDbOpenHelper(MainActivity.this);
db = myDbHelper.getReadableDatabase();
Cursor cursor1 = db.query(
NewsContract.NewsEntry.TABLE_NAME,
null, null, null, null, null, null);
...
获得Cursor 对象后,可以通过循环,借助于moveToNext() ,getString() 等方法读取每一条记录的字段值。 getString() 等方法需要的参数是列编号,因此需要使用getColumnIndex() 方法将列名转换为Cursor 对象指向结果集中的列编号。
Copy ...
List<News> newsList = new ArrayList<>();
int titleIndex = cursor.getColumnIndex(
NewsContract.NewsEntry.COLUMN_NAME_TITLE);
int authorIndex = cursor.getColumnIndex(
NewsContract.NewsEntry.COLUMN_NAME_AUTHOR);
int imageIndex = cursor.getColumnIndex(
NewsContract.NewsEntry.COLUMN_NAME_IMAGE);
while (cursor.moveToNext()) {
News news = new News();
String title = cursor.getString(titleIndex);
String author = cursor.getString(authorIndex);
String image = cursor.getString(imageIndex);
Bitmap bitmap = BitmapFactory.decodeStream(
getClass().getResourceAsStream("/" + image));
news.setTitle(title);
news.setAuthor(author);
news.setImage(bitmap);
list.add(news);
}
NewsAdapter newsAdapter = new NewsAdapter(
MainActivity.this,
R.layout.list_item,
newsList);
lvNewsList.setAdapter(newsAdapter);
步骤四,编译、部署APK
编译并部署Code07 项目的APK,运行效果应与之前的相同,但所有数据均存储与SQLite 数据库中。
实验小结
通过本次实验,你应该掌握了如下知识内容:
使用SQLiteOpenHelper 对数据库进行管理;