Eclipse開發Android應用程序入門:重裝上陣

作者: coolshell  發布時間: 2011-04-12 10:56  閱讀: 15618 次  推薦: 4   原文鏈接   [收藏]  

  原文:http://www.smashingmagazine.com/2011/03/28/get-started-developing-for-android-with-eclipse-reloaded/

  在我們教程系列的第一部分中,我們使用Android和Eclipse開發了一個簡單的飲茶計時器的應用程序。在第二部分,我們將繼續開發這個程序,并給它增加一些其他的額外的功能。在開發的過程中,我們將給你介紹更多重要而強大的Android SDK特性,包括持久化數據存儲,Activity和Intent,和共享用戶首選項(譯者注:類似于windows 的注冊表的一種機制)。

  跟著本教程,你需要上一篇教程中的代碼,如果你想直接使用代碼,你可以使用如下的指令從GitHub上check out出tutorial_par_1標記的代碼:

 
$ git clone git://github.com/cblunt/BrewClock.git
$ cd BrewClock
$ git checkout tutorial_part_1

  在GitHub中檢出了代碼后,你需要將代碼倒入到Eclipse中的項目中:

  1. 運行 Eclipse 選擇 File → Import…
  2. 在導入窗口, 選擇 “Existing Projects into Workspace”并點擊 “Next.”
  3. 在下一屏,點擊 “Browse,”選擇你從GitHub上clone出的代碼目錄。
  4. 點擊“Finish” 將項目導入到Eclipse中。

  在導入項目到Eclipse之后,你有可能會看到有如下的警告信息:

 
Android required .class compatibility set to 5.0.
Please fix project properties.

  如果有這種情況,右鍵點擊“Project Explorer ”中新導入的BrewClock項目,并選擇 “Fix Project Properties,” 并重啟Eclipse。

  數據持久化入門

  當前,BrewClock 讓用戶為他們泡的茶設置一個定時器。這個非常棒的一個工作,但是如果對于不同的茶使用同一個泡茶時間的結果會怎樣呢,是不每種茶都應該有自己的一個泡茶時間呢?如果這樣,那豈不是所有的用戶都需要記下每一類茶所需要泡的時間!這不是一個很好的用戶體驗。因此,在這篇教程中,我將新增一個功能來為用戶每種不同的茶葉存放一個泡茶時間,并當用戶想泡茶的時候,可以從茶葉列表中進行選擇。

  為了實現這個目的,我們得利用Android的豐富的數據持久化的API。Android提供了幾種方式來存儲數據,本文將要覆蓋其中的兩種方式。第一種,使用SQLite數據庫引擎來為我們存儲數據。

  SQLite 是一種流行的輕量級SQL數據庫引擎,它將數據存在單個文件中。SQLite經常用于桌面或在那些運行不能運行客戶端-服務器SQL引擎(例如MySQL或PostgreSQL)的嵌入式的應用上。

  每個安裝在Android上的應用都可以保存和使用多個SQLite數據庫文件(由數據存儲容量決定),這些數據由系統自動地進行管理。應用程序的數據是私有并且不能被其他的應用程序所訪問。(數據可以通過ContentProvider(譯者注:內容提供者類)類進行共享,但是我們不會在本教程中覆蓋關于內容提供者的內容)。當數據應用程序被更新時,數據庫文件就進行持久化,當應用程序被刪除時,數據庫文家就被刪除。

  我們在BrewClock應用使用SQLite數據來維護我們的茶葉列表和泡茶所需要的時間。下面是我們我們將使用的數據表的一個總體介紹。

 
+-------------------------------------+
| Table: teas |
+------------+------------------------+
| Column | Description |
+------------+------------------------+
| _ID | integer
, autoincrement |
| name | text
, not null |
| brew_time | integer
, not null |
+------------+------------------------+

  如果以前你使用過SQL,你應該熟悉這些內容。數據表有三個字段,一個唯一標示(_ID),茶葉名稱(name)和泡茶時間(brew_time)字段。我們將使用Android提供給我們的API在應用中建立數據表。系統將負責在正確的位置為我們的創建數據庫文件。

  抽象數據庫

  為了確保數據庫的代碼容易被維護,我們用一個單獨的類TeaData來抽象所有處理數據庫創建,插入,和查詢的代碼。如果你熟悉模型-試圖-控制(譯者注:MVC)方法的話,這個你也應該熟悉。所有數據庫代碼與我們的BrewClockActitvity類隔離開來。Actitvity可以初始化一個新的TeaData實例(這個實例將連接數據庫)并完成它所需要的工作。以這種方式工作保證了我們可以方便的更改我們所使用的數據庫而不用修改其他那些和數據庫不相關部分的代碼。

  通過菜單File → New → Class.在BrewClock項目中創建一個TeaData的新類。確保TeaData擴展于android.database.sqlite.SQLiteOpenHelper 類,并選中“Constructors from superclass”復選框。
  TeaData 類將為你自動地處理SQLite數據庫的創建和版本。我們需要增加一些方法來作為其他代碼到數據庫的接口。

  增加兩個常量來存儲數據庫的名字和版本,增加表名和表中列名。我們使用Android提供的常類BaseColumns._ID來做為表的唯一id列:

 
// src/com/example/brewclock/TeaData.java
import android.app.Activity;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.provider.BaseColumns;

public class TeaData extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "teas.db";
private static final int DATABASE_VERSION = 1;

public static final String TABLE_NAME = "teas";

public static final String _ID = BaseColumns._ID;
public static final String NAME = "name";
public static final String BREW_TIME = "brew_time";

//
}

  為TeaData增加一個構造方法,以數據庫名稱合版本號為參數調用其父類的構造方法。Android將會自動地打開數據庫(如果數據庫不存在就自動創建它)。

 
// src/com/example/brewclock/TeaData.java
public TeaData(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}

  我們需要重載onCreate方法,并執行一個SQL 串執行創建數據庫表的操作。Android將會在數據庫文件第一次被創建時調用這個方法。

  在啟動過程中,Android檢查數據庫的版本是否我們傳入的版本一致。如果版本發生了改變,Android將會調用onUpgrade方法,在這個方法總,你可以編寫修改數據庫結構的業務邏輯。在本教程中,我們將讓Android刪除數據庫并重建數據庫。

在onCreate和onUpgrade中增加如下的代碼:

 
// src/com/example/brewclock/TeaData.java
@Override
public void onCreate(SQLiteDatabase db) {
// CREATE TABLE teas (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, brew_time INTEGER);
String sql =
"CREATE TABLE " + TABLE_NAME + " ("
+ _ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ NAME + " TEXT NOT NULL, "
+ BREW_TIME + " INTEGER"
+ ");";

db.execSQL(sql);
}

@Override

public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL(
"DROP TABLE IF EXISTS " + TABLE_NAME);
onCreate(db);
}

  下一步,我們需要新增代碼讓我們方便地在數據庫中新增茶葉記錄。我們新增一個帶茶葉名稱和泡茶時間的方法來負責插入記錄。Android為了盡量避免開發者使用SQL語句,提供了一堆類來處理向數據庫中查入記錄。首先,我們創建一個ContentValues集合,并將相關的值插入到這個集合中去。

  對于ContentValues集合,我們只要簡單地提供一個列名和值來插入就行了。Android負責創建和運行正確的SQL。使用Android的數據類確保了你能寫出安全,跨平臺的數據庫操作代碼。

  Add a new method, insert(), to the TeaData class:

 
// src/com/example/brewclock/TeaData.java
public void insert(String name, int brewTime) {
SQLiteDatabase db
= getWritableDatabase();

ContentValues values
= new ContentValues();
values.put(NAME, name);
values.put(BREW_TIME, brewTime);

db.insertOrThrow(TABLE_NAME,
null, values);
}

  查詢數據

  我們應用程序具有了在數據庫中保存數據的能力后,我們同樣也需要一種方式將數據取回來。Android提供了游標Cursor接口來完成這件工作。一個游標代表了針對數據庫運行一個SQL返回的結果集,游標在這個結果集中維護了一個指針來指向結果集中的一行。這個指針可以向前,向后移動,并返回每一列的值,下面我們用圖形來幫助你理解游標:

  SQL 查詢: SELECT * from teas LIMIT 3;

 
+-----------------------------------+

| _ID | name | brew_time |

+-----------------------------------+

|
1 | Earl Grey | 3 |

|
2 | Green | 1 | <= Cursor

|
3 | Assam | 5 |

+-------+-------------+-------------+

  在這個例子中,游標指向了結果集中的第二條記錄(綠茶)。我們可以通過調用cursor.moveToPrevious()方法,將游標向前移動,讓它指向第一行(Earl Grey),或者調用moveToNext向前移動指向Assam。要取到游標所指向記錄的茶葉的名稱,我們只要調用cursor.getString(1),1代表我們向提取數據列的下標(注意下標識從0開始的,1代表第二列,依次類推)。

  在了解游標后,我們增加一個創建游標對象并返回數據庫中所有的茶葉信息。在TeaData中增加all方法:

 
// src/com/example/brewclock/TeaData.java
public Cursor all(Activity activity) {
String[] from
= { _ID, NAME, BREW_TIME };
String order
= NAME;

SQLiteDatabase db
= getReadableDatabase();
Cursor cursor
= db.query(TABLE_NAME, from, null, null, null, null, order);
activity.startManagingCursor(cursor);


return cursor;
}

  因為這個方法乍一看有點古怪,所以讓我們先來關心一下這個方法的一些細節。我們沒有使用SQL的查詢語句,而是使用了Android提供的數據庫接口方法。

  第一,我們需要告訴Android,我們所關心的列的信息。我們創建了一個字符串數組,數組中存放這TeaData中列的標示信息。我們還設置了我名們期望的結果集按照哪一個列進行排序的列名。

  第二,我們使用getReadalbeDatabase()創建了一個到數據庫的只讀連接,并調用query方法告訴Android我們希望用query方法運行一個查詢。query()方法有很多的參數,Android在內部將這些參數轉化為一個查詢語句。此外,Android的抽象層保證了即使底層數據儲存機制發生了變化,我們的應用程序代碼也能正確的工作。

  由于我們只要返回表中的所有記錄,所以我們沒有在方法中使用到鏈接join,過濾filter和分組group(例如:在SQL中的WHERE,JOIN,和GROUP BY)。from和order變量告訴查詢數據庫需要返回那些列和提取數據時按什么列進行排序。我們使用SQLiteDatabase.query()作為和數據庫的人機交互接口。

  最后,我們讓Activity(在本例中,我們的BrewClockActivity)來管理游標。通常,游標需要人工刷新內容,因此當我們增加一個新茶信息到數據庫中時,我們就需要刷新我們的游標。每當我們的應用被掛起和恢復的時候,通過調用startManagingCursor()讓Android來幫我們重建結果集。

  在TeaData類中增加count方法:

 
// src/com/example/brewclock/TeaData.java
public long count() {
SQLiteDatabase db
= getReadableDatabase();
return DatabaseUtils.queryNumEntries(db, TABLE_NAME);
}

  保存TeaData類,使用修正沒有import 的類(Source → Organize Imports),在完成我們的數據類后,下一步我們將著手修改我們BrewClock的人機界面。

  修改BrewClock用戶界面,允許進行茶葉選擇

  持久化茶和泡茶的時間的目的是讓用能快速的選擇他們所鐘愛的預設置的茶。為了完成這個功能,我們需要再BrewClock的主界面上增加一個Spinner(類似于桌面上彈出菜單),生成一個來自于TeaData的茶列表。

  和前面的教程一樣,我們使用了Eclipse的布局器編輯器在BrewClock的主界面布局XML文件中增加Spinner。在LinearLayout元素下面增加下面這些代碼(大約在24行)。如果你打開了可視化的布局編輯器后,你可以點擊窗口下面的地”Code View”進行切換。

 
<!-- /res/layout/main.xml -->

<!-- Tea Selection -->
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">

<Spinner
android:id="@+id/tea_spinner"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />

</LinearLayout>

  在BrewClockActivity類里面,增加一個成員變量指向Spinner,通過使用findViewById連接界面上的控件:

 
// src/com/example/brewclock/BrewClockActivity.java
protected Spinner teaSpinner;
protected TeaData teaData;

//

public void onCreate(Bundle savedInstanceState) {
//
teaData = new TeaData(this);
teaSpinner
= (Spinner) findViewById(R.id.tea_spinner);
}

  運行你的程序以確保新的界面正確地生效。你應該在泡茶計數器下看見一個空白的彈出式菜單(或者是Spinner)。如果點擊spinner,Android將顯示一個彈出式的菜單并為你提供選擇列表。在這時,菜單的內容因該是空的,現在讓我們來綁定Spinner和我們的茶葉數據庫。

  數據綁定

  當Android從數據庫中查詢數據時,它將會返回一個游標Cursor對象。Cursor代表了來自數據庫的結果集,并可以移動游標來提取結果中的數據。使用一類Android提供的稱為“適配器Adapter”的類,我們很容易將這個結果集綁定到Spinner上。適配器完成了提取數據庫結果集中的數據并在界面上顯示這些數據等這些復雜而困難工作。

  在我們的TeaData.all()方法中已經可以返回一個帶有tea表內容的游標,使用這個游標,我們所需要做的工作就是創建一個SimpleCursor適配器來綁定我們的teaSpinner,Android會負責處理將數據顯示在spinner的列表中。

  通過創建一個SimpleCursorAdapter類來連接Spinner與teaData.all()返回的游標:

 
// com/example/brewclock/BrewClockActivity.java

public void onCreate(Bundle savedInstanceState) {
//
Cursor cursor = teaData.all(this);

SimpleCursorAdapter teaCursorAdapter
= new SimpleCursorAdapter(
this,
android.R.layout.simple_spinner_item,
cursor,

new String[] { TeaData.NAME },
new int[] { android.R.id.text1 }
);

teaSpinner.setAdapter(teaCursorAdapter);
teaCursorAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
}

  注意,我們使用了Android內建的android.R對象。這個對象提供了你的應用程序中的默認資源,例如視圖和布局。在我們的代碼中,我們使用了android.R.layout.simple_spinner_item,它是簡單的文本標簽布局。

  如果你再次運行的應用程序,你將會看到spinner中仍然是空的!雖然我們已經連接了我們的數據庫,但是由于數據庫中沒有任何記錄,所以我們任何看到了空列表。

  我們通過在構造方法中增加一些默認記錄來讓用戶可以選擇所需要的茶葉,為了避免重復記錄,我們只有在數據庫中記錄為0的情況才增加默認記錄。在本教程的代碼中,我們使用前面增加的count()來檢查數據庫中表記錄是否為空。

  增加當數據庫中表為空的默認記錄代碼。把這些代碼增加從數據庫提取茶葉數據的前面(譯者注:上一段的代碼前)。

 
// com/example/brewclock/BrewClockActivity.java
public void onCreate(Bundle savedInstanceState) {
//

// Add some default tea data! (Adjust to your preference <img src="http://coolshell.cn/wp-includes/images/smilies/icon_smile.gif" alt=":)" class="wp-smiley">
if(teaData.count() == 0) {
teaData.insert(
"Earl Grey", 3);
teaData.insert(
"Assam", 3);
teaData.insert(
"Jasmine Green", 1);
teaData.insert(
"Darjeeling", 2);
}


// Code from the previous step:
Cursor cursor = teaData.all(this);

//
}

  現在再次運行你的應用程序。你將會發現茶葉Spinner有了一條選擇。點擊Spinner讓你可以從數據庫選擇你要的茶葉。

  恭喜你!你已經成功關聯了你的界面和代碼。這是任何軟件開發過程中一個非常重要的方面。正如你所看見的,Android將這一步簡化的非常容易,但是功能有是非常的NB。使用游標和適配器,你可以將數據源(叢簡單的字符串數組到復雜的數據庫查詢)綁定到任何類型的視圖:spinner或列表,設置是類似iTunes cover-flow gallery!

  雖然現在已經可以開始泡茶了,但是我們工作還遠沒有結束。當你從Spinner選擇了不同的茶,這個選擇卻不會發生任何作用。我們需要根據用戶所選茶葉的種類取更新我們的泡茶時間。

  讀取選中茶葉數據并更新泡茶時間

  為了能讀取用戶從數據庫中選擇茶葉的數據,我們必須增加一個針對此事件的監聽器。類似于處理按鈕點擊事件的OnClickListener監聽器一樣,我們將實現一個OnItemSelectedListener。當用戶從視圖中做出一個選擇的事件將觸發這個監聽器,例如從我們的Spinner。

  在BrewClockActivity中增加需要實現的接口OnItemSelectedListener。并增加其響應的處理方法onItemSelected()和onNothingSelected():

 
// src/com/example/brewclock/BrewClockActivity.java
public class BrewClockActivity extends Activity implements OnClickListener, OnItemSelectedListener {
//
public void onItemSelected(AdapterView<?> spinner, View view, int position, long id) {
if(spinner == teaSpinner) {
// Update the brew time with the selected tea’s brewtime
Cursor cursor = (Cursor) spinner.getSelectedItem();
setBrewTime(cursor.getInt(
2));
}
}


public void onNothingSelected(AdapterView<?> adapterView) {
// Do nothing
}
}

  在這里我們要檢查是觸發的spinner此事件是不是BrewClock的teaSpinner。如果是,我們將提取代表選中記錄的游標對象。這些都是由關聯teaData和Spinner的SimpleCursorAdapter來提供我們完成的。Android知道哪個查詢產生的Spinner數據,也知道用戶選擇的哪個數據。Android使用游標來返回數據庫的一行記錄,也代表了用戶所選擇的茶葉數據。

  Cursor的getInt()方法帶了一個我們想提取的列的下標為參數。在我們的teaData.all()方法中創建游標的時候,我們讀取的列是_ID,NAME和BREW_TIME。假設我們在teaSpinner中選擇的是Jasmine Tea,那么將返回我們所選數據所對應的數據庫記錄。

  然后我們再通過傳遞參數2來選擇此記錄的第二列的整型值。這個值提供給setBrewTime()方法。這個方法用于更新界面上的泡茶時間。

  最后,我們需要告訴teaSpinner BrewClockActivity正在監聽OnItemSelected事件。在BrewClockActivity的onCreate方法中增加下面的代碼:

 
// src/com/example/brewclock/BrewClockActivity.java
public void onCreate() {
//
teaSpinner.setOnItemSelectedListener(this);
}

  大功告成!再次運行你的程序,并從Spinner選擇不同的茶葉。每次你所選的茶葉它所對應的泡茶時間都回顯示對應的界面上。我們余下的代碼中已經可以處理從當前時間開始遞減計數。所以在有預先設置的茶葉種類下,我們已經可以完成我們所想要的功能。

  你當然可以,回到之前的代碼中去增加一些茶葉種類你滿足你的口味。但是如果你發布BrewClock程序到Android Market,每當有人向增加新的茶葉數據到數據庫中,我就需要去手動的取更新數據中的內容并重新發布它;這樣所有的人就必須去更新它,并且所有的人都有一個同樣的列表。這聽起來非常的不靈活,因此我們還有很多的工作需要完成!

  如果用戶自己有方法新增茶葉種類到數據庫里面,將會非常的不錯的做法。因此我們將在下一章繼續。。。

  Activity 介紹

  和你應用程序中每個屏幕關聯的代碼就是Activity。每次當你從一屏切換到另外一屏,Android就會創建一個新的Activity。在真實世界中,雖然一個應用程序經常由多個屏幕/Activity構成,Andriod卻將每個屏幕看作獨立的個體。多個Activity工作在一起形成一種關聯的體驗,這是因為Android讓你非常容易地在屏幕/Activity之間傳遞數據。

  在本節最后,你將為你的應用程序新增一個新的Activity(AddTeaActivity)并將它注冊到Android系統中。你還需要從最初的BrewClockActivity傳遞數據到新的Activity中。

  首先,我們需要給用戶一種方式切換到新的Activity上。我們將使用選項菜單來完成之一步。

  選項菜單

  當用戶他們的設備上的“Menu”按鍵時,選項菜單以彈出菜單的形式出現。Android負責菜單的自動創建和顯示;你只需要告訴Android,菜單顯示什么內容和當用戶點擊菜單時該做什么就行。

  然而,最好不要在代碼中硬編碼菜單的標題,我們可以使用Android的字符串資源。字符串資源是一個獨立的文件,在這個文件中你可以維護所有用于用戶閱讀的字符串和標簽資源,并可以在代碼調用它們。這就意味著當你在未來需要修改字符串時,你只要修改這一處地方即可。.

  在project explorer中導航到“res/values”下,你將會看到string.xml文件已經存在。這個是你再創建新項目的時候由Eclipse創建的,這文件存放著在整個應用程序我們將要使用的字符串。

  雙擊打開strings.xml ,通過窗口底部的選項頁切換到XML 視圖。

  在<resources>…</resources> 元素中增加下面的內容:

 
<!-- res/values/strings.xml -->
<resources>
<!---->
<string name="add_tea_label">Add Tea</string>
</resources>

  我們在這里定義了一個字符串,add_tea_label和它關聯的文本,我們可以在整個程序代碼中通過add_tea_label來使用其關聯的文本。如果標簽因為某個原因需要修改,我們只需要在這個文件修改這一個地方就能完成整個程序的修改。

  下一步,讓我們創建一個新文件完成選項菜單的定義,如果字符串和布局一樣,菜單也使用XML來定義。因此我們將在Eclipse中川建一個新的XML文件:

  通過選擇File → New → Other, 并選擇“Android XML File.”在Eclipse中創建一個新的XML文件。

  選擇資源的類型為 “Menu”,保存文件名為main.xml。Eclipse將為你自動的創建一個目錄res/menu, 來存放你的菜單文件。

  打開res/menus/main.xml 文件, 通過窗口底部的“main.xml”選項頁來切換到XML視圖。

  增加菜單項, add_tea。

 
<!-- res/values/strings.xml -->
<resources>
<!---->
<string name="add_tea_label">Add Tea</string>
</resources>

  注意android:title 屬性被設置為@string/add_tea_label。這告訴Android在我們的strings.xml文件中查找add_tea_label并返回相關聯的標簽內容。在本列中我們的菜單項的標簽時“Add Tea”。

  下一步,我們將告訴我們的Activity,當用戶點擊設備上的“memu”按鍵時來顯示這個選項菜單。

  返回BrewClockActivity.java代碼, 重載onCreateOptionsMenu 方法,這個方法告訴Android 當用戶點擊“Menu”按鍵時,裝載我們的菜單:

 
// src/com/example/brewclock/BrewClockActivity.java
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater
= getMenuInflater();
inflater.inflate(R.menu.main, menu);


return true;
}

  當用戶點擊他設備上的“Menu”按鍵時,Android將調用onCreateOptionsMenu。在這個方法中,我們創建了一個MenuInflater, 這個對象將從你的應用程序包中裝載你的菜單資源。就如同按鈕和文本域組成你的應用程序布局一樣,main.xml資源也是通過全局對象R來生效的,因此我們將此對象提交給MenuInflater對象。

  為了測試菜單,保存并在模擬器中并運行應用程序。當程序運行起來使,點擊“Menu”按鍵,你將會看到一個彈出式的菜單顯示了一個“Add Tea”選項。

  如果你點擊“Add Tea”選項,Android自動地檢測到點擊并關閉菜單。在后臺,Android將會提醒應用程序選項已經被點擊。

  處理菜單點擊

  當用戶點擊 “Add Tea” 菜單選項,我們想要顯示一個新的Activity以便我們能進入增加新茶葉種類的界面。通過選擇File → New → Class來創建一個的Activiy。

  將新類命名為 AddTeaActivity,并確保它繼承于android.app.Activity類。這個類也放在com.example.brewclock包中:

 
// src/com/example/brewclock/AddTeaActivity.java
package com.example.brewclock;

import android.app.Activity;
import android.os.Bundle;

public class AddTeaActivity extends Activity {
@Override

public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}

  上面樣例中的空白Activity將不會完成任何工作。但是通過它,我們已經可以完成選項菜單的功能。

  在BrewClockActivity增加一個重載方法onOptionsItemSelected 。當用戶點擊菜單項時,這個方法被Android調用。 (注意點擊的MenuItem為它的接收參數:

 
// src/com/example/brewclock/BrewClockActivity.java
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()) {
case R.id.add_tea:
Intent intent
= new Intent(this, AddTeaActivity.class);
startActivity(intent);

return true;

default:
return super.onOptionsItemSelected(item);
}
}

  下一步, 增加新方法, saveTea(), 來保存茶葉信息。saveTea 首先從界面上讀取茶葉的名稱和用戶所選的泡茶時間,如果這些輸入數據都能通過驗證,就將這些數據保存到數據庫中:

 
// src/com/example/brewclock/AddTeaActivity.java
public boolean saveTea() {
// Read values from the interface
String teaNameText = teaName.getText().toString();
int brewTimeValue = brewTimeSeekBar.getProgress() + 1;

// Validate a name has been entered for the tea
if(teaNameText.length() < 2) {
AlertDialog.Builder dialog
= new AlertDialog.Builder(this);
dialog.setTitle(R.string.invalid_tea_title);
dialog.setMessage(R.string.invalid_tea_no_name);
dialog.show();


return false;
}


// The tea is valid, so connect to the tea database and insert the tea
TeaData teaData = new TeaData(this);
teaData.insert(teaNameText, brewTimeValue);
teaData.close();


return true;
}

  大段的代碼,讓我們過一遍這段代碼的邏輯。

  首先,我們從文本框中讀取茶葉名稱,從SeekBar讀取泡茶時間(記著讀的時間要加1以保證時間在1到10分鐘之內)。下一步,我們驗證茶葉名大于等于2個字符(這是非常簡單的驗證,如果想做更復雜的驗證,那么就使用正則表達式吧)。

  如果茶葉名稱非法,我們需要讓用戶知道。我們使用Android提供的工具類,AlertDialog.Biulder類,這個類給我們提供了一個快捷創建和顯示模態窗口的方法。在設置完標題和錯誤信息后,通過調用show方法來顯示對話框。這個對話框是模態的modal,因此用戶只有按下back按鍵,這個對話框才會關閉。在這時,我們不想保存任何數據,所以我們的方法返回了false。

  如果茶名稱合法,我們通過TeaData類創建一個到茶葉數據庫的臨時連接。這里又一次的顯示出把數據庫訪問抽象成一個獨立文件的好處:你可以從任何地方完成對數據庫(譯者注:其實應該是對TeaData 類)的訪問。

  當調用完teaData.insert() 來增加記錄到數據庫后,我們不再需要數據庫連接,因此在我們返回成功前,我們關閉了連接。

  在模擬器中運行你的程序,按下“Menu”按鍵,點擊屏幕上的“Add Tea”。試圖通過在此按下“Menu”和點擊屏幕的 “Save Tea.”來保存空茶葉名的茶葉數據。由于是沒有茶葉名,一條錯誤消息將出現在你的面前:

  下一步,試著鍵入你的茶葉名,并選擇合適的泡茶時間,再次從菜單選擇 “Save Tea” 。這一次,你將不在看到錯誤的消息。事實上,你什么都看消息不到。

  改進用戶體驗

  這樣做不是一個很好的用戶體驗,用戶不能知道他的茶葉是否已經成功地保存了。事實上,用戶只有從“Add Tea”界面返回,去茶葉列表中查看這一個辦法來檢查他的是否成功的被保存。這樣的做法不好,讓用戶知道他們的茶葉數據被成功地保存會是更好的一種方式。在茶葉數據被成功保存后,讓我們在屏幕上顯示一條成功信息。

  我們要一條被動的非模態化的信息,因此AlertDialog這次就不能滿足我們的需求了。下面我們將要使用另外一個Android的非常流行的特性,Toast。

  Toast 在接近屏幕的下方顯示一條消息,但是并不會終止用戶的操作。Toast經常用于做非重要的的提醒和狀態更新。.

  在strings.xml 資源文件中新增一個字符串。注意字符串中的%s。我們在下一步中將保存的茶葉名字結合到這個字符串來顯示信息。

 
<!-- res/values/strings.xml -->
<string name="save_tea_success">%s tea has been saved.</string>

  注意,在onOptionsItemSelected 代碼中進行修改,當saveTea返回真時,創建并顯示一條彈出式的Toast。第二參數getString()用來連接茶葉名稱到Toast信息中。最后,我們需要將茶葉名稱清楚,以便用戶可以快速增加更多的新茶。

 
// src/com/example/brewclock/AddTeaActivity.java
//
switch(item.getItemId()) {
case R.id.save_tea:
if(saveTea()) {
Toast.makeText(
this, getString(R.string.save_tea_success, teaName.getText().toString()), Toast.LENGTH_SHORT).show();
teaName.setText(
"");
}

//

  現在,重新運行應用程序,并增加和保存一些新茶葉。你將會看到彈出式的Toast并讓你知道你的茶葉信息已經被保存成功。getString()方法用于連接存在XML文件中的String和茶葉名稱,并將%s替換成茶葉的名稱。

  按下“Back”按鍵,返回應用程序的主屏幕,點擊茶葉spinner。你新增的在數據庫中的茶葉已近可以顯示在spinner的選項中!

  用戶首選項

  現在BrewClock已經完成了所有的功能。用戶可以增加他們喜愛的茶葉和各自不同的泡茶時間到數據庫中,并且他們可以快速的從選擇他們并開始泡上一杯新茶。任何新增的茶葉信息都被保存在數據庫中,因此,即使你退出你的程序,這些茶葉信息在你下次啟動程序時仍然可以從spinner列表中找到。

  當你重啟BrewClock的時候,有一件事你必須注意,就是泡茶計數被清為了0。這使得跟蹤我們每天喝了多少茶(一條重要的數據)變得困難。作為最后一個練習,讓我們將泡茶計數保存在我們設備上。

  我們將不通過增加茶葉數據庫的表來完成這個功能,我們將使用Android的“共享首選項Shared Preferences”,一個Android提供給你應用程序用于存儲簡單數據的數據庫(字符串,數字,等等)。例如,優秀的最高分和用戶首選項等(譯者注:非常類似Windows下的注冊表)。

  我們首先在BrewClockActivity.java 中增加一堆常量。這些常量用于存放你的共享首選項的名稱。我們將使用鍵的名稱來訪問泡茶計數。Android負責保存和持久化我們的共享首選項文件。

 
// src/com/example/brewclock/BrewClockActivity.java

protected static final String SHARED_PREFS_NAME = "brew_count_preferences";

protected static final String BREW_COUNT_SHARED_PREF = "brew_count";

  下一步,為了我們能在用戶首選項中讀寫泡茶計數,而不是直接的依賴于代碼中的初始值,我們將在代碼中做一些修改。在BrewClockActivity 的 onCreate 方法中我們將就該setBrewCount附件的代碼:

 
// src/com/example/brewclock/BrewClockActivity.java
public void onCreate() {
//

// Set the initial brew values
SharedPreferences sharedPreferences = getSharedPreferences(SHARED_PREFS_NAME, MODE_PRIVATE);
brewCount
= sharedPreferences.getInt(BREW_COUNT_SHARED_PREF, 0);
setBrewCount(brewCount);


//
}

  這里我們將以使用SharedPreference來獲取應用程序的共享首選項的實例,并希望得到brew_count鍵值的值(通過我們之前定義的BREW_COUNT_SHARED_PREF常量來標示)。如果值能獲取,這個值將返回給應用程序,如果沒有我們使用getInt的第二參數作為默認值返回(在教程中為0)。

  現在我們取得存儲的泡茶計數值,我們需要確保每當泡茶計數更新的時候,這個值能寫回到共享首選項中。

  BrewClockActivity的setBrewCount中增加下面的代碼:

 
// src/com/example/brewclock/BrewClockActivity.java
public void setBrewCount(int count) {
brewCount
= count;
brewCountLabel.setText(String.valueOf(brewCount));


// Update the brewCount and write the value to the shared preferences.
SharedPreferences.Editor editor = getSharedPreferences(SHARED_PREFS_NAME, MODE_PRIVATE).edit();
editor.putInt(BREW_COUNT_SHARED_PREF, brewCount);
editor.commit();
}

  共享首選項不能直接地保存。我們需要使用Android的SharedPreferences.Editor類。調用SharedPreferences的edit方法,返回一個editor實例,這個實例用來保存我們的首選項值。我們只要調用editor實例的commit方法就可以將值保存到共享首選項中。

  我們應用程序的所有代碼都已完成,現在讓我們測試一下我們的程序!

  在模擬器中運行應用程序,定一個泡茶時間(這真是一個良好的借口去泡一杯你自己愛喝的茶哦)并退出應用程序,試著運行模擬器上的安裝的其他應用程序確保BrewClock被終止。記住,除非這個應用程序已經不在內存中,否則Android不會終止一個Activity。

  當你下一次運行你的應用程序時,你將看見之前的泡茶計數已經被維護了。

  總結

  恭喜!你已經完成了這個應用的程序的所有開發工作,并使用了Android SDK中的數個核心組件。在本教程中,你從中學到了:

  • 創建一個簡單的SQLite數據庫,并保存你的數據;
  • 使用Android的數據庫類和編寫客戶化類抽象數據訪問;
  • 在你的應用程序中增加選項菜單。;
  • 在你應用程序中創建并注冊新Activity并使用Intent將他們綁定成一組界面;
  • 使用內建的“共享首選項”數據庫來保存和提取簡單用戶數據。

  無論你要開發神馬樣類型的應用程序,數據存儲和持久化是一個重要的主題。從工具程序和業務工具到3-D游戲,幾乎每個應用程序都需要使用到Android提供的數據工具類。

  Activities

  雖然BrewClock現在在某方面來說已經是個功能完善的應用程序了。但是我們仍然可以在增加一些功能以改進用戶體驗。例如你可以使用下面的方法來改進你的應用程序:

  • 在保存茶葉的時候檢查是否存在茶葉名稱重名;
  • 增加一個菜單選項以將泡茶統計清0;
  • 在共享首選項中保存最后所選的泡茶名稱和時間以便程序重啟時有一個有意義的默認值;
  • 增加用戶從茶葉數據庫中刪除記錄的選項。

  在GitHub庫 可以獲取到所有的源代碼,庫中的未來的分支包含著Activitiy的解決方案 你可以通過切換你的本地代碼拷貝到tutorial_2分支,下載這個開發教程源代碼:

 
$ git clone git://github.com/cblunt/BrewClock.git

$ cd BrewClock

$ git checkout tutorial_2

  我希望你喜歡這個教程,希望這個教程能幫助你設計和開發更棒的Android應用程序。請通過在下面的回復讓我知道你的建議和意見,當然我也歡迎你將你建議寫在email中并發送給我。

  感謝Anselm的建議和反饋!

4
0
 
標簽:Android Eclipse
 
 

文章列表

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()