Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發 >> 開發入門 >> Android記事本應用程序開發【教程:練習2】

Android記事本應用程序開發【教程:練習2】

編輯:開發入門

在本練習中,你將學習如何添加第二個Activity至你的記事本應用程序中,該Activity可以讓用戶創建、編輯以及刪除便箋。這個Activity假定通過用戶的輸入響應創建新便箋,並將其自身打包至一個由intent提供的Bundle返回值中。這個練習的目標是: ·構建一個新Activity並將其添加至android manifest中 ·通過異步調用方法startActivityForResult()來調用另一Activity ·用諸多Bundle對象在Activity之間傳遞數據 ·如何使用一個更高級的屏幕布局 第一步 用NotepadCodeLab文件夾下Notepadv2目錄裡的資源創建一個新Android 工程,正如與練習一中做過類似的一樣。如果你看到一個有關androidManifest.XML,或有關android.zip文件的錯誤信息,請右擊工程選擇android Tools > Fix Project PropertIEs,即可修正錯誤。 打開Notepadv2工程並查看如下內容: ·打開並查看res/values路徑下的strings.XML文件——裡面有若干我們將要用於新功能的新字符串 ·同時,打開並查看Notepadv2這個類的頂部,你將會注意到一個用於存放我們即將用到的新字段,指針mNotesCursor,其下已定義了幾個新常量。 ·還需注意的是方法fillData()已多了一些注釋,並且現在用新定義的字段存儲便箋指針。本練習中的方法onCreate()與練習一中的聲明和實現相比並無變化。另需留意的是用於存儲便箋指針的類成員變量叫做mNotesCursor。首字母m已表明該變量為一成員變量並符合android編碼風格標准。 ·接下來還有幾個我們將要實現的新覆寫接口(onListItemClick()和onActivityResult())。 第二步 添加一個刪除一條便箋的菜單項: 1.             在接口onCreateOptionsMenu()中,添加如下一行代碼: menu.add(0, DELETE_ID, 0, R.string.menu_delete); 2.             該接口的完整定義如下: 3.             @Override 4.             public boolean onCreateOptionsMenu(Menu menu) { 5.                 super.onCreateOptionsMenu(menu); 6.                 menu.add(0, INSERT_ID, 0, R.string.menu_insert); 7.                 menu.add(0, DELETE_ID, 0, R.string.menu_delete); 8.                 return true; } 第三步 在接口onMenuItemSelected()中,為參數DELETE_ID添加一個新分支語句: mDbHelper.deleteNote(getListVIEw().getSelectedItemId()); fillData(); return true; 1.             這裡,我們通過接口deleteNote()刪除指定ID的便箋。為了得到指定便箋的ID,我們調用接口 getListVIEw().getSelectedItemId(). 2.             接下來,我們會調用接口fillData()以保持數據的更新. 接口onMenuItemSelected()的完整定義如下: @Override public boolean onMenuItemSelected(int featureId, MenuItem item) {     switch(item.getItemId()) {     case INSERT_ID:         createNote();         return true;     case DELETE_ID:         mDbHelper.deleteNote(getListVIEw().getSelectedItemId());         fillData();         return true;     }            return super.onMenuItemSelected(featureId, item); } 第四步 開始其它ActivitIEs 在本例中,我們的intent用了一個特定名稱的類。我們也可以創建Intents而不必清楚地知道控制此Intent的應用程序究竟是哪個,因為正如我們對類中的starting intents所了解的那樣,該類既可以在我們自己的應用程序中也可以在另一應用程序中。 例如,我們可能想打開一個浏覽器中的一個頁面,這裡我們仍將用到一個Intent。但是我們用一個預先定義好的Intent常量和一個用於描述我們目的的URI內容,而非某一特定的類,來控制此Intent。 實現接口 createNote(): 為創建一個便箋(ACTIVITY_CREATE)而用一個名為NoteEdit的類創建一個新Intent。緊接著調用接口startActivityForResult()來啟動該Intent。: Intent i = new Intent(this, NoteEdit.class); startActivityForResult(i, ACTIVITY_CREATE); 這種形式的Intent調用是面向我們Activity中特定類的,本例中該特定類即為NoteEdit。由於Intent類需要通過android 操作系統來發送請求信息,我們還得提供一個Context(this)。 觸發該Intent的接口startActivityForResult()是在一個新Activity完成後,才在我們的Activity中被調用的。 在我們的Activity中用於接收回調的接口名為onActivityResult(),我們將在稍後的步驟中實現它。另一調用Activity的接口名為startActivity(),但這是一種一勞永逸的調用方式(fire-and-forget)——在該調用方式下,當前的Activity並不會收到另一Activity已完成的通知,同時我們無法從被調Activity的接口startActivity()返回有關該Activity的任何結果信息。 不必擔心NoteEdit目前還不存在這一事實,我們很快就將完善之。 第五步 實現覆寫接口onListItemClick(): onListItemClick()是一個我們將要覆寫的回調函數。它在用戶點擊便箋列表中的某一項時調用。它一共有四個參數,分別是:調用該接口的ListView對象,所點擊的View(存在於ListView中),所點擊的position(該項在列表中的位置),所點擊的某一項的行索引(mRowId)。在本例中我們先忽略前兩個參數(當前我們只可能有一個ListVIEw),以及參數行索引。我們的著眼點全在用戶所選中的某一項在列表中所在的位置position。通過這個參數,我們可以從相應的行得到想要的數據,並將此數據打包發送至NoteEdit Activity。 在該回調函數接口的實現中,我們用類NoteEdit創建一個Intent來修改便箋內容,接著添加數據至一個作為參數傳遞給被調用Activity的Intent的附加包中。我們用其傳遞當前正在編輯的便箋的標題、正文以及行索引。最後,它將調用接口startActivityForResult()來啟動該Intent。以下是接口onListItemClick()實現的完整代碼: super.onListItemClick(l, v, position, id); Cursor c = mNotesCursor; c.moveToPosition(position); Intent i = new Intent(this, NoteEdit.class); i.putExtra(NotesDbAdapter.KEY_ROWID, id); i.putExtra(NotesDbAdapter.KEY_TITLE, c.getString(         c.getColumnIndexOrThrow(NotesDbAdapter.KEY_TITLE))); i.putExtra(NotesDbAdapter.KEY_BODY, c.getString(         c.getColumnIndexOrThrow(NotesDbAdapter.KEY_BODY))); startActivityForResult(i, ACTIVITY_EDIT); ·putExtra()是一個為觸發Intent而添加數據至附加包中的接口。這裡,我們用包傳遞我們想編輯的便箋內容的標題、正文以及行索引。 ·當我們定位到所選擇的對象所在列表中的相應位置時,通過接口moveToPosition(),有關該便箋的細節信息盡在查詢指針中。 ·通過添加到Intent的附加信息,及接口startActivityForResult(),我們在類NoteEdit中觸發該Intent及請求類型碼(請求類型碼將作為參數requestCode返回給接口onActivityResult())。 注意: 我們在接口定義的開始處就將類成員變量mNotesCursor賦給一個局部變量。這可視為對Android 應用程序代碼的優化。因為訪問一個局部變量其效率遠高於訪問Dalvik 虛擬機中的一個類成員變量。所以通過這種方式處理,我們只需訪問類成員變量一次,而訪問局部變量五次,就可使得常規處理更高效。所以極力推薦盡可能用此方法優化你的android應用程序代碼。 第六步 上述接口createNote()和onListItemClick()采用的是異步觸發Intent的方式。我們得靠一個句柄來回調,所以以下是接口onActivityResult()的實現部分: onActivityResult()是一個只在一個Activity有返回結果時才被調用的覆寫接口(記住!一個Activity只有在用接口startActivityForResult() 啟動時才有一個返回結果)。用於回調的一些參數如下: ·requestCode — 是Intent觸發時確定的初始請求類型碼(對我們而言,請求類型碼無非就是ACTIVITY_CREATE或者ACTIVITY_EDIT這兩者之一)。 ·resultCode — 調用返回的結果(或錯誤)類型,如果一切正常則該類型值為零,但若有失敗的異常情況則有對應的非零錯誤類型碼表示。有標准的結果類型碼可用,同時你也可以自定義一些結果類型碼常量,以滿足自己特定的問題。 ·intent — 這是一個由Activity返回的結果創建的Intent。它用於在Intent中的’Extras’返回數據。 接口startActivityForResult()和onActivityResult()的結合可視為一種異步的遠程進程調用(RPC: Remote Procedure Call),形成了一個Activity啟動另一個Activity並彼此共享服務的推薦方式。 以下為方法 onActivityResult()完整的定義: super.onActivityResult(requestCode, resultCode, intent); Bundle extras = intent.getExtras();   switch(requestCode) { case ACTIVITY_CREATE:     String title = extras.getString(NotesDbAdapter.KEY_TITLE);     String body = extras.getString(NotesDbAdapter.KEY_BODY);     mDbHelper.createNote(title, body);     fillData();     break; case ACTIVITY_EDIT:     Long mRowId = extras.getLong(NotesDbAdapter.KEY_ROWID);     if (mRowId != null) {         String editTitle = extras.getString(NotesDbAdapter.KEY_TITLE);         String editBody = extras.getString(NotesDbAdapter.KEY_BODY);         mDbHelper.updateNote(mRowId, editTitle, editBody);     }     fillData();     break; } ·在本方法中我們既控制 ACTIVITY_CREATE 也控制 ACTIVITY_EDIT 的Activity結果。 ·在請求類型碼為創建的情況下,我們從extras(源於返回的Intent)得到便箋的標題和正文,並用這些創建一條新便箋。 ·在請求類型碼為編輯的情況下,我們除了從extras得到上述字段外,還有行索引,並用這些編輯並更新數據庫中的便箋。 ·fillData() 最後的接口確保每一個都更新完畢。 第七步 布局的藝術 練習提供的屏幕布局文件note_edit.XML是我們將要建立的應用中最復雜的一個了,但這並不意味著你若想在真正的android應用中用到一個布局,非得和這個有多接近才行。 創建一個良好的界面是部分藝術、部分科學,剩余的就是孜孜以求了。掌握Android layout 是創建一個賞心悅目的android應用程序的核心部分。 不妨看看 VIEw Gallery 中的一些例子,了解如何運用它們。范例工程ApiDemos是學習如何創建千姿百態的屏幕布局的極好資源。 打開文件note_edit.XML並浏覽一二,這是有關便箋編輯界面方面的代碼。 這是目前我們處理過的最復雜的界面布局了,這裡給出的文件是為了避免在代碼排版中出現淡入淡出的問題。(XML對大小寫及結構有非常嚴格的限制,而這些錯誤往往又都是屏幕布局問題的根源所在。) 這裡用到了一個之前我們從未見過的新參數: android:layout_weight (在本例中該參數每種情況下都賦值為1) layout_weight用於給一個線性布局中的諸多視圖的重要度賦值。所有的視圖都有一個layout_weight值,默認為零,意思是需要顯示多大的視圖就占據多大的屏幕空間。若賦一個高於零的值,則將父視圖中的可用空間分割,分割大小具體取決於每一個視圖的layout_weight值以及該值在當前屏幕布局的整體layout_weight值和在其它視圖屏幕布局的layout_weight值中所占的比率而定。 舉個例子:比如說我們在水平方向上有一個文本標簽和兩個文本編輯元素。該文本標簽並無指定layout_weight值,所以它將占據需要提供的最少空間。 如果兩個文本編輯元素每一個的layout_weight值都設置為1,則兩者平分在父視圖布局剩余的寬度(因為我們聲明這兩者的重要度相等)。如果兩個文本編輯元素其中第一個的layout_weight值設置為1,而第二個的設置為2,則剩余空間的三分之一分給第一個,三分之二分給第二個(因為我們聲明第二個有較之第一個更高的重要度)。 這個布局也演示了如何在其它布局中嵌套多個布局以實現更復雜更漂亮的布局。在本例中,一個水平方向上的線性布局嵌套在另一垂直方向上的布局中,以使標題標簽和文本字段在水平方向上挨個對齊。 第八步 創建一個NoteEdit類以擴展android.app.Activity。 這是我們第一次不用android Eclipse插件的幫助來創建一個Activity。當按這種方式創建時,方法onCreate()並未自動覆寫。很難想象一個沒有覆寫其onCreate()方法的Activity會是什麼樣子,所以這是接下來你先得完成的首要任務。 1.             右擊Eclipse文件夾樹形目錄中的com.android.demo.notepad2文件夾,在彈出的右鍵菜單中選擇New > Class。 2.             為類NoteEdit命一個名:對話框中的字段(fIEld in the dialog)。 3.             在類Superclass中,輸入android.app.Activity,也可以通過組合鍵(在Window和Linux環境下是Ctrl+Space,在Mac環境下則是Cmd+Space)來觸發IDE中的代碼提示以找到對應的文件夾和類。 4.             單擊Finish。 5.             在生成的類中,在編輯窗口中單擊右鍵,選擇Source > Override/Implement Methods...。 6.             滑動對話框復選列表的滾動條定位到onCreate(Bundle)——並選擇緊鄰的復選框。 7.             單擊 OK. 這樣方法就出現在你的類中了。 第九步 為類NoteEdit的方法onCreate()實現其定義: 該方法將為我們的新Activity設置一個名為”編輯便簽”的標題(這是一個在文件strings.xml中定義的字符串),同時通過布局文件note_edit.XML設置便箋內容視圖。我們可以得到便箋標題和文本正文視圖,以及確認按鈕的句柄。如此一來,我們就可以通過這些句柄設置得到便簽的標題和正文,並將確認按鈕綁定至響應用戶按下該按鈕的事件。 然後,我們可以解開因傳入於所調用的Intent中的附加包中,而已傳到Activity裡的參數值,並用這些值預填充標題和便簽文本正文及視圖,這樣用戶就可以編輯它們了。接下來,我們可以獲取和存儲便箋行索引(mRowId)這一值,從而可以知道用戶當前正在編輯哪一條便簽。 1.             在方法onCreate()中,建立布局: setContentVIEw(R.layout.note_edit); 2.             找到我們需要的編輯和按鈕組件 可以通過在R類中聲明的與之關聯的ID找到這些資源,然後需要將它轉換為正確的視圖類型 (兩個文本視圖為EditText 類, 確認按鈕為Button類) mTitleText = (EditText) findVIEwById(R.id.title); mBodyText = (EditText) findVIEwById(R.id.body); Button confirmButton = (Button) findVIEwById(R.id.confirm); 需要注意的是mTitleText和mBodyText這兩個是類成員變量(你得先在類中聲明才能在此使用) 3.             在類中聲明一個私有類成員變量Long mRowId,用於存儲當前的行索引值mRowId(如果有的話) 4.             在方法onCreate()中繼續添加代碼,用Intent附加數據包(如果出現的話)中的值初始化便箋標題、正文及行索引ID這些變量 5.             mRowId = null; 6.             Bundle extras = getIntent().getExtras(); 7.             if (extras != null) { 8.                 String title = extras.getString(NotesDbAdapter.KEY_TITLE); 9.                 String body = extras.getString(NotesDbAdapter.KEY_BODY); 10.             mRowId = extras.getLong(NotesDbAdapter.KEY_ROWID); 11.                     12.             if (title != null) { 13.                 mTitleText.setText(title); 14.             } 15.             if (body != null) { 16.                 mBodyText.setText(body); 17.             } } o    我們將因觸發Intent而設置到附加數據包中的標題和正文兩個值從中抽取出來。 o    我們也對文本的字段賦值實行非空保護(防止出現空字符串賦給文本字段的意外情況出現)。 18.         為按鈕創建一個方法onClickListener(): Listener也許是UI實現中讓人倍感困惑的方面之一,但在本例中我們要實現的效果卻很簡單,就是在用戶點確定按鈕時調用onClick()方法。用其做一些事,並將便簽被編輯後的一些值返回給Intent這一調用者。我們通過名為匿名的內部類來實現這一目的,這個類除非你之前見過,否則第一次看到確實會讓人覺得有點暈。但是你確實需要將困惑的心態暫且拋開,因為將來有機會可以參看這一段代碼,並將了解到如何創建一個Listener並將其關聯至一個按鈕(Listener是Java開發中一個常見的術語,尤其是在UI的設計實現中)。下面代碼是一個空的Listener: confirmButton.setOnClickListener(new VIEw.OnClickListener() {       public void onClick(View vIEw) {                    }            }); 第十步 在我們的Listener中完成方法onClick()的實現部分: 當用戶按下確定按鈕,這個接口就會運行。我們希望該方法能獲取被編輯的便簽文本中的標題和文本字段,並且將這些數據放到一個作為返回值的包中,這樣這些數據就可以被傳遞回最初觸發這個NoteEdit Activity的Activity中,如果該接口響應的是一個編輯而不是創建操作的話,我們還需將行行索引ID(rowed)也放至作為返回值的包中,這樣Notepadv2 類就可以保存對便簽所做的編輯。 1.             創建一個作為返回值的數據包(Bundle)。並將便箋標題和正文這兩個值,在Notepadv2中定義的關鍵字常量,傳遞給它 2.             Bundle bundle = new Bundle(); 3.                    4.             bundle.putString(NotesDbAdapter.KEY_TITLE, mTitleText.getText().toString()); 5.             bundle.putString(NotesDbAdapter.KEY_BODY, mBodyText.getText().toString()); 6.             if (mRowId != null) { 7.                 bundle.putLong(NotesDbAdapter.KEY_ROWID, mRowId); } 8.             將此數據包傳入一個新的Intent中並結束Activity 9.             Intent mIntent = new Intent(); 10.         mIntent.putExtras(bundle); 11.         setResult(RESULT_OK, mIntent); finish(); o    這裡的這個新Intent只是扮演一個簡單的數據包傳遞者的角色(裡面包括我們要傳遞的便箋標題、正文及行索引ID) o    方法setResult()是設置結果類型碼並將Intent傳遞回此Intent的調用者。在本例中,每個都奏效了,所以我們返回的結果編碼為RESULT_OK。 o    調用方法finish()是標識該Activity已完成的信號(相當於一個返回調用)。任何設置到結果中的接口都和執行控制信息一並返回至結果調用者。 方法onCreate() (另加支持該類的一些字段) 的完整實現如下: private EditText mTitleText; private EditText mBodyText; private Long mRowId;   @Override protected void onCreate(Bundle savedInstanceState) {     super.onCreate(savedInstanceState);     setContentVIEw(R.layout.note_edit);        mTitleText = (EditText) findVIEwById(R.id.title);     mBodyText = (EditText) findVIEwById(R.id.body);       Button confirmButton = (Button) findVIEwById(R.id.confirm);        mRowId = null;     Bundle extras = getIntent().getExtras();     if (extras != null) {         String title = extras.getString(NotesDbAdapter.KEY_TITLE);         String body = extras.getString(NotesDbAdapter.KEY_BODY);         mRowId = extras.getLong(NotesDbAdapter.KEY_ROWID);               if (title != null) {             mTitleText.setText(title);         }         if (body != null) {             mBodyText.setText(body);         }     }        confirmButton.setOnClickListener(new VIEw.OnClickListener() {           public void onClick(View vIEw) {             Bundle bundle = new Bundle();                        bundle.putString(NotesDbAdapter.KEY_TITLE, mTitleText.getText().toString());             bundle.putString(NotesDbAdapter.KEY_BODY, mBodyText.getText().toString());             if (mRowId != null) {                 bundle.putLong(NotesDbAdapter.KEY_ROWID, mRowId);             }               Intent mIntent = new Intent();             mIntent.putExtras(bundle);             setResult(RESULT_OK, mIntent);             finish();         }     }); } 第十一步 至關重要的android Manifest文件 androidManifest.XML文件是android操作系統理解你應用程序的方式。此文件定義了在啟動和設置時在何處(或者是否)顯示應用程序、該應用程序定義了哪些活動(acivitIEs)、服務(services)和內容提供者(Content Providers)、以及該應用程序能接收哪些Intent 等等諸如此類的應用程序分類目錄。 最後,在manifest文件中定義了新的Activity: 在新Activity被Android操作系統識別之前,它需要在AndroidManifest.xml文件中有自己的Activity 入口(Activity Entry)。這樣Android操作系統就知道該Activity在androidManifest.XML文件中並可以被其調用。我們也可以確定該Activity在此處實現了哪些IntentFilter,但是我們現在先略過此部分,暫且只讓android操作系統知道該Activity已定義了。 在Eclipse 插件中包括一個可以很方便地編輯androidManifest文件的Manifest編輯器,我們即將用到該插件。若你更願意直接編輯文件或不用Eclipse插件的話,請看下面框中有關如何不用新的Manifest編輯器實現編輯Manifest文件的信息。 1.             雙擊文件夾樹形目錄中的androidManifest.XML文件,打開。 2.             點擊Manifest 編輯器底部的Application選項卡。 3.             在應用程序部分點擊 Add...。 如果在對話框頂部你若看到一個單選框(radiobutton)的話,選擇頂部為”在應用程序頂層創建一個新元素”的單選框。 4.             在點擊” OK”前,確定已選中了對話框選項卡中的”(A)Activity”. 5.             在應用程序Node部分,點擊該新”Activity”,在Name*字段中輸入.NoteEdit,然後回車 Android Manifest編輯器可以讓你在文件中添加更復雜的Activity入口,同時也看看其它一些可用的選項(但千萬別選擇,否則這些選項將添加到你的Manifest文件中)。當你邁向更高級的Android應用時,該編輯器幫助你明白及如何改變androidManifest.XML文件。 如果你更願意直接編輯AndroidManifest.xml文件,只需簡單打開該文件並查看其資源(用Eclipse 編輯器中的androidManifest.XML選項卡直接查看資源代碼),然後按如下方式修改代碼:
<activity android:name=".NoteEdit"></activity>

這一句應放在下面這句的後面
</activity> for the .Notepadv2 activity. 第十二步 現在開始運行它吧! 你現在應該已可以從菜單添加真正的便簽了,也可以刪除一個已存在的便簽了。但需要注意的是為了按順序刪除某條便簽,你必須首先用設備上的方向控制來高亮顯示它。還有就是,若從列表中選擇一條便簽應該同時引出可以讓用戶編輯的便簽編輯器。當結束編輯按確定按鈕後,則將編輯後的效果保存至數據庫中。 答案和下一步的學習計劃 你可以從壓縮文件Notepadv2Solution中得到有關本練習的答案並與你自己的實驗代碼進行對比。 現在試著編輯一條便簽,然後點擊模擬器上的後退按鈕而非確定按鈕(後退按鈕就在菜單按鈕下面)。你將發現突然蹦出一個錯誤。顯然我們的應用程序還有些問題。更糟糕的是,如果你在對便簽做了一些編輯操作後點擊後退按鈕,你再返回到記事本中查看你修改過的便簽時,你將發現你所做的所有編輯操作的效果全部丟失了。在下一階段練習中,我們將修復這些問題。 一旦你准備好了,就繼續進行下一個練習——教程: 練習 3——你將從中學習通過在便簽編輯Activity中引入一個合理的生命周期來解決後退按鈕和編輯信息丟失的問題。
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved