您的位置:首页 > 移动开发

背单词App开发日记3

2015-07-24 23:16 429 查看
2015.07.22

距离上一次记录已经有5天时间了......这几天进度比较慢.主要是一直在纠结一些问题(后面会说到).最终将数据库封装好了!进度条又跳了一节有木有!!!

其实进度慢的原因主要是在纠结该怎么写.看了很多别人的写法,总感觉没办法用在我这里.一开始纠结要不要用SQLite,打算直接用读文件绑定控件的,后来发现行不通,还是得乖乖用SQLite.于是问题来了----怎么具体化这个数据库?其实面临的问题无非是一下几个

a.怎么创建数据库?

b.准备让数据库具有什么功能?

c.如何将单词表封装到数据库中?

d.如何将数据库和控件绑定----也就是让单词显示在屏幕上

e.如何处理进度问题和其他细节(比如占用更少资源,怎么避免重复创建等等)?

以上这些问题是我这几天来思考良久想到的接下来急需解决的问题,其他的细节可以暂且放在一边.今天主要记录的是数据库的准备工作.

本文基本上是参考scott's blog的代码改写的.本质上是重复.我下面会贴出代码还有相应简略的介绍,更多细节请移步http://blog.csdn.net/liuhe688/article/details/6715983/
查看:

1.常用方法概述:

//打开或创建test.db数据库
SQLiteDatabase db = openOrCreateDatabase("test.db", Context.MODE_PRIVATE, null);
db.execSQL("DROP TABLE IF EXISTS person");
//创建person表
db.execSQL("CREATE TABLE person (_id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR, age SMALLINT)");
Person person = new Person();
person.name = "john";
person.age = 30;
//插入数据
db.execSQL("INSERT INTO person VALUES (NULL, ?, ?)", new Object[]{person.name, person.age});

person.name = "david";
person.age = 33;
//ContentValues以键值对的形式存放数据
ContentValues cv = new ContentValues();
cv.put("name", person.name);
cv.put("age", person.age);
//插入ContentValues中的数据
db.insert("person", null, cv);

cv = new ContentValues();
cv.put("age", 35);
//更新数据
db.update("person", cv, "name = ?", new String[]{"john"});

Cursor c = db.rawQuery("SELECT * FROM person WHERE age >= ?", new String[]{"33"});
while (c.moveToNext()) {
int _id = c.getInt(c.getColumnIndex("_id"));
String name = c.getString(c.getColumnIndex("name"));
int age = c.getInt(c.getColumnIndex("age"));
Log.i("db", "_id=>" + _id + ", name=>" + name + ", age=>" + age);
}
c.close();

//删除数据
db.delete("person", "age < ?", new String[]{"35"});

//关闭当前数据库
db.close();

//删除test.db数据库
deleteDatabase("test.db");
这些是比较基础的,对于插入删除我们还有:
db.executeSQL(String sql);
db.executeSQL(String sql, Object[] bindArgs);//sql语句中使用占位符,然后第二个参数是实际的参数集
db.insert(String table, String nullColumnHack, ContentValues values);
db.update(String table, Contentvalues values, String whereClause, String whereArgs);
db.delete(String table, String whereClause, String whereArgs);
对于查询操作,可以这样:
db.rawQuery(String sql, String[] selectionArgs);
db.query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy);
db.query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit);
db.query(String distinct, String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit);
他们最终都返回一个Cursor对象,其操作有:
c.move(int offset);	//以当前位置为参考,移动到指定行
c.moveToFirst();	//移动到第一行
c.moveToLast();		//移动到最后一行
c.moveToPosition(int position);	//移动到指定行
c.moveToPrevious();	//移动到前一行
c.moveToNext();		//移动到下一行
c.isFirst();		//是否指向第一条
c.isLast();		//是否指向最后一条
c.isBeforeFirst();	//是否指向第一条之前
c.isAfterLast();	//是否指向最后一条之后
c.isNull(int columnIndex);	//指定列是否为空(列基数为0)
c.isClosed();		//游标是否已关闭
c.getCount();		//总数据项数
c.getPosition();	//返回当前游标所指向的行数
c.getColumnIndex(String columnName);//返回某列名对应的列索引值
c.getString(int columnIndex);	//返回当前行指定列的值
具体细节参见链接.

2.具体在app里面的实现:



我将和数据库有关的文件单独建了一个包.这三个文件中:DBHelper是一个继承SQLiteOpenHelper的子类,他的作用是替我们生成一个生成一个新的数据库,并且含有一个更新版本的操作,这两个方法会在onCreate方法执行后启动.具体代码见下:
<span style="font-family:SimSun;">package mywordapp.wordsqlite;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class DBHelper extends SQLiteOpenHelper{

private static final String DATABASE_NAME = "vocabulary_db";
private static final int DATABASE_VERSION = 1;

public DBHelper(Context context)
{//CursorFactory 设置为默认值null
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}

//数据库第一次被创建时onCreate会被调用
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE IF NOT EXISTS word"+
"(_id INTEGER PRIMARY KEY AUTOINCREMENT,en_meaning VARCHAR,cn_meaning VARCHAR,repeating_number INTEGER)");
}

//如果DATABASE_VERSION的值被改成2,系统发现现有数据库版本不同,就会调用onUpgrade
@Override
public void onUpgrade(SQLiteDatabase db,int oldVersion,int newVersion)
{
db.execSQL("ALTER TABLE word ADD COLUMN other STRING");
}
}</span>


当然,在生成数据库之前,写一个相关的对象是必要的,这就是Word类.它代表的就是单词这个类,其中的属性我目前只设置了三个----分别是en_meaning(单词的英文),cn_meaning()单词的中文释义,repeating_number(已经记忆的遍数),但让还有id,这个是数据库默认的.非常简单,注意每个属性都必须要get和set方法,至于单独写还是写成一个函数看自己了,这里不在多说.有了这个类之后,我们便可以着手将一些常用的功能封装在DBManager这个类里面以便我们后续的操作:
<span style="font-family:SimSun;">package mywordapp.wordsqlite;

import java.util.ArrayList;
import java.util.List;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

public class DBManager {

private DBHelper helper;
private SQLiteDatabase db;

public DBManager(Context context)
{
helper=new DBHelper(context);
db=helper.getWritableDatabase();
}

/*
* 添加单词*/
public void add(List<Word> words)
{
db.beginTransaction();
try
{
for (Word word : words)
{
db.execSQL("INSERT INTO word VALUES(null,?,?,?)",new Object[]{word.en_meaning,word.cn_meaning,word.repeating_number});
}
db.setTransactionSuccessful();
}finally{
db.endTransaction();
}
}

/*更新记忆次数*/
public void updateRepeating_Number(Word word)
{
ContentValues cv=new ContentValues();
cv.put("repeating_number", word.repeating_number);
db.update("word", cv, "en_meaning=?", new String[]{word.en_meaning});
}

/*删除烂熟的单词*/
public void deleteAdroidWord(Word word) {
db.delete("word", "repeating_number >=40", new String[]{String.valueOf(word.repeating_number)});
}

/*查询所有单词,返回一个完整的单词表*/
public List<Word> query()
{
ArrayList<Word> words=new ArrayList<Word>();
Cursor csr=queryTheCursor();
while (csr.moveToNext()) {
Word word = new Word();
word._id=csr.getInt(csr.getColumnIndex("_id"));
word.en_meaning=csr.getString(csr.getColumnIndex("en_meaning"));
word.cn_meaning=csr.getString(csr.getColumnIndex("cn_meaning"));
word.repeating_number=csr.getInt(csr.getColumnIndex("repeating_number"));
}
return words;
}
public Cursor queryTheCursor() {
Cursor c=db.rawQuery("SELECT*FROM word", null);
return c;
}

/*关闭数据库*/
public void closeDB()
{
db.close();
}
}</span>
我们在DBManager构造方法中实例化DBHelper并获取一个SQLiteDatabase对象,作为整个应用的数据库实例;在添加多个Person信息时,我们采用了事务处理,确保数据完整性;最后我们提供了一个closeDB方法,释放数据库资源,这一个步骤在我们整个应用关闭时执行,这个环节容易被忘记,所以要注意一下。至于选择获取可写数据库而不是只读数据库的原因,我没有深究,有兴趣的可以看一下链接中的解释.

3.控件的设置
经过以上的设置,数据库已经有框架了,万事俱备,只欠东风----东风就是我们单词表.限于能力和自己的惰性,我没有去学习比较可行的模式:诸如读写缓存,读写SDcard,读写raw之类的方法.我的做法很原始----利用asset里面的文件不会被压缩的特性将单词表的txt格式存入,然后用读写文件的方式将它里面的内容导入数据库.
<span style="font-family:SimSun;">package example.mywordapp;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.http.util.EncodingUtils;

import android.app.Activity;
import android.database.Cursor;
import android.database.CursorWrapper;
import android.os.Bundle;
import android.view.View;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.SimpleCursorAdapter;
import mywordapp.wordsqlite.DBManager;
import mywordapp.wordsqlite.Word;
public class MySQLiteActivity extends Activity{

private DBManager mgr;
private ListView listView;

@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sqlite);
listView=(ListView)findViewById(R.id.listView);
//初始化DBManager
mgr=new DBManager(this);
}

//添加单词
public void add(View view) {
ArrayList<Word> words=new ArrayList<Word>();

String fileName = "GRE.txt"; //单词表名字  ,文件要放在asset文件夹下
String res="";
try{

//得到资源中的asset数据流
InputStream in = getResources().getAssets().open(fileName);

int length = in.available();
byte [] buffer = new byte[length];

in.read(buffer);
in.close();
res = EncodingUtils.getString(buffer, "UTF-8");

}catch(Exception e){

e.printStackTrace();

}
String []tmp=res.split("\n");

for(int i=0;i<tmp.length;i++)
{

String []tmp_word=tmp[i].split(" ");
System.out.println(i+1+"  "+tmp_word[0]+"      "+tmp_word[1]);
Word word=new Word(tmp_word[0],tmp_word[1],0);
words.add(word);
}

mgr.add(words);
}

//更新记忆次数
public void update(View view)
{
Word word=new Word();
word.en_meaning="son";
word.repeating_number=1;
mgr.updateRepeating_Number(word);
}

//利用HashMap获得单词以便查询
public void query(View view) {
List<Word> words = mgr.query();
ArrayList<Map<String, Integer>> list = new ArrayList<Map<String, Integer>>();
for (Word word : words) {
HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("repeating_number", word.repeating_number);
//map.put("en_meaning", word.en_meaning);
list.add(map);
}
SimpleAdapter adapter = new SimpleAdapter(this, list, android.R.layout.simple_list_item_2,
new String[]{"en_meaning"}, new int[]{android.R.id.text1});
listView.setAdapter(adapter);
}

//删除烂熟的单词
public void delete(View view) {
Word word = new Word();
word.repeating_number = 40;
mgr.deleteAdroidWord(word);
}

//全体查询,返回的是整个单词表,然后利用SimpleAdapter和ListView绑定在一起
//注意要确保查询结果中有"_id"列
@SuppressWarnings("deprecation")
public void queryTheCursor(View view) {
Cursor c = mgr.queryTheCursor();
startManagingCursor(c); //托付给activity根据自己的生命周期去管理Cursor的生命周期
CursorWrapper cursorWrapper = new CursorWrapper(c) {
@Override
public String getString(int columnIndex) {
//在英文后加上中文
if (getColumnName(columnIndex).equals("en_meaning")) {
String cn_meaning = getString(getColumnIndex("cn_meaning"));
return  super.getString(columnIndex)+"   "+cn_meaning;
}
return super.getString(columnIndex);
}
};
//下面是在绑定语句----其结果是每个list显示两行:第一行是单词+释义,第二行是记忆次数.
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_2,
cursorWrapper, new String[]{"en_meaning", "repeating_number"}, new int[]{android.R.id.text1, android.R.id.text2});
ListView listView = (ListView) findViewById(R.id.listView);
listView.setAdapter(adapter);
}
}</span><span style="font-family:SimHei;">
</span>
注意的一点是加入调试的时候发现logcat里面显示id列找不到证明你的word类里面的id列的名称不是"_id",这个很重要,解决办法是要么按规范做(列名称改为_id而不是id),要么在数据库里面的查询语句该名称(声明在id里面查而不是_id)

以上就是建立数据库的全过程,进度条感觉前进了好大一截!!!好开森!!!!

接下来是考虑如何将单词导入进去,加油!!!!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: