android 导出带数据库文件的APK


http://www.ophonesdn.com/article/show/111


技术文章 基于OPhone SDK1.5的英文电子词典的实现
基于OPhone SDK1.5的英文电子词典的实现
OPhone平台开发, 2009-12-07 11:06:42

标签 : OPhone SDK1.5 英文词典

       英文词典是手机中经常使用的应用。因此,在本文将结合中国移动最新的OPhone SDK1.5来讨论如何实现一个OPhone版的英文词典。实现英文词典的方法很多。在本文使用了SQLite数据库来保存英文单词信息。系统通过SQLite数据库中保存的单词信息来查找到与指定英文对应的中文信息。当然,实现这样一个英文词典需要解决一系列技术问题。例如,如何将保存英文单词信息的数据库文件随程序(apk文件)一起发布;发布后如何打开数据库;如何在输入前几个字母后,在AutoCompleteTextView组件提示列表中显示以所输入字符串开头的所有单词。在本章将逐渐给出这些问题的详细答案。
      关于本文实现的英文电子词典的完整代码请读者参本文提供的源代码。

电子词典的程序结构
       本文实现的电子词典可以从数据库中查找单词的中文解释。程序的界面由一个AutoCompleteTextView和一个Button组成,这两个组件在main.xml文件中定义,代码如下:
view plaincopy to clipboardprint?
<?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"> 
    <AutoCompleteTextView android:id="@+id/actvWord" 
        android:layout_width="fill_parent" android:layout_height="wrap_content" 
        android:layout_marginTop="10dp" android:singleLine="true" /> 
    <Button android:id="@+id/btnSelectWord" android:layout_width="wrap_content" 
        android:layout_height="wrap_content" android:text="查单词" /> 
</LinearLayout> 
     在系统中只有一个Main类,负责处理电子词典的所有功能。在该类的onCreate方法中装载了main.xml文件,装载后的效果如图1所示。

        在界面上方的文本框中可以输入要查找的单词,如图2所示。

如何建立SQLite数据库
        在本例中提供了一个dictionary.db数据库文件,该数据库中包含了一个t_words表,在这个表中保存了英文单词的相关信息。但这个数据库是事先建立的,在本节将介绍一下如何在PC上建立这个数据库文件及t_words表。
SQLite数据库是一个开源免费的数据库,支持多种数据库接口。Ophone系统正是使用了这种数据库作为其优秀的数据存储方案。建立SQLite数据库的工具很多,例如,SQlite官方提供了一个控制台程序,可以建立并维护SQLite数据库工具,读者可以从如下的地址下载这个控制台程序:
http://www.sqlite.org/download.html

         目前这个程序的最新版本是3.6.2,读者可以下载Sqlite-3_6_20.zip文件,解压后只有一个sqlite3.exe文件,启动这个文件,即可进入控制台界面。如图3所示。

         如果想建立一个数据库,只需要输入如下命令即可:
view plaincopy to clipboardprint?
sqlite3.exe dictionary.db 
     这时控制台程序还不会立即生成dictionary.db文件,直到输入一条与数据库相关的命令后,才会在当前目录生成一个dictionary.db,例如,输入如下的SQL语句建立一个表:
view plaincopy to clipboardprint?
create table table1 ( 
   id integer primary key, 
   age int, 
   name text  
); 

       虽然sqlite3.exe可以很容易地生成并管理数据库,但毕竟是控制台程序,使用起来很不方便。因此,在这里作者介绍一种可视化的管理SQLite数据库的工具:SQLite Expert Professional。这种工具有些类似于SQL Server的企业管理器,可以可视化地建立数据库、表、视图等数据库组件。SQLite Expert Professional的主界面如图4所示。

        读者可以从如下的地址下载SQLite Expert Professional。
http://www.sqliteexpert.com

           下面我们来使用SQLite Expert Professional建立一个dictionary.db数据库,并建立一个t_words表,结果如图5所示。通过图5中的Design标签页可以修改t_words表的结构,例如,添加、删除字段等。

        从图5所示的表结构可以看出,t_words有两个字段:english和chinese,分别用来保存英文单词和中文解释信息。最后就需要我们向t_words表中添加等查找的单词信息了。至于这些信息,读者可以在网上查找或自己输入,在本例中只提供了大学英语4级(CET4)的大纲词汇。由于本文的目的只是介绍如何编写一个基于OPhone SDK1.5的电子词典,因此,关于t_words表中实际的英文单词信息读者可以根据自己的需要添加。
如何处理电子词典中的数据库文件
        在上一节已经介绍了如何建立本例所使用的dictionary.db数据库和t_words表,那么在建立完dictionary.db数据库文件,并添加相应的数据后,需要将这个文件放在Eclipse工程的res\raw目录中与apk文件一起发布。但在这里有一个问题。就是这个数据库文件被放在了res\raw目录中同apk文件一起发布。那么如何在安装apk后打开并使用这个数据库文件呢?
       OPhone SDK不能直接打开res\raw目录中的数据库文件,不过我们可以通过将这个文件复制到手机内存或SD卡上来访问。复制的方法也很简单,就是在程序第一次启动时判断要复制的目标路径是否存在该数据库文件,如果不存在该数据库文件,那先复制这个文件,然后再打开数据库文件。

       复制数据库文件可以通过getResources().openRawResource方法获得res\raw目录中资源的InputStream对象,然后将该InputStream对象中的数据写入其他的目录中相应文件中。打开数据库可以使用SQLiteDatabase.openOrCreateDatabase方法来打开SQLite数据库文件。openOrCreateDatabase方法可以打开任意目录下的数据库文件。

       复制和打开数据库文件的工作由openDatabase方法来完成,这个方法的完整代码如下:

view plaincopy to clipboardprint?
<?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"> 
    <AutoCompleteTextView android:id="@+id/actvWord" 
        android:layout_width="fill_parent" android:layout_height="wrap_content" 
        android:layout_marginTop="10dp" android:singleLine="true" /> 
    <Button android:id="@+id/btnSelectWord" android:layout_width="wrap_content" 
        android:layout_height="wrap_content" android:text="查单词" /> 
</LinearLayout> 
      在系统中只有一个Main类,负责处理电子词典的所有功能。在该类的onCreate方法中装载了main.xml文件,装载后的效果如图1所示。

 
         在界面上方的文本框中可以输入要查找的单词,如图2所示。

如何建立SQLite数据库
            在本例中提供了一个dictionary.db数据库文件,该数据库中包含了一个t_words表,在这个表中保存了英文单词的相关信息。但这个数据库是事先建立的,在本节将介绍一下如何在PC上建立这个数据库文件及t_words表。
            SQLite数据库是一个开源免费的数据库,支持多种数据库接口。Ophone系统正是使用了这种数据库作为其优秀的数据存储方案。建立SQLite数据库的工具很多,例如,SQlite官方提供了一个控制台程序,可以建立并维护SQLite数据库工具,读者可以从如下的地址下载这个控制台程序:
http://www.sqlite.org/download.html
        
           目前这个程序的最新版本是3.6.2,读者可以下载Sqlite-3_6_20.zip文件,解压后只有一个sqlite3.exe文件,启动这个文件,即可进入控制台界面。如图3所示。

         如果想建立一个数据库,只需要输入如下命令即可:
view plaincopy to clipboardprint?
sqlite3.exe dictionary.db 
              这时控制台程序还不会立即生成dictionary.db文件,直到输入一条与数据库相关的命令后,才会在当前目录生成一个dictionary.db,例如,输入如下的SQL语句建立一个表:
view plaincopy to clipboardprint?
create table table1 ( 
   id integer primary key, 
   age int, 
   name text  
); 
        虽然sqlite3.exe可以很容易地生成并管理数据库,但毕竟是控制台程序,使用起来很不方便。因此,在这里作者介绍一种可视化的管理SQLite数据库的工具:SQLite Expert Professional。这种工具有些类似于SQL Server的企业管理器,可以可视化地建立数据库、表、视图等数据库组件。SQLite Expert Professional的主界面如图4所示。

       读者可以从如下的地址下载SQLite Expert Professional。
http://www.sqliteexpert.com

           下面我们来使用SQLite Expert Professional建立一个dictionary.db数据库,并建立一个t_words表,结果如图5所示。通过图5中的Design标签页可以修改t_words表的结构,例如,添加、删除字段等。


       从图5所示的表结构可以看出,t_words有两个字段:english和chinese,分别用来保存英文单词和中文解释信息。最后就需要我们向t_words表中添加等查找的单词信息了。至于这些信息,读者可以在网上查找或自己输入,在本例中只提供了大学英语4级(CET4)的大纲词汇。由于本文的目的只是介绍如何编写一个基于Ophone SDK1.5的电子词典,因此,关于t_words表中实际的英文单词信息读者可以根据自己的需要添加。

如何处理电子词典中的数据库文件
        在上一节已经介绍了如何建立本例所使用的dictionary.db数据库和t_words表,那么在建立完dictionary.db数据库文件,并添加相应的数据后,需要将这个文件放在Eclipse工程的res\raw目录中与apk文件一起发布。但在这里有一个问题。就是这个数据库文件被放在了res\raw目录中同apk文件一起发布。那么如何在安装apk后打开并使用这个数据库文件呢?

        Ophone SDK不能直接打开res\raw目录中的数据库文件,不过我们可以通过将这个文件复制到手机内存或SD卡上来访问。复制的方法也很简单,就是在程序第一次启动时判断要复制的目标路径是否存在该数据库文件,如果不存在该数据库文件,那先复制这个文件,然后再打开数据库文件。

        复制数据库文件可以通过getResources().openRawResource方法获得res\raw目录中资源的InputStream对象,然后将该InputStream对象中的数据写入其他的目录中相应文件中。打开数据库可以使用SQLiteDatabase.openOrCreateDatabase方法来打开SQLite数据库文件。openOrCreateDatabase方法可以打开任意目录下的数据库文件。

        复制和打开数据库文件的工作由openDatabase方法来完成,这个方法的完整代码如下:
view plaincopy to clipboardprint?
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\raw目录中复制这个文件到 
        // 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)中定义的,代码如下:
view plaincopy to clipboardprint?
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"; 
... ... 

       从DATABASE_PATH常量可以看出,本系统将dictionary.db文件复制到了/sdcard/dictionary目录中,复制的结果如图6所示。

         在本节的最后,我们看一看如何在Main类的onCreate方法的实现,在这个方法中调用了openDatabase方法来打开SD卡上的数据库,并完成了一些组件的装载和事件的绑定工作,代码如下:
view plaincopy to clipboardprint?
public void onCreate(Bundle savedInstanceState) 

    super.onCreate(savedInstanceState); 
    setContentView(R.layout.main); 
    // 打开数据库,database是在Main类中定义的一个SQLiteDatabase类型的变量 
    database = openDatabase(); 
    // 下面的代码装载了相关组件,并设置了相应的事件 
    btnSelectWord = (Button) findViewById(R.id.btnSelectWord); 
    actvWord = (AutoCompleteTextView) findViewById(R.id.actvWord); 
    btnSelectWord.setOnClickListener(this); 
    actvWord.addTextChangedListener(this); 

查询单词
        下面来看看在英文词典中如何查找单词。查找单词的工作由【查单词】按钮的onClick事件方法完成,先看看这个方法的代码。
view plaincopy to clipboardprint?
public void onClick(View view) 

    //  查找单词的SQL语句 
    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方法中的代码所示。
   
     查询单词的效果如图7所示。

如何使查找单词更智能化
         虽然到现在为止,我们的英文词典已经可以正常工作了。但我们的探索精神是无止境的。大家可以想象,如果某位用户要查找一个单词,却突然想不起来这个单词的完整拼写了,而只记得单词的前面几个字母,那该怎么办呢?
        也许有很多读者用过很多有查找功能的系统,其中Google搜索就是其中之一。只要输入一些要查找的字符串,Google搜索框就会为我们列出以这些输入串开头的经常使用到的搜索字符串,如图8所示。

       实际上,这样的功能使用Ophone SDK也很容易实现。在电子词典中之所以使用AutoCompleteTextView组件输入单词就是为了达到这个目的。AutoCompleteTextView类有一个setAdapter方法,可以将一个Adapter对象绑定到AutoCompleteTextView组件中,一但在AutoCompleteTextView组件中输入两个及以上字母后,AutoCompleteTextView组件就会在这个绑定的Adapter对象中查找以所输入的字符串开头的单词,并显示在AutoCompleteTextView组件下方的列表中。效果如图9所示。


        通过这个的方法,即使用户只记得单词的头几个字母,也可以查找这个单词的完整拼写形式。关于实现这个功能的详细过程,请读者看一下节的介绍。
如何显示以输入字符串开头的单词列表
       由于Adapter对象既可以从数组、List等对象中获得数据,又可以从Cursor对象中获得数据。由于电子词典使用了SQLite数据库来保存英文单词信息,因此,在本例中使用从Cursor对象中获得数据的方法来创建Adapter对象。这需要我们自定义一个DictionaryAdapter类,该类需要从CursorAdapter类继承,代码如下:

view plaincopy to clipboardprint?
public class DictionaryAdapter extends CursorAdapter 

    private LayoutInflater layoutInflater; 
    @Override 
    public 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"))); 
    } 
 
    @Override 
    public void bindView(View view, Context context, Cursor cursor) 
    { 
        setView(view, cursor); 
    } 
 
    @Override 
    public 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类时应注意如下4点:
         1. 为了将Cursor对象与AutoCompleteTextView组件绑定, DictionaryAdapter类必须从CursorAdapter类继承。
         2.  由于CursorAdapter类中的convertToString方法直接返回了Cursor对象的地址,因此,在DictionaryAdapter类中必须覆盖convertToString方法,以返回当前选中的单词。CursorAdapter类中的convertToString方法的源代码。

view plaincopy to clipboardprint?
public CharSequence convertToString(Cursor cursor) 

        //  如果cursor不为null,返回Cursor对象的地址(cursor.toString()) 
    return cursor == null ? "" : cursor.toString(); 

   覆盖后的convertToToString方法的源代码如下:
view plaincopy to clipboardprint?
public CharSequence convertToString(Cursor cursor) 

    return cursor == null ? "" : cursor.getString(cursor 
                    .getColumnIndex("_id")); 

     在这里要注意一下,当选中AutoCompleteTextView组件中单词列表中某一个单词后,系统会用convertToString方法的返回值来设置AutoCompleteTextView组件中的文本。因此,必须使用Cursor的getString来获得相应的字段值。

       3.  由于将Cursor对象与Adapter绑定时必须要有一个叫“_id”的字段,因此,在本例中将english字段名映射成了“_id”字段。
为了监视AutoCompleteTextView组件中的文本输入情况,需要实现android.text.TextWatcher接口。在该接口中只需要实现afterTextChanged方法即可,代码如下:

view plaincopy to clipboardprint?
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”。

      4.  在DictionaryAdapter类中需要使用bindView和newView方法设置每一个列表项。bindView方法负责设置已经存在的列表项,也就是该列表项已经生成了相应的组件对象。而newView方法负责设置新的列表项,在该方法中需要创建一个View对象来显示当前的列表项。在本例中使用word_list_item.xml布局文件来显示每一个列表项,代码如下:

view plaincopy to clipboardprint?
<?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" 
/> 
总结
       本文介绍了基于Ophone SDK1.5的英文词典的实现方法。实现英文词典主要需要解决3个问题:如何将保存英文单词的SQLite数据库文件随同apk文件一起发布;如何打开SD卡中的数据库文件;如何在AutoCompleteTextView组件显示以输入字符串开头的英文单词列表。下面来总结一下这3个技术问题的基本解决方案。
    
       1. 如何将SQLite数据库(dictionary.db文件)与apk文件一起发布?
解答:可以将dictionary.db文件复制到Eclipse Ophone工程中的res\raw目录中。所有在res\raw目录中的文件不会被压缩,这样可以直接提取该目录中的文件。   

        2. 如何将打开res\raw目录中的数据库文件?
解答:在Ophone中不能直接打开res\raw目录中的数据库文件,而需要在程序第一次启动时将该文件复制到手机内存或SD卡的某个目录中,然后再打开该数据库文件。复制的基本方法是使用getResources().openRawResource方法获得res\raw目录中资源的InputStream对象,然后将该InputStream对象中的数据写入其他的目录中相应文件中。在Ophone SDK中可以使用SQLiteDatabase.openOrCreateDatabase方法来打开任意目录中的SQLite数据库文件。

        3.  如果在AutoCompleteTextView组件中输入两个及以上字母时显示以所输入字符串开头的所有单词列表?
解答:AutoCompleteTextView所使用的Adapter是一个自定义的Adapter类,类的结构如下:
view plaincopy to clipboardprint?
public class DictionaryAdapter extends CursorAdapter 

       ... ... 

        要注意的是,不能将整个数据库中的单词都查出,然后生成一个Adapter对象再使用setAdapter方法来设置AutoCompleteTextView组件的Adapter对象。AutoCompleteTextView组件不会为我们筛选以某个字符串开头的单词。这些工作需要开发人员通过编码来实现。解决这个问题的基本方法是在AutoCompleteTextView类的afterTextChanged事件中监视AutoCompleteTextView组件中字符的输入情况,每当输入一个字符时就生成一个Adapter对象,然后将新生成的Adapter对象与AutoCompleteTextView关联。

        在最后仍然要提一句的是在编写自定义DictionaryAdapter类时一定要覆盖contertToString方法,以便在用户选项某一个列表项时在AutoCompleteTextView组件中显示选中的单词,而不是Cursor对象地址。

作者介绍
        李宁:东北大学软件学院工程硕士。拥有超过10年的软件开发经验。曾任国内某知名企业项目经理。曾领导并参与开发了多个大中型项目。从2005年进入写作领域以来,为程序员、电脑编程技巧与维护、电脑报、IT168、天极网等平面媒体和网络媒体撰写了一百多篇原创技术和评论文章。并在个人 blog(http://nokiaguy.blogjava.net)上发表了大量的原创技术文章。2008年曾获得《电脑编程技巧与维护》优秀作者奖。曾独立编写了《Java Web开发技术大全》和《Java Web开发速学宝典》两本书。本人擅长Android、Ophone、Java ME、Java EE等技术。
(声明:本网的新闻及文章版权均属OPhone SDN网站所有,如需转载请与我们编辑团队联系。任何媒体、网站或个人未经本网书面协议授权,不得进行任何形式的转载。已经取得本网协议授权的媒体、网站,在转载使用时请注明稿件来源。)

你可能感兴趣的:(android)