Android Smart Dict - 单词导入 - 词汇表解析
Android Smart Dict - 单词导入 - 词汇表解析
有了一个可以用的文件浏览器之后,我们就可以选择并且得到要加载的词汇表文件。当用户点击了某一个词汇表文件之后,浏览器把此文件返回给词汇表解析器,解析出每一个单词,然后插入数据表。
如何解析?为了不在这一部分花费掉太多的时间,我只需要做简单的格式定制和解析就好,后面再做性能上面的优化工作。
在其他地方kiang到一个词汇表,格式很简单,就像这样:
-------------------------------------------------------------------------
ability [4'biliti]
n. (U)能力,本领;(C)才能,才智
-------------------------------------------------------------------------
有人一定发现了,音标没显示对,是的,我们后面再来处理这些细节问题。
我现在只需要提取三部分出来就可以了,本来也就只有三部分:单词,音标,解释。了便于显示信息给用户,我在文件前面加入一个header,来描述一下这个词汇表的名称和单词数量。用两个关键字来定义开始和结束。类似xml文件的方式,注意是类似,并不完全一致。在这里,我只是为了解析的方便。
-------------------------------------------------------------------------
<header>
三个英语词汇
3
</header>
<words>
ability [4'biliti]
n. (U)能力,本领;(C)才能,才智
ability [4'biliti]
n. (U)能力,本领;(C)才能,才智
able ['eib4l]
adj. 能够…的,有能力的
</words>
-------------------------------------------------------------------------
header的名称叫“三个英语词汇”,单词数量是3个,单词部分用<words>和</words>包围起来,单词之间用空行隔开。这样够简单了吧,哈哈哈!
我定义一个叫BookParser的类,就负责解析这词库文件,它有一个公开的方法叫getNextWord()返回解析出来的单词对象。
代码如下:
- public class BookParser {
- private static final String TAG = "BookParser";
- private BookHeader mBookHeader;
- private BufferedReader mReader;
- public BookParser(File file) {
- try {
- InputStream in = new FileInputStream(file);
- mReader = new BufferedReader(new InputStreamReader(in, "UTF-8"));
- // Parse book header;
- String line = mReader.readLine();
- if(line.startsWith(ParseScheme.HEADER_TOKEN_START)) {
- mBookHeader = new BookHeader();
- String name = mReader.readLine();
- String number = mReader.readLine();
- mBookHeader.setBookName(name);
- mBookHeader.setWordNumber(Integer.valueOf(number));
- }
- // Move the cursor to the first word;
- while((line = mReader.readLine()) == null ||
- !ParseScheme.WORD_TOKEN_START.equalsIgnoreCase(line.trim())) {
- continue;
- }
- } catch (FileNotFoundException e) {
- Log.d(TAG, e.getLocalizedMessage());
- } catch (IOException e) {
- Log.d(TAG, e.getLocalizedMessage());
- }
- }
- public void close() {
- if(mReader != null) {
- try {
- mReader.close();
- mReader = null;
- mBookHeader = null;
- } catch (IOException e) {
- Log.d(TAG, e.getLocalizedMessage());
- }
- }
- }
- public BookHeader getBookHeader() {
- return mBookHeader;
- }
- /**
- * The format like:
- * word [symbol] translations
- */
- public Word getNextWord() throws IOException {
- Word word = new Word();
- String line = mReader.readLine().trim();
- if(!Utils.isEmpty(line) && !ParseScheme.WORD_TOKEN_END.equalsIgnoreCase(line)) {
- int offset = line.indexOf('[');
- if(offset < 0) {
- offset = line.indexOf(" ");
- if(offset < 0) {
- offset = line.length();
- }
- }
- word.setWord(line.substring(0, offset).trim());
- word.setSymbol(line.substring(offset).trim());
- line = mReader.readLine();
- String trans = line;
- while(!Utils.isEmpty(line)) {
- trans = trans + "\n" + line;
- line = mReader.readLine();
- }
- word.setTranslation(trans);
- return word;
- }
- return
这个一定要注意一点,文件存储为上面格式,在构造Reader的时候就要相应地注明以什么格式读取,比如我的是UTF-8格式。
- InputStream in = new FileInputStream(file);
- mReader = new BufferedReader(new InputStreamReader(in, "UTF-8"));
接着我需要用一个类来驱动解析,一个叫BookLoader的类,继承Thread驱动整个解析过程。
它内部有一个Listener,提供一些有用的回调方法。
- public static interface OnBookLoadListener {
- public void onStart();
- public void onLoadHeader(BookHeader header);
- public void onComplete(int state);
- public void onLoadWord(Word word);
- }
onStart():就是在解析开始之前调用,这里面可以弹出一些dialog显示一些相关信息。
onLoadHeader():这个方法是当解析完header之后,返回给调用者关于词库的信息,可以反馈给用户。
onComplete():这个很明显,在整个load完成以后调用,参数state用来提示状态,成功,还是取消还是出错。
onLoadWord():这个方法在每次load一个单词的时候调用,可以用来即时反馈用户目前在load那个单词。
这些方法都是用在UI里边给用户提示相关信息。
代码如下:
- public class BookLoader extends Thread {
- public static final int LOAD_STATE_SUCCESS = 0;
- public static final int LOAD_STATE_ERROR = 1;
- public static final int LOAD_STATE_CANCEL = 3;
- private static final String TAG = "BookLoader";
- private boolean mCancel;
- private BookParser mParser;
- private File mBookFile;
- private OnBookLoadListener mOnBookLoadListener;
- private Context mContext;
- public static interface OnBookLoadListener {
- public void onStart();
- public void onLoadHeader(BookHeader header);
- public void onComplete(int state);
- public void onLoadWord(Word word);
- }
- public BookLoader(Context context) {
- mContext = context;
- }
- public void setOnBookLoadListener(OnBookLoadListener l) {
- mOnBookLoadListener = l;
- }
- public void startParse(File bookFile) {
- if(mOnBookLoadListener == null) {
- throw new RuntimeException("The OnBookLoadListener is null, " +
- "you have to call setOnBookLoadListener() method first!");
- }
- mBookFile = bookFile;
- start();
- mOnBookLoadListener.onStart();
- }
- @Override
- public void run() {
- mParser = new BookParser(mBookFile);
- mOnBookLoadListener.onLoadHeader(mParser.getBookHeader());
- final DatabaseHelper mDatabaseHelper = DatabaseHelper.getInstance(mContext);
- int counter = 0;
- try {
- while (true) {
- if(mCancel) {
- mOnBookLoadListener.onComplete(LOAD_STATE_CANCEL);
- return;
- }
- Word word = mParser.getNextWord();
- // If word is null, load finished.
- if(word == null) {
- if(mParser.getBookHeader().getWordNumber() == counter) {
- mOnBookLoadListener.onComplete(LOAD_STATE_SUCCESS);
- } else {
- mOnBookLoadListener.onComplete(LOAD_STATE_ERROR);
- }
- return;
- } else {
- mOnBookLoadListener.onLoadWord(word);
- counter++;
- // Insert word to database;
- mDatabaseHelper.insertWord(word);
- }
- // Give chance for UI thread change its state;
- Thread.sleep(10);
- }
- } catch (IOException e) {
- Log.d(TAG, e.getLocalizedMessage());
- mOnBookLoadListener.onComplete(LOAD_STATE_ERROR);
- } catch (InterruptedException e) {
- Log.d(TAG, e.getLocalizedMessage());
- mOnBookLoadListener.onComplete(LOAD_STATE_ERROR);
- } finally {
- if(mParser != null) {
- mParser.close();
- mParser = null;
- }
- }
- }
- }
为了让UI线程能够即时地显示最新状态,必须在while循环里面每次sleep几毫秒,不然UI的显示就会stutter。
现在来看看我在Activity里边做了什么。
主要就是实现OnBookLoadListener这个接口,开始load的时候弹出dialog,当header解析出来以后,显示词库的名称,单词数量,每次load一个单词就相应地显示到dialog里面,完成load以后显示结果信息。
代码如下:
- private void loadBook(File bookFile) {
- BookLoader loader = new BookLoader(this);
- loader.setOnBookLoadListener(new OnBookLoadListener() {
- private ProgressDialog dialog;
- @Override
- public void onStart() {
- FileLoaderActivity.this.runOnUiThread(new Runnable() {
- public void run() {
- dialog = new ProgressDialog(FileLoaderActivity.this);
- dialog.setIcon(R.drawable.file_book);
- dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
- dialog.setCancelable(false);
- dialog.setMessage("");
- dialog.setTitle(getText(R.string.loading_word).toString().
- replace("%s", ""));
- dialog.setButton(ProgressDialog.BUTTON_NEUTRAL, getText(R.string.cancel),
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialogInterface, int whichButton) {
- final CharSequence buttonTxt = dialog.getButton(
- ProgressDialog.BUTTON_NEUTRAL).getText();
- if (buttonTxt.equals(getText(R.string.cancel))) {
- // TODO: Cancel loading;
- } else if (buttonTxt.equals(getText(R.string.ok))) {
- dialog.dismiss();
- }
- }
- });
- dialog.show();
- }
- });
- }
- @Override
- public void onLoadWord(final Word word) {
- mDatabaseHelper.insertWord(word);
- FileLoaderActivity.this.runOnUiThread(new Runnable() {
- public void run() {
- dialog.setMessage(word.getWord() + ' ' + word.getSymbol());
- dialog.incrementProgressBy(1);
- }
- });
- }
- @Override
- public void onLoadHeader(final BookHeader header) {
- FileLoaderActivity.this.runOnUiThread(new Runnable() {
- public void run() {
- dialog.setTitle(header.getBookName());
- dialog.setMax(header.getWordNumber());
- }
- });
- }
- @Override
- public void onComplete(int state) {
- if(state == BookLoader.LOAD_STATE_SUCCESS) {
- FileLoaderActivity.this.runOnUiThread(new Runnable() {
- public void run() {
- dialog.setMessage(getText(R.string.load_word_success));
- dialog.getButton(ProgressDialog.BUTTON_NEUTRAL).setText(getText(R.string.ok));
- }
- });
- }
- }
- });
- loader.startParse(bookFile);
- }
OK,来看看效果如何:
还行,只是load速度比较慢,因为我们是一条一条地insert所以会很慢,后面我会考虑用transaction来做,这样整个过程就会快很多。
评论暂时关闭