接上一講。本節我們把詞典功能基本實現,首先上一個效果圖,我發現大家比較關心界面方面的東西,我之前寫的一個關於QQ界面的項目就很受歡迎,當然我現在能寫出遠比那個好看的界面。但是我想一個應用最核心的東西不完全在界面,一個好的創意,一個好的后台服務才是成功的關鍵,這也是我不斷學習的方向。
另外我發現在百度搜“悅詞背單詞91”搜索結果全是我在博客園的博文了。。。所以我把這個項目在91應用市場的下載地址粘上來吧,十分歡迎大家試用一下,有什么改進的意見請寫在下面的評論里!地址:http://apk.91.com/Soft/Android/com.carlos.yueci-4.html
今天我們就實現這個界面,一個能查詞的詞典。
為了明確思路,把上一節的項目功能分析拷貝過來:
功能分析:
功能1、查單詞。
實現方法:金山詞霸開放平台提供了一個開放API,通過Http訪問金山詞霸API提供的URL,可以獲得一個XML文檔,這個文檔就包含了要查詢的單詞的釋義、例句、音標、聲音的地址。通過解析該XML就可以獲得單詞的所有信息。
所用到的技術:
1)Http訪問網絡,並下載網絡文件
2)對SD卡進行操作,從SD卡(這里的SD卡是指手機默認的存儲卡,因為有些手機既能能插SD卡又有內部存儲,這里不涉及這個問題)中讀取文件,和把從網絡下載的文件存進SD卡。
3)解析XML文件
4)播放音樂(這個我后來封裝成了一個類,專門從網絡上查詢某個單詞,解析XML文件,並且將下載的Mp3文件存在SD卡中,然后播放該Mp3文件)
5)數據庫,這里涉及到第一個數據庫,每查找一個單詞之后,就會將該單詞和釋義存儲到一個SQLite數據庫中。這樣一來,下一次查找這個單詞時,先訪問數據庫,看看數據庫中有沒有這個單詞,若有,就不用訪問網絡了。
根據上面的分析,可見我們已經實現了Http網絡模塊,XML文件解析,還有三個內容,我們先實現這三個功能,然后再把UI搭建起來。
(一)后台功能實現
一、對SD進行操作。FileUtils類,一個專門用於實現講輸入流寫入文件,從文件中獲得輸入流的類,當我們從網上搜索一個單詞后,會把它對應的音頻Mp3存儲在本地的一個文件夾,這樣當我們再查這個單詞或者在背單詞時遇到這個單詞時,就不用再訪問網絡了。
package com.carlos.utils; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import android.os.Environment; public class FileUtils { private String SDPATH; public FileUtils(){ SDPATH=Environment.getExternalStorageDirectory()+"/"; //System.out.println(Environment.getExternalStorageDirectory().getAbsolutePath()); } //創建文件,一定不會返回空 /** * * @param path 直接創建文件即可,無需考慮文件夾有沒有創建,若文件已存在返回null * @param fileName * @return */ public File createSDFile(String path,String fileName){ File file=null; createSDDir(path); try{ file=new File(SDPATH+path+fileName); if(file.exists() && file.isFile()){ return null; } file.createNewFile(); //創建文件 }catch(Exception e){ e.printStackTrace(); } return file; } //創建目錄,如果存在同名文件夾則返回該文件夾,否則創建文件 public File createSDDir(String dirName){ File dir=new File(SDPATH+dirName); if(dir.exists() && dir.isDirectory()){ return dir; } dir.mkdirs(); //可創建多級文件夾 return dir; } //這里寫相對目錄 public ArrayList<String> listContentsOfFile(String path){ ArrayList<String> list=new ArrayList<String>(); File file=new File(SDPATH+path); File[] fileList=file.listFiles(); if(fileList==null) return list; for(int i=0; i<fileList.length;i++){ System.out.println(fileList[i].getName()); } return list; } //判斷SD卡文件夾是否存在 public boolean isFileExist(String path,String fileName){ File file=new File(SDPATH+path+fileName); return file.exists(); } //獲得文件輸入流 public InputStream getInputStreamFromFile(String path,String fileName){ InputStream input=null; File file=new File(SDPATH+path+fileName); if(file.exists()==false) return null; try { input=new FileInputStream(file); } catch (FileNotFoundException e) { // TODO Auto-generated catch bloc e.printStackTrace(); return null; } return input; } /** * * @param in * @param path 文件存儲的相對路徑 * @param fileName * @return */ public boolean saveInputStreamToFile(InputStream in, String path,String fileName ){ File file=createSDFile(path,fileName); //相對路徑即可 int length=0; if(file==null) return true; //其實這里的情況是文件已存在 byte[] buffer=new byte[1024]; FileOutputStream fOut=null; try { fOut=new FileOutputStream(file); while((length=in.read(buffer))!=-1){ //要利用read返回的實際成功讀取的字節數,將buffer寫入文件,否則將會出現錯誤的字節 fOut.write(buffer, 0, length); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); return false; }finally{ try { fOut.close(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); return false; } } return true; } public String getSDRootPath(){ return Environment.getExternalStorageDirectory().getAbsolutePath()+"/"; } public String getSDPATH() { return SDPATH; } public void setSDPATH(String sDPATH) { SDPATH = sDPATH; } }
各個函數的功能已經有說明,這個屬於一個工具類,可無需改動使用。
二、數據庫,用於存放所查的單詞的單詞、音標、釋義、例句
1、首先需創建一個DataBaseHelper類,繼承SQLiteOpenHelper類:
package com.carlos.database; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.database.sqlite.SQLiteOpenHelper; public class DataBaseHelperDict extends SQLiteOpenHelper{ public Context mContext=null; public String tableName=null; public static int VERSION=1; public DataBaseHelperDict(Context context, String name, CursorFactory factory, int version) { super(context, name, factory, version); // TODO Auto-generated constructor stub mContext=context; tableName=name; } public DataBaseHelperDict(Context context, String name, CursorFactory factory){ this(context,name,factory,VERSION); mContext=context; tableName=name; } public DataBaseHelperDict(Context context, String name){ this(context,name,null); mContext=context; tableName=name; }; @Override public void onCreate(SQLiteDatabase db) { // TODO Auto-generated method stub db.execSQL("create table dict(word text,pse text,prone text,psa text,prona text," + "interpret text, sentorig text, senttrans text)"); } @Override public void onUpgrade(SQLiteDatabase arg0, int arg1, int arg2) { // TODO Auto-generated method stub } }
這是一個非常簡單的類,在onCreate方法中我們需要用到類似JDBC的語言創建一個表table
public void onCreate(SQLiteDatabase db) { // TODO Auto-generated method stub db.execSQL("create table dict(word text,pse text,prone text,psa text,prona text," + "interpret text, sentorig text, senttrans text)"); }
其中word 單詞,pse 英式音標,prone 英式發音地址,psa 美式音標,prona 美式發音地址,interpret 翻譯, sentorig 利用英文, senttrans 例句中文
這8項是這個表的8列,用於存儲一個單詞的所有信息。
那么接下來的問題便是,有了這個數據庫我們怎么操作它呢?為此我封裝了一個詞典類Dict,這個類具有和詞典一樣的功能:在詞典中查找一個單詞,查找一個單詞的翻譯,或例句,從網絡上下載某個單詞的信息,將某個單詞添加到詞典中。一些細節的說明在代碼中有注釋
package com.carlos.wordcontainer; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URLEncoder; import java.util.ArrayList; import java.util.LinkedList; import org.xml.sax.InputSource; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.net.Uri; import com.carlos.database.DataBaseHelperDict; import com.carlos.internet.NetOperator; import com.carlos.text_parser.JinShanContentHandler; import com.carlos.text_parser.XMLParser; import com.carlos.utils.FileUtils; public class Dict { public Context context=null; public String tableName=null; private DataBaseHelperDict dbHelper=null; private SQLiteDatabase dbR=null,dbW=null; public Dict(Context context,String tableName){ this.context=context; this.tableName=tableName; dbHelper=new DataBaseHelperDict(context, tableName); //這里要用到前面的DataBaseHelper類,在Dict的構造方法中實例化該類,
//並且調用下面兩個方法獲得dbR和dbW,用於完成對數據庫的增刪改查操作。
//這里吧dbR dbW作為成員變量目的是避免反復實例化dbR dbW造成數據庫指針泄露。
dbR=dbHelper.getReadableDatabase(); dbW=dbHelper.getWritableDatabase(); } @Override protected void finalize() throws Throwable { //在該對象銷毀時,釋放dbR和dbW // TODO Auto-generated method stub dbR.close(); dbW.close(); dbHelper.close(); super.finalize(); }
//將包含單詞信息的WordValue對象添加進數據庫,這里使用了dbW的insert方法,需要創建一個ContentValue對象存放鍵值對 public void insertWordToDict(WordValue w, boolean isOverWrite){ if(w==null){ //避免空指針異常 return; } Cursor cursor=null; try{ ContentValues values=new ContentValues(); values.put("word",w.getWord() ); values.put("pse", w.getPsE()); values.put("prone",w.getPronE()); values.put("psa", w.getPsA()); values.put("prona", w.getPronA()); values.put("interpret",w.getInterpret()); values.put("sentorig", w.getSentOrig()); values.put("senttrans", w.getSentTrans()); cursor=dbR.query(tableName, new String[]{"word"}, "word=?", new String[]{w.getWord()}, null, null, null); if(cursor.getCount()>0){ if(isOverWrite==false)//首先看看數據庫中有沒有這個單詞,若詞典庫中已經有了這一個單詞,所以不再操作 return; else{ //執行更新操作 dbW.update(tableName, values, "word=?",new String[]{ w.getWord()}); } }else{ dbW.insert(tableName, null, values); //這里可能會發生空指針異常,到時候考慮 } }catch(Exception e){ }finally{ if(cursor!=null) cursor.close(); } }
//判斷數據庫中是否存在某個單詞 public boolean isWordExist(String word){ Cursor cursor=null; try{ cursor=dbR.query(tableName, new String[]{"word"}, "word=?", new String[]{word}, null, null, null); if(cursor.getCount()>0){ cursor.close(); return true; }else{ cursor.close(); return false; } }finally{ if(cursor!=null) cursor.close(); } }
//從單詞庫中獲得某個單詞的信息,如果詞庫中沒有改單詞,那么返回null public WordValue getWordFromDict(String searchedWord){ WordValue w=new WordValue();//預防空指針異常 // db.execSQL("create table dict(word text,pse text,prone text,psa text,prona text," + // "interpret text, sentorig text, senttrans text)"); String[] columns=new String[]{"word", "pse","prone","psa","prona","interpret","sentorig","senttrans"}; String[] strArray=new String[8]; Cursor cursor=dbR.query(tableName, columns, "word=?", new String[]{searchedWord}, null, null, null); while(cursor.moveToNext()){ for(int i=0;i<strArray.length;i++){ strArray[i]=cursor.getString(cursor.getColumnIndex(columns[i])); } w=new WordValue(strArray[0],strArray[1],strArray[2],strArray[3],strArray[4],strArray[5],strArray[6],strArray[7]); } cursor.close(); return w; }
//從網絡查找某個單詞,並且返回一個含有單詞信息的WordValue對象,這個方法在第二講的最后提過 public WordValue getWordFromInternet(String searchedWord){ WordValue wordValue=null; String tempWord=searchedWord; if(tempWord==null&& tempWord.equals("")) return null; char[] array=tempWord.toCharArray(); if(array[0]>256) //是中文,或其他語言的的簡略判斷 tempWord="_"+URLEncoder.encode(tempWord); InputStream in=null; String str=null; try{ String tempUrl=NetOperator.iCiBaURL1+tempWord+NetOperator.iCiBaURL2; in=NetOperator.getInputStreamByUrl(tempUrl); if(in!=null){ new FileUtils().saveInputStreamToFile(in, "", "gfdgf.txt"); XMLParser xmlParser=new XMLParser(); InputStreamReader reader=new InputStreamReader(in,"utf-8"); JinShanContentHandler contentHandler=new JinShanContentHandler(); xmlParser.parseJinShanXml(contentHandler, new InputSource(reader)); wordValue=contentHandler.getWordValue(); wordValue.setWord(searchedWord); } }catch(Exception e){ e.printStackTrace(); } return wordValue; }
//以下幾個方法都是獲得某個單詞的某一項信息,基本的思路還是先獲得全部信息WordValue然后調用WordValue的get方法獲得具體的信息。 //獲取發音文件地址 public String getPronEngUrl(String searchedWord){ Cursor cursor=dbR.query(tableName, new String[]{"prone"}, "word=?", new String[]{searchedWord}, null, null, null); if(cursor.moveToNext()==false){ cursor.close(); return null; } String str=cursor.getString(cursor.getColumnIndex("prone")); cursor.close(); return str; } public String getPronUSAUrl(String searchedWord){ Cursor cursor=dbR.query(tableName, new String[]{"prona"}, "word=?", new String[]{searchedWord}, null, null, null); if(cursor.moveToNext()==false){ cursor.close(); return null; } String str=cursor.getString(cursor.getColumnIndex("prona")); cursor.close(); return str; } //獲取音標 public String getPsEng(String searchedWord){ Cursor cursor=dbR.query(tableName, new String[]{"pse"}, "word=?", new String[]{searchedWord}, null, null, null); if(cursor.moveToNext()==false){ cursor.close(); return null; } String str=cursor.getString(cursor.getColumnIndex("pse")); cursor.close(); return str; } public String getPsUSA(String searchedWord){ Cursor cursor=dbR.query(tableName, new String[]{"psa"}, "word=?", new String[]{searchedWord}, null, null, null); if(cursor.moveToNext()==false){ cursor.close(); return null; } String str=cursor.getString(cursor.getColumnIndex("psa")); cursor.close(); return str; } /** * 若沒有句子那么返回的鏈表的長度為0,若單詞不存在那么直接返回null,所以最好對null和長度同時檢驗 * @param searchedWord * @return */ public String getInterpret(String searchedWord){ Cursor cursor=dbR.query(tableName, new String[]{"interpret"}, "word=?", new String[]{searchedWord}, null, null, null); if(cursor.moveToNext()==false){ cursor.close(); return null; } String str=cursor.getString(cursor.getColumnIndex("interpret")); cursor.close(); return str; } }
有了這個類以后大家可能會有一個比較明確的思路了,在接下來將UI時Activity中會實例化一個Dict對象,直接調用它的某些方法完成操作。
三、音頻播放
當我們查到一個單詞時,可能想聽一聽這個單詞的發音,這時就需要根據數據庫中prone/prona這一列的音頻地址,從網絡上獲得音頻輸入流下載到SD卡,然后調用MediaPlayer播放音樂,這是一個一系列的動作,再后面背單詞時因為要聽單詞的發音,會更多的用到這個操作,為此我封裝了一個方法,專門完成這組操作。
package com.carlos.music; import java.io.InputStream; import java.io.InputStreamReader; import org.xml.sax.InputSource; import com.carlos.internet.NetOperator; import com.carlos.text_parser.JinShanContentHandler; import com.carlos.text_parser.XMLParser; import com.carlos.utils.FileUtils; import com.carlos.wordcontainer.Dict; import com.carlos.wordcontainer.WordValue; import com.carlos.yueci.ReciteActivity; import android.content.Context; import android.media.MediaPlayer; import android.net.Uri; public class Mp3Player { public final static String MUSIC_ENG_RELATIVE_PATH="yueci/sounds/sounds_EN/"; public final static String MUSIC_USA_RELATIVE_PATH="yueci/sounds/sounds_US/"; public final static int ENGLISH_ACCENT=0; public final static int USA_ACCENT=1; public Context context=null; public String tableName=null; public MediaPlayer mediaPlayer=null; FileUtils fileU=null; Dict dict=null; public boolean isMusicPermitted=true; //用於對是否播放音樂進行保護性設置,當該變量為false時,可以阻止一次音樂播放 public Mp3Player(Context context,String tableName){ this.context=context; this.tableName=tableName; fileU=new FileUtils(); dict=new Dict(context,tableName); isMusicPermitted=true; } /** * 首先先看一下SD卡上有沒有,若有則播放,沒有執行下一步 * 看一下dict表中有沒有單詞的記錄,若有,看一下發音字段是不是有美式發音或英式發音,若無則退出 * 若沒有字段記錄,訪問網絡下載Mp3然后播放 * 一個Activity中一般只能有一個Voice成員變量,對應的也就只有一個MediaPlayer對象,這樣才能對播放 * 狀態進行有效控制 * 該方法原則上只能在線程中調用 * @param word * @param accent */ public void playMusicByWord(String word , int accent,boolean isAllowedToUseInternet, boolean isPlayRightNow){ if(word==null || word.length()<=0) return; char[] wordArray=word.toCharArray(); char initialCharacter=wordArray[0]; String path=null; String pronUrl=null; WordValue w=null; if(accent==ENGLISH_ACCENT){ path=MUSIC_ENG_RELATIVE_PATH; }else{ path=MUSIC_USA_RELATIVE_PATH; } if(fileU.isFileExist(path+initialCharacter+"/","-$-"+word+".mp3")==false){ if(isAllowedToUseInternet==false) return; //為了避免多次多個線程同時訪問網絡下載同一個文件,這里加了這么一個控制變量 if(dict.isWordExist(word)==false){ //數據庫中沒有單詞記錄,從網絡上進行同步 if((w=dict.getWordFromInternet(word))==null){ return; } dict.insertWordToDict(w, true); }//能走到這一步說明從網上同步成功,數據庫中一定存在單詞記錄 if(accent==ENGLISH_ACCENT){ pronUrl=dict.getPronEngUrl(word); }else{ pronUrl=dict.getPronUSAUrl(word); } if(pronUrl==null ||pronUrl=="null"||pronUrl.length()<=0) return; //這說明網絡上也沒有對應發音,故退出 //得到了Mp3地址后下載到文件夾中然后進行播放 InputStream in=null; in = NetOperator.getInputStreamByUrl(pronUrl); if(in==null) return; if(fileU.saveInputStreamToFile(in, path+initialCharacter+"/","-$-"+word+".mp3")==false) return; } //走到這里說明文件夾里一定有響應的音樂文件,故在這里播放 if(isPlayRightNow==false) return; /** * 這個方法存在缺點,可能因為同時new 了多個MediaPlayer對象,導致start方法失效, * 因此解決的方法是,使用同一個MediaPlayer對象,若一次播放時發現對象非空,那么先 * 調用release()方法釋放資源,再重新create */ if(isMusicPermitted==false){ return; } try{ if(mediaPlayer!=null){ if(mediaPlayer.isPlaying()) mediaPlayer.stop(); mediaPlayer.release(); mediaPlayer=null; //為了防止mediaPlayer多次調用stop release,這里置空還是有必要 } mediaPlayer=MediaPlayer.create(context, Uri.parse("file://"+fileU.getSDRootPath() +path+initialCharacter+"/-$-"+word+".mp3")); mediaPlayer.start(); }catch(Exception e){ mediaPlayer.release(); e.printStackTrace(); } } }
public void playMusicByWord(String word , int accent,boolean isAllowedToUseInternet, boolean isPlayRightNow){}
這個方法的基本邏輯:
根據接受的單詞word,先看“詞典”(數據庫)中有沒有該單詞記錄,若沒有,先調用dict的getWordFromInternet()獲取單詞信息存儲進數據庫“詞典”中,然后根據對應的口音accent,獲得prone或prona的地址,然后訪問網絡,講Mp3下載到本地,然后調用MediaPlayer對象播放音樂。
需要注意的幾點:
形參中isAllowedToUseInternet 是用於控制是否允許訪問網絡,添加這個控制項是因為,我們在播放音樂時有時候不想讓該方法訪問網絡,因為過多的縣城訪問網絡可能會造成同時下載同一個音頻,會造成線程阻塞或者下載的文件數據損壞。
isPlayeRightNow 是用於控制是否立即播放該MP3,因為在之后實現背單詞功能時有時只需要把MP3文件下載下來,而並不需要立刻播放,例如背單詞時給你中文意思和英文選項,此時就不能立即播放音樂,否則就知道選項是什么了。
注意到還有一個isMusicPermitted 參量這是個靜態成員變量,這個參量是用來阻止一次播放的,比如我們背單詞時,突然不想背了退出,此時可能單詞的音頻正好要播放,這時需要把isMusicPermitted置為false,否則會出現退出后仍播放單詞發音的現象。
OK,添加了上述三個支持功能我們終於可以進行最后的進擊————搭建UI界面了。
這里插一句題外話,雖然在寫教程時我是先實現了上述五個功能然后再搭的界面,但實際開發時這兩個工作是同時進行的,我喜歡先把簡單的UI搭出來,然后再慢慢實現各個按鈕的功能,遇到需要的后台支持再實現它,很難說這兩種方法哪個效率高些,我覺得還是先搭一個粗略的框架,然后實現后台功能,然后再回過頭來完善UI和后台功能,這其中要經過n次調試。 我這里之所以先給出了完整的后台功能是因為應用已經寫完了,我想讓整個開發過程的思路更清晰一些。
(二)UI搭建
一、布局文件:先給出一個總的布局文件然后我根據圖片分析。
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#E5E6E0" > <RelativeLayout android:id="@+id/edit_text_dict_search_bar" android:layout_width="match_parent" android:layout_height="42dp" android:layout_alignParentTop="true" android:background="#2C2C2C" android:focusable="true" android:focusableInTouchMode="true" > <ImageButton android:id="@+id/image_btn_dict_back_to_general" android:layout_width="50dp" android:layout_height="match_parent" android:layout_marginTop="1dp" android:layout_marginBottom="1dp" android:layout_marginLeft="1dp" android:layout_alignParentLeft="true" android:layout_weight="5" android:background="@android:color/transparent" android:src="@drawable/btn_back_blue"/> <ImageButton android:id="@+id/image_btn_dict_search" android:layout_width="50dp" android:layout_height="match_parent" android:layout_alignParentRight="true" android:background="@android:color/transparent" android:src="@android:drawable/ic_menu_search" /> <EditText android:paddingLeft="10dp" android:id="@+id/edit_text_dict" android:layout_toRightOf="@id/image_btn_dict_back_to_general" android:layout_toLeftOf="@id/image_btn_dict_search" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="1" android:background="@drawable/edit_back" android:inputType="text" android:maxLines="1" android:gravity="center_vertical" android:hint="請在此輸入單詞" android:textColor="#FCFDFE" android:imeOptions="actionSearch" /> <ImageButton android:id="@+id/image_btn_dict_delete_all" android:layout_width="35dp" android:layout_height="match_parent" android:layout_marginTop="5dp" android:layout_marginBottom="5dp" android:layout_toLeftOf="@id/image_btn_dict_search" android:scaleType="fitXY" android:src="@drawable/delete_all" android:background="@android:color/transparent"/> </RelativeLayout> <TextView android:id="@+id/text_dict_word" android:layout_width="match_parent" android:layout_height="40dp" android:text="fabulous" android:gravity="bottom" android:textColor="#3B3C3D" android:textStyle="bold" android:textSize="24sp" android:layout_below="@id/edit_text_dict_search_bar" android:layout_marginTop="36dp" android:layout_marginLeft="22dp" android:layout_marginRight="22dp" android:layout_marginBottom="3dp"/> <ImageButton android:id="@+id/image_btn_dict_add_to_wordlist" android:layout_height="30dp" android:layout_marginTop="10dp" android:layout_marginRight="30dp" android:layout_width="30dp" android:layout_alignParentRight="true" android:layout_alignTop="@id/text_dict_word" android:src="@drawable/add_danciben" android:background="@android:color/transparent" android:scaleType="fitXY"/> <RelativeLayout android:id="@+id/phonetic_bar" android:layout_width="match_parent" android:layout_marginLeft="22dp" android:layout_height="40dp" android:layout_below="@id/text_dict_word"> <ImageButton android:id="@+id/image_btn_dict_horn_accent_eng" android:layout_height="match_parent" android:layout_marginTop="7dp" android:layout_marginBottom="7dp" android:layout_width="30dp" android:background="@android:color/transparent" android:src="@drawable/horn_dict" android:scaleType="fitCenter" /> <TextView android:id="@+id/text_dict_phosymbol_eng" android:layout_height="match_parent" android:layout_width="wrap_content" android:text="英[fanted]" android:gravity="center_vertical" android:textColor="#6C6C6C" android:textSize="14sp" android:layout_toRightOf="@id/image_btn_dict_horn_accent_eng"/> <TextView android:id="@+id/text_dict_phosymbol_divider" android:layout_height="match_parent" android:layout_width="50dp" android:text="/" android:gravity="center" android:textColor="#6C6C6C" android:textSize="14sp" android:layout_toRightOf="@id/text_dict_phosymbol_eng"/> <ImageButton android:id="@+id/image_btn_dict_horn_accent_usa" android:layout_height="match_parent" android:layout_marginTop="7dp" android:layout_marginBottom="7dp" android:layout_width="30dp" android:background="@android:color/transparent" android:src="@drawable/horn_dict" android:scaleType="fitCenter" android:layout_toRightOf="@id/text_dict_phosymbol_divider" /> <TextView android:id="@+id/text_dict_phosymbol_usa" android:layout_height="match_parent" android:layout_width="wrap_content" android:text="美[fan'ted]" android:gravity="center_vertical" android:textColor="#6C6C6C" android:textSize="14sp" android:layout_toRightOf="@id/image_btn_dict_horn_accent_usa"/> </RelativeLayout> <LinearLayout android:id="@+id/dict_interpret_divider" android:layout_height="20dp" android:layout_width="match_parent" android:layout_marginLeft="10dp" android:layout_below="@id/phonetic_bar" android:orientation="horizontal"> <ImageView android:layout_width="30dp" android:layout_height="match_parent" android:src="@drawable/right_cursor"/> <TextView android:layout_width="wrap_content" android:layout_height="match_parent" android:text="基本釋義" android:textSize="14sp" android:textColor="#00A2DC" android:gravity="center_vertical"/> </LinearLayout> <TextView android:id="@+id/text_dict_interpret" android:layout_below="@id/dict_interpret_divider" android:layout_width="match_parent" android:layout_margin="10dp" android:layout_height="80dp" android:background="@drawable/layer_list_dict_item_back" android:padding="12dp" android:text="不切實際的\nsrge了然於心fsfg" android:textSize="14sp" android:textColor="#3C3C3C" android:lineSpacingMultiplier="1.2"/> <LinearLayout android:id="@+id/dict_sentence_divider" android:layout_height="20dp" android:layout_width="match_parent" android:layout_marginLeft="10dp" android:layout_below="@id/text_dict_interpret" android:orientation="horizontal"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="2" > <ImageView android:layout_width="30dp" android:layout_height="match_parent" android:src="@drawable/right_cursor"/> <TextView android:layout_width="wrap_content" android:layout_height="match_parent" android:text="例句" android:textSize="14sp" android:textColor="#00A2DC" android:gravity="center_vertical"/> </LinearLayout> <RelativeLayout android:layout_height="match_parent" android:layout_width="match_parent" android:layout_weight="1"> <ImageView android:id="@+id/dict_jinshan_ico" android:layout_height="match_parent" android:layout_width="30dp" android:layout_alignParentRight="true" android:layout_marginRight="15dp" android:src="@drawable/jinshan_ico"/> <TextView android:layout_height="match_parent" android:layout_width="wrap_content" android:gravity="bottom" android:layout_toLeftOf="@id/dict_jinshan_ico" android:text="supported by iCIBA" android:textColor="#6C6C6C" android:textSize="10sp"/> </RelativeLayout> </LinearLayout> <ListView android:id="@+id/listview_dict_sentence" android:layout_below="@id/dict_sentence_divider" android:layout_width="match_parent" android:layout_marginTop="10dp" android:layout_marginLeft="10dp" android:layout_marginRight="10dp" android:padding="12dp" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:background="@drawable/layer_list_dict_item_back" android:divider="#8C8C8C" android:dividerHeight="0px" > </ListView> </RelativeLayout>
是不是有點暈呢?我把布局分成幾塊就懂了。
Part One:
這是頂部的橫條,這是一個RelativeLayout,里面有以后Back 按鈕, 搜索按鈕, 刪除EditText內容的按鈕,還有一個EditText,對應的代碼如下:
<RelativeLayout android:id="@+id/edit_text_dict_search_bar" android:layout_width="match_parent" android:layout_height="42dp" android:layout_alignParentTop="true" android:background="#2C2C2C" android:focusable="true" android:focusableInTouchMode="true" > <ImageButton android:id="@+id/image_btn_dict_back_to_general" android:layout_width="50dp" android:layout_height="match_parent" android:layout_marginTop="1dp" android:layout_marginBottom="1dp" android:layout_marginLeft="1dp" android:layout_alignParentLeft="true" android:layout_weight="5" android:background="@android:color/transparent" android:src="@drawable/btn_back_blue"/> <ImageButton android:id="@+id/image_btn_dict_search" android:layout_width="50dp" android:layout_height="match_parent" android:layout_alignParentRight="true" android:background="@android:color/transparent" android:src="@android:drawable/ic_menu_search" /> <EditText android:paddingLeft="10dp" android:id="@+id/edit_text_dict" android:layout_toRightOf="@id/image_btn_dict_back_to_general" android:layout_toLeftOf="@id/image_btn_dict_search" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="1" android:background="@drawable/edit_back" android:inputType="text" android:maxLines="1" android:gravity="center_vertical" android:hint="請在此輸入單詞" android:textColor="#FCFDFE" android:imeOptions="actionSearch" /> <ImageButton android:id="@+id/image_btn_dict_delete_all" android:layout_width="35dp" android:layout_height="match_parent" android:layout_marginTop="5dp" android:layout_marginBottom="5dp" android:layout_toLeftOf="@id/image_btn_dict_search" android:scaleType="fitXY" android:src="@drawable/delete_all" android:background="@android:color/transparent"/> </RelativeLayout>
這一塊就是TextView ImageButton(加入單詞本)進行的一個Relative布局,沒什么特別之處:布局代碼如下
<TextView android:id="@+id/text_dict_word" android:layout_width="match_parent" android:layout_height="40dp" android:text="fabulous" android:gravity="bottom" android:textColor="#3B3C3D" android:textStyle="bold" android:textSize="24sp" android:layout_below="@id/edit_text_dict_search_bar" android:layout_marginTop="36dp" android:layout_marginLeft="22dp" android:layout_marginRight="22dp" android:layout_marginBottom="3dp"/> <ImageButton android:id="@+id/image_btn_dict_add_to_wordlist" android:layout_height="30dp" android:layout_marginTop="10dp" android:layout_marginRight="30dp" android:layout_width="30dp" android:layout_alignParentRight="true" android:layout_alignTop="@id/text_dict_word" android:src="@drawable/add_danciben" android:background="@android:color/transparent" android:scaleType="fitXY"/> <RelativeLayout android:id="@+id/phonetic_bar" android:layout_width="match_parent" android:layout_marginLeft="22dp" android:layout_height="40dp" android:layout_below="@id/text_dict_word"> <ImageButton android:id="@+id/image_btn_dict_horn_accent_eng" android:layout_height="match_parent" android:layout_marginTop="7dp" android:layout_marginBottom="7dp" android:layout_width="30dp" android:background="@android:color/transparent" android:src="@drawable/horn_dict" android:scaleType="fitCenter" /> <TextView android:id="@+id/text_dict_phosymbol_eng" android:layout_height="match_parent" android:layout_width="wrap_content" android:text="英[fanted]" android:gravity="center_vertical" android:textColor="#6C6C6C" android:textSize="14sp" android:layout_toRightOf="@id/image_btn_dict_horn_accent_eng"/> <TextView android:id="@+id/text_dict_phosymbol_divider" android:layout_height="match_parent" android:layout_width="50dp" android:text="/" android:gravity="center" android:textColor="#6C6C6C" android:textSize="14sp" android:layout_toRightOf="@id/text_dict_phosymbol_eng"/> <ImageButton android:id="@+id/image_btn_dict_horn_accent_usa" android:layout_height="match_parent" android:layout_marginTop="7dp" android:layout_marginBottom="7dp" android:layout_width="30dp" android:background="@android:color/transparent" android:src="@drawable/horn_dict" android:scaleType="fitCenter" android:layout_toRightOf="@id/text_dict_phosymbol_divider" /> <TextView android:id="@+id/text_dict_phosymbol_usa" android:layout_height="match_parent" android:layout_width="wrap_content" android:text="美[fan'ted]" android:gravity="center_vertical" android:textColor="#6C6C6C" android:textSize="14sp" android:layout_toRightOf="@id/image_btn_dict_horn_accent_usa"/> </RelativeLayout>
這是兩個TextView ,釋義的TextView使用了一個LayerList做背景,布局代碼:
<LinearLayout android:id="@+id/dict_interpret_divider" android:layout_height="20dp" android:layout_width="match_parent" android:layout_marginLeft="10dp" android:layout_below="@id/phonetic_bar" android:orientation="horizontal"> <ImageView android:layout_width="30dp" android:layout_height="match_parent" android:src="@drawable/right_cursor"/> <TextView android:layout_width="wrap_content" android:layout_height="match_parent" android:text="基本釋義" android:textSize="14sp" android:textColor="#00A2DC" android:gravity="center_vertical"/> </LinearLayout> <TextView android:id="@+id/text_dict_interpret" android:layout_below="@id/dict_interpret_divider" android:layout_width="match_parent" android:layout_margin="10dp" android:layout_height="80dp" android:background="@drawable/layer_list_dict_item_back" android:padding="12dp" android:text="不切實際的\nsrge了然於心fsfg" android:textSize="14sp" android:textColor="#3C3C3C" android:lineSpacingMultiplier="1.2"/>
layer_list_dict_item_back layerList位於drawable文件夾中,可搜索layerlist的使用方法學習
<?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android" > <item > <shape > <corners android:radius="4dp"/> <solid android:color="#D0D0D0"/> </shape> </item> <item android:left="0.8dp" android:top="0dp" android:right="2dp" android:bottom="2dp"> <shape > <corners android:radius="4dp"/> <solid android:color="#FEFEFE"/> </shape> </item> </layer-list>
這是一個ListView,它也采用了layerlist作背景。
<LinearLayout android:id="@+id/dict_sentence_divider" android:layout_height="20dp" android:layout_width="match_parent" android:layout_marginLeft="10dp" android:layout_below="@id/text_dict_interpret" android:orientation="horizontal"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="2" > <ImageView android:layout_width="30dp" android:layout_height="match_parent" android:src="@drawable/right_cursor"/> <TextView android:layout_width="wrap_content" android:layout_height="match_parent" android:text="例句" android:textSize="14sp" android:textColor="#00A2DC" android:gravity="center_vertical"/> </LinearLayout> <RelativeLayout android:layout_height="match_parent" android:layout_width="match_parent" android:layout_weight="1"> <ImageView android:id="@+id/dict_jinshan_ico" android:layout_height="match_parent" android:layout_width="30dp" android:layout_alignParentRight="true" android:layout_marginRight="15dp" android:src="@drawable/jinshan_ico"/> <TextView android:layout_height="match_parent" android:layout_width="wrap_content" android:gravity="bottom" android:layout_toLeftOf="@id/dict_jinshan_ico" android:text="supported by iCIBA" android:textColor="#6C6C6C" android:textSize="10sp"/> </RelativeLayout> </LinearLayout> <ListView android:id="@+id/listview_dict_sentence" android:layout_below="@id/dict_sentence_divider" android:layout_width="match_parent" android:layout_marginTop="10dp" android:layout_marginLeft="10dp" android:layout_marginRight="10dp" android:padding="12dp" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:background="@drawable/layer_list_dict_item_back" android:divider="#8C8C8C" android:dividerHeight="0px" > </ListView>
設計好布局文件后,我們很容易理解各個控件的作用,接下來我們就要寫Activity子類,用於實例化各個控件,設置監聽器,實現各個按鈕的功能。
在寫Activity之前,首先就成員變量的命名做一個說明,因為個Activity中的控件很多,如果隨便命名很容易混亂,這里我采用了一種根據屬性和類命名的方法,大家可以參考:
比如 上面有一個“放大鏡”的搜索按鈕,這是一個ImageButton 同時它是屬於DictActivity的,它的功能是開始查單詞,那么就將這個ImageButton對象命名為:
ImageButton imageBtnDictSearch=(ImageButton)findViewById(R.id.image_btn_dict_search);
在設置布局文件后把id也設置成相同的名字,這樣就不會搞混了,根據我的經驗這種命名方式非常高效。
下面就給出DictActivity類:
package com.carlos.yueci; import java.security.KeyStore; import java.util.ArrayList; import java.util.Currency; import java.util.HashMap; import com.carlos.adapter.DictSentenceListAdapter; import com.carlos.database.DataBaseHelper; import com.carlos.music.Mp3Player; import com.carlos.yueci.R; import com.carlos.wordcontainer.Dict; import com.carlos.wordcontainer.WordValue; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.inputmethodservice.Keyboard.Key; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.text.method.ScrollingMovementMethod; import android.view.KeyCharacterMap.KeyData; import android.view.KeyEvent; import android.view.View; import android.view.Window; import android.view.View.OnClickListener; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; import android.widget.Button; import android.widget.EditText; import android.widget.ImageButton; import android.widget.ListView; import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; import android.widget.TextView.OnEditorActionListener; public class DictActivity extends Activity{ public TextView textDictWord=null; public TextView textDictPhonSymbolEng=null; public TextView textDictPhonSymbolUSA=null; public TextView textDictInterpret=null; public ListView listViewDictSentence=null; public ImageButton imageBtnDictAddToWordList=null; public ImageButton imageBtnDictHornEng=null; public ImageButton imageBtnDictHornUSA=null; public ImageButton imageBtnDictSerach=null; public ImageButton imageBtnDictBackToGeneral=null; public ImageButton imageBtnDictDelteEditText=null; public Button buttonDictDialogConfirm=null; public Button buttonDictDialogCancel=null; public EditText editTextDictSearch=null; public Dict dict=null; public WordValue w=null; public DataBaseHelper dbGlossaryHelper=null; public SQLiteDatabase dbGlossaryR=null; public SQLiteDatabase dbGlossaryW=null; public Mp3Player mp3Box=null; public static String searchedWord=null; public Handler dictHandler=null; public void initial(){ textDictWord=(TextView) findViewById(R.id.text_dict_word); textDictInterpret=(TextView)findViewById(R.id.text_dict_interpret); textDictPhonSymbolEng=(TextView)findViewById(R.id.text_dict_phosymbol_eng); textDictPhonSymbolUSA=(TextView)findViewById(R.id.text_dict_phosymbol_usa); listViewDictSentence=(ListView)findViewById(R.id.listview_dict_sentence); imageBtnDictAddToWordList=(ImageButton)findViewById(R.id.image_btn_dict_add_to_wordlist); imageBtnDictBackToGeneral=(ImageButton)findViewById(R.id.image_btn_dict_back_to_general); imageBtnDictHornEng=(ImageButton)findViewById(R.id.image_btn_dict_horn_accent_eng); imageBtnDictHornUSA=(ImageButton)findViewById(R.id.image_btn_dict_horn_accent_usa); imageBtnDictSerach=(ImageButton)findViewById(R.id.image_btn_dict_search); imageBtnDictDelteEditText=(ImageButton)findViewById(R.id.image_btn_dict_delete_all); editTextDictSearch=(EditText)findViewById(R.id.edit_text_dict); editTextDictSearch.setOnEditorActionListener(new EditTextDictEditActionLis()); dict=new Dict(DictActivity.this, "dict"); mp3Box=new Mp3Player(DictActivity.this, "dict"); dbGlossaryHelper=new DataBaseHelper(DictActivity.this, "glossary"); dbGlossaryR=dbGlossaryHelper.getReadableDatabase(); dbGlossaryW=dbGlossaryHelper.getWritableDatabase(); dictHandler=new Handler(Looper.getMainLooper()); //對searchedWord進行初始化 Intent intent=this.getIntent(); searchedWord=intent.getStringExtra("word"); if(searchedWord==null) searchedWord=""; //設置查找的文本 textDictWord.setText(searchedWord); } /** * 該方法可能需要訪問網絡,因此放在線程里進行 * @param word */ public void searchWord(String word){ //調用該方法后首先初始化界面 dictHandler.post(new RunnableDictSetTextInterface(searchedWord, "", "", "", null, null)); w=null; //對w進行初始化 if(dict.isWordExist(word)==false){ //數據庫中沒有單詞記錄,從網絡上進行同步 if((w=dict.getWordFromInternet(word))==null ||w.getWord().equals("")){ return; } //錯詞不添加進詞典 dict.insertWordToDict(w, true); //默認添加到詞典中 }//能走到這一步說明從網上同步成功,數據庫中一定存在單詞記錄 w=dict.getWordFromDict(word); if(w==null){ //這里又進一步做了保護,若詞典中還是沒有,那么用空字符串代替 w=new WordValue(); } String searchStr=w.getWord(); String phoSymEng=w.getPsE(); String phoSymUSA=w.getPsA(); String interpret=w.getInterpret(); ArrayList<String> sentList=w.getOrigList(); //一定不會是null ArrayList<String> sentInChineseList=w.getTransList(); dictHandler.post(new RunnableDictSetTextInterface(searchedWord, phoSymEng, phoSymUSA, interpret, sentList, sentInChineseList)); if(phoSymEng.equals("")==false && phoSymUSA.equals("")==false){ //只有有音標時才去下載音樂 mp3Box.playMusicByWord(searchedWord, Mp3Player.ENGLISH_ACCENT, true, false); mp3Box.playMusicByWord(searchedWord, Mp3Player.USA_ACCENT, true, false); } } public void setOnClickLis(){ imageBtnDictBackToGeneral.setOnClickListener(new IBDictBackToGeneralClickLis() ); imageBtnDictHornEng.setOnClickListener(new IBDictPlayMusicByAccentClickLis(Mp3Player.ENGLISH_ACCENT)); imageBtnDictHornUSA.setOnClickListener(new IBDictPlayMusicByAccentClickLis(Mp3Player.USA_ACCENT)); imageBtnDictAddToWordList.setOnClickListener(new IBDictAddWordToGlossaryClickLis()); imageBtnDictDelteEditText.setOnClickListener(new IBDictDeleteEditTextClickLis()); imageBtnDictSerach.setOnClickListener(new IBDictSearchClickLis()); } public void showAddDialog(){ if(searchedWord==null) return; AlertDialog dialog=new AlertDialog.Builder(DictActivity.this).create(); dialog.show(); Window window=dialog.getWindow(); window.setContentView(R.layout.dialog_if_layout); buttonDictDialogConfirm=(Button)window.findViewById(R.id.dialog_confirm); buttonDictDialogCancel=(Button)window.findViewById(R.id.dialog_cancel); buttonDictDialogConfirm.setOnClickListener(new BDictDialogConfirmClickLis(dialog)); buttonDictDialogCancel.setOnClickListener(new BDictDialogCancelClickLis(dialog)); TextView dialogText=(TextView)window.findViewById(R.id.dialog_text); dialogText.setText("把"+searchedWord+"添加到單詞本?"); } public void insertWordToGlossary(){ if(w==null || w.getInterpret().equals("")){ Toast.makeText(DictActivity.this, "單詞格式錯誤", Toast.LENGTH_SHORT).show(); return; //若是不是有效單詞,那么將不能添加到單詞本 } boolean isSuccess=dbGlossaryHelper.insertWordInfoToDataBase(searchedWord, w.getInterpret(), false); if(isSuccess){ Toast.makeText(DictActivity.this, "添加成功", Toast.LENGTH_SHORT).show(); }else{ Toast.makeText(DictActivity.this, "單詞已存在", Toast.LENGTH_SHORT).show(); } } @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); setContentView(R.layout.activity_dict); initial(); setOnClickLis(); new ThreadDictSearchWordAndSetInterface().start(); } @Override protected void onStart() { // TODO Auto-generated method stub super.onStart(); mp3Box.isMusicPermitted=true; } @Override protected void onPause() { // TODO Auto-generated method stub mp3Box.isMusicPermitted=false; super.onPause(); } public class ThreadDictSearchWordAndSetInterface extends Thread{ @Override public void run() { // TODO Auto-generated method stub super.run(); searchWord(searchedWord); } } public class RunnableDictSetTextInterface implements Runnable{ String searchStr=null; String phoSymEng=null; String phoSymUSA=null; String interpret=null; ArrayList<String> sentList=null; ArrayList<String> sentInChineseList=null; public RunnableDictSetTextInterface(String searchStr, String phoSymEng, String phoSymUSA, String interpret, ArrayList<String> sentList, ArrayList<String> sentInChineseList) { super(); this.searchStr = searchStr; this.phoSymEng = "英["+phoSymEng+"]"; this.phoSymUSA = "美["+phoSymUSA+"]"; this.interpret = interpret; this.sentList = sentList; this.sentInChineseList = sentInChineseList; } @Override public void run() { // TODO Auto-generated method stub textDictWord.setText(searchStr); textDictPhonSymbolEng.setText(phoSymEng); textDictPhonSymbolUSA.setText(phoSymUSA); textDictInterpret.setText(interpret); if(sentList==null ||sentInChineseList==null){ //對鏈表為空進行防護 // textDictSentence.setText(""); return; } int count=0; if(sentList.size()<=sentInChineseList.size()){ count=sentList.size(); }else{ count=sentInChineseList.size(); //保護機制,這里取兩者長度最小值,但一般二者長度相等 } ArrayList<HashMap<String,Object>> list=new ArrayList<HashMap<String,Object>>(); for(int i=0; i<count;i++){ // sb.append(sentList.get(i)+"\n"+sentInChineseList.get(i)+"\n\n"); HashMap<String,Object> map=new HashMap<String, Object>(); map.put("sentence", sentList.get(i)+"\n"+sentInChineseList.get(i)); list.add(map); } // textDictSentence.setText(sb.toString()); DictSentenceListAdapter adapter=new DictSentenceListAdapter(DictActivity.this, R.layout.dict_sentence_list_item, list, new String[]{"sentence"}, new int[]{R.id.text_dict_sentence_list_item}); listViewDictSentence.setAdapter(adapter); } } class IBDictBackToGeneralClickLis implements OnClickListener{ @Override public void onClick(View arg0) { // TODO Auto-generated method stub DictActivity.this.finish(); } } class IBDictPlayMusicByAccentClickLis implements OnClickListener{ public int accentTemp=0; public IBDictPlayMusicByAccentClickLis(int accentTemp) { super(); this.accentTemp = accentTemp; } @Override public void onClick(View arg0) { // TODO Auto-generated method stub mp3Box.playMusicByWord(searchedWord, accentTemp, false, true); } } class IBDictAddWordToGlossaryClickLis implements OnClickListener{ @Override public void onClick(View arg0) { // TODO Auto-generated method stub showAddDialog(); } } class IBDictDeleteEditTextClickLis implements OnClickListener{ @Override public void onClick(View arg0) { // TODO Auto-generated method stub editTextDictSearch.setText(""); } } class BDictDialogConfirmClickLis implements OnClickListener{ AlertDialog dialog=null; public BDictDialogConfirmClickLis(AlertDialog dialog){ this.dialog=dialog; } @Override public void onClick(View arg0) { // TODO Auto-generated method stub insertWordToGlossary(); dialog.cancel(); } } class BDictDialogCancelClickLis implements OnClickListener{ AlertDialog dialog=null; public BDictDialogCancelClickLis(AlertDialog dialog){ this.dialog=dialog; } @Override public void onClick(View arg0) { // TODO Auto-generated method stub dialog.cancel(); } } class EditTextDictEditActionLis implements OnEditorActionListener{ @Override public boolean onEditorAction(TextView arg0, int arg1, KeyEvent arg2) { // TODO Auto-generated method stub if(arg1==EditorInfo.IME_ACTION_SEARCH || arg2!=null&&arg2.getKeyCode()==KeyEvent.KEYCODE_ENTER){ startSearch(); return true; } return false; } } class IBDictSearchClickLis implements OnClickListener{ @Override public void onClick(View arg0) { // TODO Auto-generated method stub startSearch(); } } public void startSearch(){ String str=editTextDictSearch.getText().toString(); if(str==null || str.equals("")) return; searchedWord=str; new ThreadDictSearchWordAndSetInterface().start(); InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(editTextDictSearch.getWindowToken(), 0); } }
我把所有的findViewById全部放在了initial()方法中,方便調用;這個Activity類的主要目的就是給各個按鈕設置監聽器,這里對監聽器類的命名我也做了一個統一的規定:
還是以“搜索”按鈕為例:搜索按鈕是一個ImageButton,是屬於DictActivity類,是實現搜索功能,需要設置一個ClickListener,那么我們創建的一個OnClickListener實現類的名字就叫: class IBDictSearchClickLis implements OnCLickListner{ };
這里還涉及到使用AlertDialog ,關於使用方法可以百度之,比較簡單,其余部分都是利用我前面介紹的5個后台類進行對單詞的操作,各個函數的功能都有注釋。
另外要注意在子線程中是不能操作UI的,這里需要DictActivity實例化一個Handler對象,通過Runnable向UI發送信息,不推薦用Message
這里單詞的例句使用到了ListView,關於ListView 可以參考我去年寫的兩篇關於ListView的教程:
http://www.cnblogs.com/carlos-vic/p/Carlos_V_Android_5.html
http://www.cnblogs.com/carlos-vic/p/Carlos_V_Android_6.html
這里進把基本ListView所需要的ListItem的布局文件和適配器BaseAdapter貼上來,最最基本的一個ListView:
listItem布局文件:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:paddingTop="8dp" android:paddingBottom="8dp" android:paddingLeft="3dp" android:paddingRight="3dp" android:background="@drawable/list_item_sentence_back" > <TextView android:id="@+id/text_dict_sentence_list_item" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_vertical" android:textColor="#3C3C3C" android:textSize="14sp"/> </LinearLayout>
適配器類:
package com.carlos.adapter; import java.util.ArrayList; import java.util.HashMap; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.TextView; public class DictSentenceListAdapter extends BaseAdapter{ private Context context=null; private int resources; private ArrayList<HashMap<String,Object>> list=null; private String[] from; private int[] to; /** * 這里仿照的是SimpleAdapter的形參列表 * @param context * @param Resources * @param list * @param from * @param to */ public DictSentenceListAdapter(Context context, int resources, ArrayList<HashMap<String, Object>> list, String[] from, int[] to) { super(); this.context = context; this.resources = resources; this.list = list; this.from = from; this.to = to; } @Override public int getCount() { // TODO Auto-generated method stub return list.size(); } @Override public Object getItem(int arg0) { // TODO Auto-generated method stub return null; } @Override public long getItemId(int arg0) { // TODO Auto-generated method stub return arg0; } @Override public View getView(int position, View contentView, ViewGroup arg2) { // TODO Auto-generated method stub LayoutInflater inflater=LayoutInflater.from(context); contentView=inflater.inflate(resources, null); TextView text=(TextView)contentView.findViewById(to[0]); text.setText((String)(list.get(position).get(from[0]))); return contentView; } }
這樣,“悅詞-i背單詞”應用的查單詞功能就完全實現了,另外這里的布局文件中用到了很多背景資源,
等所有UI搭建完成后我會把資源文件上傳上來,今天的教程就到這里了~