图1 将dictionary.db文件复制到res
aw目录中
2. 如何将打开res
aw目录中的数据库文件?
解答:在Android中不能直接打开res
aw目录中的数据库文件,而需要在程序第一次启动时将该文件复制到手机内存或SD卡的某个目录中,然后再打开该数据库文件。复制的基本方法是使用getResources().openRawResource方法获得res
aw目录中资源的InputStream对象,然后将该InputStream对象中的数据写入其他的目录中相应文件中。在Android SDK中可以使用SQLiteDatabase.openOrCreateDatabase方法来打开任意目录中的SQLite数据库文件。
3. 如果在AutoCompleteTextView组件中输入两个及以上字母时显示以所输入字符串开头的所有单词列表?
解答:AutoCompleteTextView所使用的Adapter是一个自定义的Adapter类,类的结构如下:
public class DictionaryAdapter extends CursorAdapter{}要注意的是,不能将整个数据库中的单词都查出,然后生成一个Adapter对象再使用setAdapter方法来设置AutoCompleteTextView组件的Adapter对象。AutoCompleteTextView组件不会为我们筛选以某个字符串开头的单词。这些工作需要开发人员通过编码来实现。
图2 显示以输入字符串开头的单词列表
复制并打开保存英文单词的数据库文件
在本文实现的英文词典中使用openDatabase方法来打开数据库文件(该文件在SD卡的dictionary目录中,因此,要想运行本文实现的英文 词典,需要在手机或模拟器中需要安装SD卡)。如果该文件不存在,系统会自动创建/sdcard/dictionary目录,并将res
aw目录中的 dictionary.db文件复制到/sdcard/dictionary目录中。openDatabase方法的实现代码如下:
private SQLiteDatabase openDatabase() {try{ // 获得dictionary.db文件的绝对路径 String databaseFilename = DATABASE_PATH + "/" + DATABASE_FILENAME; File dir = new File(DATABASE_PATH); // 如果/sdcard/dictionary目录中存在,创建这个目录 if (!dir.exists())dir.mkdir(); // 如果在/sdcard/dictionary目录中不存在 // dictionary.db文件,则从res
aw目录中复制这个文件到 // SD卡的目录(/sdcard/dictionary) if (!(new File(databaseFilename)).exists()) {// 获得封装dictionary.db文件的InputStream对象InputStream is = getResources().openRawResource(R.raw.dictionary);FileOutputStream fos = new FileOutputStream(databaseFilename);byte[] buffer = new byte[8192];int count = 0;// 开始复制dictionary.db文件while ((count = is.read(buffer)) > 0){ fos.write(buffer, 0, count);}fos.close();is.close(); } // 打开/sdcard/dictionary目录中的dictionary.db文件 SQLiteDatabase database = SQLiteDatabase.openOrCreateDatabase( databaseFilename, null); return database;}catch (Exception e){}return null; }在openDatabase方法中使用了几个常量,这些常量是在程序的主类(Main)中定义的,代码如下:public class Main extends Activity implements OnClickListener, TextWatcher{ private final String DATABASE_PATH = android.os.Environment .getExternalStorageDirectory().getAbsolutePath() + "/dictionary"; private final String DATABASE_FILENAME = "dictionary.db";}查询单词public void onClick(View view) {String sql = "select chinese from t_words where english=?"; Cursor cursor = database.rawQuery(sql, new String[]{ actvWord.getText().toString() });String result = "未找到该单词.";// 如果查找单词,显示其中文信息if (cursor.getCount() > 0){ // 必须使用moveToFirst方法将记录指针移动到第1条记录的位置 cursor.moveToFirst(); result = cursor.getString(cursor.getColumnIndex("chinese"));}// 显示查询结果对话框new AlertDialog.Builder(this).setTitle("查询结果").setMessage(result).setPositiveButton("关闭", null).show(); }讲到这里我们应该了解一个dictionary.db中的t_words表的结果,该表只有两个字段:english和chinese。分别表示单词的英文和中文描述。如果要获得单词的中文描述,只需要查找chinese字段即可。如onClick方法中的代码所示。查询单词的效果如图3所示。
图3 查询英文单词
如果显示以输入字符串开头的单词列表
虽然到目前为止,我们的英文词典已经可以正常工作了,但为了方便读者使用,在本节将添加单词输入的自动提示功能。也就是说,如果读者在 AutoCompleteTextView组件中输入单词的前几个字母,该组件就会自动列出数据库中所有以该字符串开头的单词。效果如图2所示。拥有这样 的功能就可以使用户在只知道单词的前几个字母时也可以查找到相应的单词。
由于AutoCompleteTextView组件使用了自定义的Adapter类,下面先给出这个自定义的Adapter类的完整代码。
public class DictionaryAdapter extends CursorAdapter {private LayoutInflater layoutInflater;@Overridepublic CharSequence convertToString(Cursor cursor){ return cursor == null ? "" : cursor.getString(cursor .getColumnIndex("_id"));}// 用于将_id字段(也就是english字段)的值设置TextView组件的文本// view参数表示用于显示列表项的TextView组件private void setView(View view, Cursor cursor){ TextView tvWordItem = (TextView) view; tvWordItem.setText(cursor.getString(cursor.getColumnIndex("_id")));}@Overridepublic void bindView(View view, Context context, Cursor cursor){ setView(view, cursor);}@Overridepublic View newView(Context context, Cursor cursor, ViewGroup parent){ View view = layoutInflater.inflate(R.layout.word_list_item, null); setView(view, cursor); return view;}public DictionaryAdapter(Context context, Cursor c, boolean autoRequery){ super(context, c, autoRequery); // 通过系统服务获得LayoutInflater对象layoutInflater = (LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE);} }在编写DictionaryAdapter类时应注意如下3点: public CharSequence convertToString(Cursor cursor) { // 如果cursor不为null,返回Cursor对象的地址(cursor.toString())return cursor == null ? "" : cursor.toString(); } 覆盖后的convertToToString方法的源代码如下:public CharSequence convertToString(Cursor cursor){ return cursor == null ? "" : cursor.getString(cursor .getColumnIndex("_id"));}在这里要注意一下,当选中AutoCompleteTextView组件中单词列表中某一个单词后,系统会用convertToString方法的返回值来设置AutoCompleteTextView组件中的文本。因此,必须使用Cursor的getString来获得相应的字段值。public void afterTextChanged(Editable s){// 必须将english字段的别名设为_id Cursor cursor = database.rawQuery("select english as _id from t_words where english like ?",new String[]{ s.toString() + "%" });DictionaryAdapter dictionaryAdapter = new DictionaryAdapter(this,cursor, true);// actvWord是在Main类中定义的AutoCompleteTextView类型的变量actvWord.setAdapter(dictionaryAdapter);}从上面的代码中可以看到,在查询SQL语句中的english字段名的别名是“_id”。<?xml version="1.0" encoding="utf-8"?><TextView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/tvWordItem" android:layout_width="fill_parent" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceLarge" android:gravity="center_vertical" android:paddingLeft="6dip" android:textColor="#000"android:minHeight="?android:attr/listPreferredItemHeight"/>本文介绍了实现基于Android的英文词典的实现方法。实现英文词典主要需要解决3个问题:如何将保存英文单词的SQLite数据库文件随同apk文件一起发布;如何打开SD卡中的数据库文件;如何在AutoCompleteTextView组件显示以输入字符串开头的英文单词列表。在最后仍然要提一句的是在编写自定义DictionaryAdapter类时一定要覆盖contertToString方法,以便在用户选项某一个列表项时在AutoCompleteTextView组件中显示选中的单词,而不是Cursor对象地址。