劉關張三人在茅房外聽孔明講述了Android各界面控件的使用方法。等孔明好不容易講完的時候,不覺之間天已大亮。
劉備好奇道:“諸葛先生一直在茅房裏,難道這麼一直上廁所不會累的嗎?”
孔明說:“嘿嘿,我特意我的茅坑上加了個掏空的椅子改裝成了坐式的,茅房裏電視冰箱廚具裝飾品應有盡有,進茅房之前我還沏了壺茶,煮了碗拉麪,別說待上一個晚上,就是在這裏待上幾天我都不會累!”
劉備感嘆道:“看來先生真是一位特別講究如廁情趣的男人!剛纔我聽先生講解時聞到一陣清香,我還以爲大牛如先生這般境界,如廁的味道都不比常人,現在想想原來是拉麪味……真是想不到一個簡單的茅房竟然被諸葛先生改造的如此奢華!”
孔明說:“嘿嘿,其實方法很簡單,我就是把茅廁挪到臥室裏了……好了,不要拍沒有意義的馬屁了,我能教你們的都已經教給你們了,沒什麼事的話你們就散了吧。”
劉備急道:“我等三人遠道而來探訪先生就是想向先生求教Android開發之道,如今還有很多不會的,還望先生指教哇!”
張飛問道:“大哥?我們不就走了50米嗎?爲啥是遠道而來?”
孔明不耐煩道:“不管你們近道還是遠道,現在天都亮了,我也到點該睡覺了呀!”說完就傳來了陣陣呼嚕聲,也不知是真睡還是假睡着了。
張飛說:“這廝好沒禮貌,這半天連個面都不露,倒頭就睡。大哥,現在怎麼辦?”
劉備無助的看了看關羽,關羽捋了捋鬍子,說道:“做開發,人品很重要!我們不如就在此休息一下,等諸葛先生醒了我們再請教不遲。”
劉備讚道:“樓上正解!”
於是三人席地而睡,抱作一團。
三人醒過來的時候已是晚上。茅房裏傳來孔明的聲音:“醒了?”
劉備奇道:“先生真乃神人,爲何知道我們醒了?”
孔明說:“唉,本來這個世界充滿了呼嚕聲,後來這個世界安靜了,我就知道你們醒了……醒了就趕緊走吧,吵得我心情好煩躁,上個廁所都不得安寧”
劉備說:“前面先生介紹了Android的界面控件,我們受益匪淺,不過光會這些控件使用好像遠遠不夠啊,聽說Android的Activity異常重要,求先生再指點一二!”
孔明無奈道:“唉,沒辦法,看你們這麼沉迷,那我再給你們透露一下Android的Activity開發的小祕密,聽完了可就得乖乖給我走人哈!”
劉備一副陰謀得逞的表情說道:“一定一定!”
Activity是Android的MVC架構中的Controller,即控制器,是整個架構的關鍵。因此學好Activity非常地重要,前面的幾章我們在示例工程的時候都有創建Activity,但是這章我們來詳細的介紹一下Activity。
Activity是Android程序的四大組件之一,Activity是Android程序的表示層,我們可以簡單地把程序的每一個顯示屏幕理解成一個Activity,學過WEB開發的讀者,可以把Activity理解成網頁中的一個JSP文件,或者你可以把它理解成一個Windows的窗口,它是應用程序與用戶發生交互的地方。
孔明:Android四大基本組件分別是Activity(活動),Service(服務),Content Provider(內容提供者),BroadcastReceiver(廣播接收器)。 |
一個應用程序通常都包含一個或多個Activity。Activity之間的相互協作構成了整個應用程序的完整存在。Activity主要有以下兩個非常重要的功能:
l 顯示指定的視圖組件
通過Activity的setContentView()方法將xml佈局文件或視圖組件設置爲Activity的佈局。
l 接收與分發用戶事件
Activity接收用戶與應用程序交互產生的點擊,觸摸等事件,接收到事件之後分發給相應的視圖組件,由各組件的監聽器進行處理。
開發者在編寫自己的應用界面時往往先需要繼承Activity類,重寫相應的方法從而完成自定義的功能。在之前的章節中,我們用到的Activity都重寫過了onCreate()方法,此方法是在Activity創建的時候被調用的,因此我們可以把初始化的操作放到這個方法裏面執行,除此之外,Activity還有許多方法,下面幾節我們來一一介紹!
Activity是Android API中的類,是Context類的子類,它是Android的四大組件之一,是構建Android應用的基石。Android手機的一切活動,包括打電話,玩遊戲等都與Activity離不開關係。
1.2.1. Activity生命週期
在Android中,系統會創建Activity的棧來管理Activity。棧的特性是後進先出,因此可以用來恢復上一個Activity,也就是當用戶按返回鍵時在系統中就會進行一個彈出操作,來展示用戶之前的Activity。Activity擁有四種基本狀態,分別是:
Active/Runing即活動狀態,一個新 Activity 啓動入棧後,它在屏幕最前端,處於棧的最頂端,此時它處於可見並可和用戶交互的激活狀態。
Paused 即處於運行但失去焦點的狀態,是一種暫停的狀態。當 Activity 被一個透明的對話框或者 對話框樣式的 Activity 覆蓋時,此時它依然與窗口管理器保持連接,系統繼續維護其內部狀態,所以它仍然可見,但它已經失去了焦點故不可與用戶交互。
Stopped即停止狀態,當 Activity 被另外一個 Activity 覆蓋、失去焦點並不可見時,處於 Stopped狀態。
Killed即結束狀態,Activity 被系統殺死回收或者沒有被啓動時處於 Killed狀態。
Activity的生命週期是由系統來控制的而不是Activity本身,與Activity的生命週期有關的方法包括:onCreate(),onStart(),onResume(),onPause(),onStop(),onDestroy(),onRestart()。從名字上可以看出以下6種方法是一一對應的,onCreate()創建與onDestroy()銷燬;onStart()可見與onStop()不可見;onResume()恢復(即獲取焦點)與onPause()暫停。onRestart()是當Activity被onStop()後,但是沒有被onDestroy()銷燬,再次啓動此Activity時調用;如果被onDestroy()了,則是調用onCreate()方法。
Activity的生命週期圖如6-1所示:
onRestart() |
onDestory() |
onStart() |
onCreate() |
onResume() |
onStop() |
onPause() |
圖6-1 Activity的生命週期圖
當Activity第一次被創建的時候,onCreate()方法被調用,再執行完初始化操作之後界面出現時onStart()方法被調用,此方法結束之後Activity進入到Paused狀態,待執行完onResume()方法之後,Activity進入到Active/Running狀態,當Activity被暫停,調用onPause()方法之後,Activity進入到Paused狀態,當Activity 被另外一個 Activity 覆蓋、失去焦點並不可見時,onStop()方法被調用,Activity進入到Stopped狀態,當Activity被系統殺死回收時,onDestory()方法被調用,Activity處於Killed即結束狀態。
我們再通過一個比喻來看Activity的生命週期。我們把Activity比作一本書,我們要看書,首先從書架上取出書(onCreate()),然後放到桌上(onStart()),接着打開書(onResume()),這樣我們就可以看書並可以在書本上寫字了。如果這時候我們要啓動另一個Activity,也就是要看另一本書,首先我們放下手中的筆或者說合上書(onPause()),然後從書架上拿下另一本書(書2:onCreate()),然後把書本2放到桌上並打開(書2:onStart()、onResume())。如果書本1被書本2完全蓋住了,即不可見了,就調用書本1的onStop();而如果書本2較小,沒有完全蓋住書本1,則不會調用。我們還可以把書本1放回書架上,即onDestroy()。
onCreate()方法在初始化時被調用,因此最適合重寫這個方法來初始化開發者需要的內容;onStart()當界面出現時被調用,onResume()是Activity獲得焦點時被調用,此後Activity進入到Active狀態,因此這個方法裏面最適合來寫需要進行的一些恢復操作,onPause()最適合進行一些保存數據的操作,因爲此後系統隨時都有可能銷燬Activity,onStop()和onDestroy()方法不一定會被調用,onStop()也會寫一些釋放界面資源的方法,onDestroy()方法的內容往往是釋放佔用的資源,這也是保存數據的最後關卡,過了這一步,數據就無法保存。
在前面已經提到過了,系統使用棧來管理Activity。正在運行的Activity 處在在棧的最頂端,它是運行狀態的,當有新Activity進入屏幕最上端時,原來的Activity就會被壓入第二層,如果它的屏幕沒有被完全遮蓋,那麼他處於Paused狀態,如果它被遮蓋那麼他處於Stopped狀態。當然不管Activity處於哪一層,都可能在系統覺得資源不足時被強行關閉,關閉時在棧底的程序最先被關閉。
1.2.2. Activity配置詳解
系統是如何得知開發者創建了一個Activity的呢?在Android中,系統提供了一個AndroidManifest.xml文件,在這個文件中,開發者需要爲創建的每一個Activity進行配置,這樣系統纔會找到開發者定義的Activity。
在AndroidManifest.xml文件中,在application標籤下面開發者必須爲每一個自定義的Activity創建一個activity標籤,否則,系統將報錯。我們通過一個具體的示例來看一下Activity的xml文件配置。
代碼清單6-1 AndroidManifest.xml:
<activity
android:name=".DialogActivity"
android:label="@string/title_activity_main">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
android:name是指Activity的名字,這個名字必須和開發者定義的類名相同。android:label是指Activity的標籤,這個可以由開發者自由指定,可以是string.xml文件中的一個字符串。<intent-filter>標籤告訴系統該Activity將如何被調用。<actionandroid:name="android.intent.action.MAIN"/>是指該Activity作爲應用程序的入口,<category android:name="android.intent.category.LAUNCHER"/>是指由系統來啓動這個Activity,當用戶點擊應用圖標時會啓動這個Activity。
Activity還有許多其他的配置,我們列表如下:
屬性名 |
屬性值 |
android:screenOrientation |
這個屬性用於設置Activity在設備上顯示的方向。默認是由系統來選擇,landscape是橫向的,portrait是豎向的。 |
android:stateNotNeeded |
這個屬性用於設置在沒有保存Activity狀態的情況下,Activity能否在被銷燬後成功的重啓。如果設置爲true,則不引用Activity之前的狀態就能夠被重啓,如果設置爲false,重啓Activity時,則需要它之前的狀態。默認值是false。 |
android:process |
這個屬性用於設置Activity運行時特定的進程名字。通常,應用程序的所有組件都運行在爲這個程序所創建的一個默認的進程中。它跟應用程序的包有相同的名字。<application>元素的process屬性能夠給所有的組件設置一個不同的默認值。但是每個組件都能夠覆蓋這個默認設置,允許把應用程序分離到多個進程中。 |
android:permission |
這個屬性用於設定啓動Activity的客戶端或者是響應一個Intent對象的請求所必須要有的權限。如果startActivity()方法或startActivityForResult()方法的調用者沒有被授予指定的權限,那麼它的Intent對象就不會發送給對應的Activity。 |
android:noHistory |
這個屬性用於設置在用戶離開該Activity,並且它在屏幕上不再可見的時候,它是否應該從Activity的堆棧被刪除。如果設置了true,則要刪除,否則不刪除。默認值是false。 |
android:multiprocess |
這個屬性用於設置Activity的實例能否被加載到與啓動它的那個組件所在的進程中,如果設置爲true,則可以,否則不可以。默認值是false。 |
android:theme |
activity的樣式主題,如果沒有設置,則從屬於應用程序的樣式主題。 |
孔明:你們聽好了!Activity的屬性配置非常複雜,使用恰當時往往會達到一些意象不到的效果,當然這需要在平時的編程中多積累經驗纔行啊!
|
Activity之間的交互非常重要,在Android中,一個應用程序往往有很多的Activity,這些Activity之間的相互協作才能構成一個完美的程序。本節我們來看下Activity之間到底是如何交互的,數據是如何傳遞的。
1.3.1. 使用Intent傳遞數據
通過Intent來傳遞數據是在Activity之間傳遞數據的一種常用手段,使用起來非常方便,只需要做的是在使用Intent啓動另一個Activity的時候使用Intent類的方法爲Intent實例增加數據,然後在目標Activity裏面將數據接收即可。下面我們來講一下如何創建Intent以及如何使用Intent來傳遞數據。
Intent的創建非常簡單,代碼示例如下:
Intent it = new Intent(MainActivity.this, SlaveActivity.class);
代碼裏面的第一個參數是創建Intent的Activity,第二個參數是接收Intent實例的Activity,接下來我們使用Intent的putExtra()方法來添加要傳遞的數據。putExtra()方法有許多重構的方法,可以滿足我們添加各種類型的參數,我們以字符串類型的參數爲例,添加數據的代碼爲如下所示:
it.putExtra("send_data","從MainActivity傳過來的數據");
第一個字符串相當於一個鍵值對中的鍵,也就是key,也就是在目的Activity中取數據需要用到的一個值。第二個字符串相對於鍵值對中的值,是我們要實際傳遞的值,如果我們傳遞的值不是字符串類型,我們可以使用putExtra()方法的重構方法來傳遞不同類型的值。
上面講到了如何在源Activity裏面將數據添加到Intent裏面,而數據是如何在目的Activity裏面接收呢,實際上也是非常簡單的。代碼如下:
Intent intent =getIntent();
String text =intent.getStringExtra("send_data");
首先,需要獲取Intent的實例,然後再根據發送的鍵值來獲取數據即可,比如上面的發送Activity中以“send_data”作爲鍵值,接收類中,就要以這個鍵值來獲取發送的數據。完整的代碼如下所示:
代碼清單6-2 MainActivity.java:
/**
* Activity展示類
* @author孔明
*/
public class MainActivityextends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent it = new Intent(MainActivity.this,SlaveActivity.class);
it.putExtra("send_data","從MainActivity傳過來的數據");
startActivity(it);
}
}
上面是發送Activity的類,我們只是在onCreate方法裏面創建了一個Intent,然後加入要發送的數據,啓動一個名爲SlaveActivity的Activity。代碼如下:
代碼清單6-3 SlaveActivity.java:
/**
* Activity數據的接收類
* @author孔明
*/
public class SlaveActivityextends Activity {
private TextView tv1;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.slave);
tv1 = (TextView)findViewById(R.id.tv1);
Intent intent = getIntent();
String text =intent.getStringExtra("send_data");
tv1.setText(text);
}
}
在上面的代碼中,我們將接收到的數據顯示到一個TextView中,在實際的編程中,用戶可以根據需要調整數據的使用方式。上面兩個Activity中用的佈局文件如下所示:
代碼清單6-4 activity_main.xml:
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:padding="@dimen/padding_medium"
android:text="@string/hello_world"
tools:context=".MainActivity" />
</RelativeLayout>
代碼清單6-5 slave.xml:
<?xmlversion="1.0"encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/tv1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world" />
</LinearLayout>
運行程序之後,MainActivity的界面將不會顯示,直接跳到SlaveActivity的界面,如圖6-2所示:
圖6-2 Intent傳遞數據示意圖
孔明:MainActivity界面沒有顯示是因爲創建和啓動SlaveActivity的動作是在onCreate方法裏面執行的,所以MainActivity還沒來得及顯示就跳轉到SlaveActivity了,想必你一定知道吧! |
使用靜態變量來傳遞數據,看到這個題目,Java程序員們也就大概猜到了這個傳遞數據的方式是如何進行的,因爲在Java中,我們會經常用到這種方法來傳遞數據。大體上說來就是在目的Activity中創建一個靜態變量,然後在源Activity將其賦值改變即可達到傳遞數據的目的,比如我們在SlaveActivity中建立如下一個靜態變量:
代碼清單6-6 SlaveActivity.java:
/**
* Activity數據的接收類
* @author孔明
*/
public class SlaveActivityextends Activity {
staticString SLAVE = null;
privateTextView tv2;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.slave);
tv2 = (TextView)findViewById(R.id.tv2);
tv2.setText(SLAVE);
}
}
然後我們在發送Activity中將SLAVE這個靜態變量的值改變即可,代碼如下:
代碼清單6-7 MainActivity.java:
/**
* Activity展示類
* @author孔明
*/
public class MainActivityextends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SlaveActivity.SLAVE= "靜態變量傳遞的數據";
Intent it = new Intent(MainActivity.this,SlaveActivity.class);
startActivity(it);
}
}
上面的代碼中,在使用Intent啓動SlaveActivity之前,我們給SlaveActivity.SLAVE,這樣SlaveActivity在接收到新值之後就可以將其顯示出來了,運行程序,界面如圖6-2所示:
圖6-2 靜態變量傳遞數據示意圖
使用剪貼板來傳遞數據的功能類似於Windows上的“複製+粘貼”的功能,相信大家對這個功能都非常的熟悉。
那麼按照上述的原理呢,我們需要在發送類裏面做的工作是獲取Android系統的剪貼板,然後將數據添加到剪貼板上去,在接收類裏面要做的工作是獲取剪貼板,然後獲取剪貼板上的數據。代碼如下:
代碼清單6-8 MainActivity.java:
/**
* Activity展示類
* @author孔明
*/
public class MainActivityextends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ClipboardManager cm =
(ClipboardManager)getSystemService(CLIPBOARD_SERVICE);
cm.setText("剪貼板傳遞的數據");
Intent it = new Intent(MainActivity.this,SlaveActivity.class);
startActivity(it);
}
}
在上面的代碼中,我們使用getSystemService()方法來獲取了剪貼板實例,然後將數據設置到剪貼板上去,最後啓動SlaveActivity。
代碼清單6-9 SlaveActivity.java:
/**
* Activity數據的接收類
* @author孔明
*/
public class SlaveActivityextends Activity {
private TextView tv4;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.slave);
tv4 = (TextView)findViewById(R.id.tv4);
ClipboardManager cm =
(ClipboardManager)getSystemService(CLIPBOARD_SERVICE);
tv4.setText(cm.getText());
}
}
上面的代碼中,我們首先獲取了剪貼板的實例,然後獲取剪貼板上的數據,最後將數據顯示到TextView上,界面如圖6-3所示:
圖6-3 剪貼板傳遞數據示意圖
1.3.4. 使用Application傳遞數據
使用Application來傳遞數據更具有一般性。我們在這個過程中需要創建自定義的Application類,然後在自定義的Application類裏面增加我們需要傳遞的數據。爲了演示傳遞數據的過程,我們編寫了如下自定義Application類:
代碼清單6-9 MyApp.java:
/**
* 自定義App示例類
* @author孔明
*/
public class MyApp extends Application {
privateString myString;
publicString getMyString() {
returnmyString;
}
public void setMyString(String myString) {
this.myString= myString;
}
}
上面代碼中的myString域就是我們要傳遞的數據,創建完這個類之後呢,我們同時要修改一下AndroidMenifest.xml文件,在<application>中加入一個說明,表示程序中使用的Application類是我們創建的MyApp,而不是系統自己的Application類型,代碼如下:
android:name=".MyApp"
接下來的工作是在數據源Activity裏面將數據添加到MyApp裏面,具體來看下如何實現:
代碼清單6-10 MainActivity.java:
/**
* Activity展示類
* @author孔明
*/
public class MainActivityextends Activity {
@Override
public voidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MyApp myApp =(MyApp)getApplicationContext();
myApp.setMyString("全局變量傳遞的數據");
Intent it = new Intent(MainActivity.this,SlaveActivity.class);
startActivity(it);
}
}
在上面的代碼中,我們在onCreate方法裏面通過getApplicationContext()方法來獲取一個Application,通過強制類型轉換成我們定義的MyApp類型,然後調用MyApp的setMyString方法將數據保存到MyApp中,在目標Activity中,我們只需要獲取到MyApp,然後用相應的get方法就可以獲取數據了,代碼如下:
代碼清單6-11 SlaveActivity.java:
/**
* Activity數據的接收類
* @author孔明
*/
public class SlaveActivityextends Activity {
privateTextView tv3;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.slave);
tv3 = (TextView)findViewById(R.id.tv3);
MyApp myApp =(MyApp)getApplicationContext();
tv3.setText(myApp.getMyString());
}
}
運行程序之後,截圖如圖6-4所示:
圖6-4 全局變量傳遞數據示意圖
1.3.5. 返回數據到前一個Activity
返回數據到前一個Activity也是Activity交互中的一個非常重要的環節。在本節,我們要講一個Android系統本身所具有的一個返回數據的方式。
要想有數據返回到一個Activity,那麼我們在這個Activity啓動另外一個Activity的時候要調用startActivityForResult方法,而不是startActivity。startActivityForResult方法有兩個參數,第一個參數是Intent,第二個是一個int類型,代表一個需求碼,即REQUEST_CODE。那麼具體如何實現呢?我們來看下面代碼:
代碼清單6-12 MainActivity.java:
/**
* Activity展示類
* @author孔明
*/
public class MainActivityextends Activity {
staticintREQUEST_CODE = 0;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent it = new Intent(MainActivity.this,SlaveActivity.class);
startActivityForResult(it, REQUEST_CODE);
}
@Override
protected void onActivityResult(intrequestCode, int resultCode,Intent data) {
super.onActivityResult(requestCode,resultCode, data);
if (requestCode == REQUEST_CODE) {
String text =data.getStringExtra("result_back");
Toast.makeText(this,text, Toast.LENGTH_LONG).show();
}
}
}
REQUEST_CODE是我們自行定義的,如果這個值是大於0的,那麼它將在目的Activity退出的時候返回到前一個Activity的onActivityResult方法中來,因此上面的代碼中,我們重寫了這個方法,而返回的數據是封裝到一個Intent實例中的,因此只要調用Intent相應的get方法即可獲得返回的數據。
在接收的Activity裏面顯然需要我們將要返回的數據封裝到一個Intent實例裏面去了,具體實現如下所示:
代碼清單6-13 SlaveActivity.java:
/**
* Activity數據的接收類
* @author關雲長
*/
public class SlaveActivityextends Activity {
private TextView tv3;
@Override
public voidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.slave);
Intent intent = getIntent();
intent.putExtra("result_back", "從SlaveActivity返回的數據");
setResult(RESULT_OK, intent);
}
}
在接收類的onCreate方法裏面獲取Intent實例,並通過putExtra方法將數據設置到Intent中,最後通過調用setResult(RESULT_OK,intent)來設置將會返回的結果,其中RESULT_OK表示操作成功。因此當我們在SlaveActivity界面的時候,按下返回鍵或者後退鍵就會看到從SlaveActivity中返回的數據了,如界面6-5所示:
圖6-5 返回數據到前一個Activity示意圖
上面兩節,我們介紹了Activity的基本原理,使用方法以及如何交互,實際上在Android系統中有一些Activity的子類。這些子類除了有Activity的功能之外還有一些其他的功能,使得在使用這些Activity子類來解決某一類問題的時候特別方便。比如下面第一小節要介紹的ListActivity在解決含有ListView佈局的Activity時就非常地好用。
ListActivity是Android中Activity的子類,因此擁有Activity的所有特性,同時,ListActivity可以看做是一個Activity與一個ListView的結合體,因此我們在使用ListActivity的時候都不需要去設置ListActivity的佈局了,因爲默認是一個ListView。而且這個ListView的id是android:list,不能隨便取名字的。因此在使用ListActivity的時候,我們只需要給ListActivity設置一個適配器以及定義事件監聽器即可。我們來寫一個簡單的ListActivity如下所示:
代碼清單6-14 SlaveActivity.java:
/**
* ListActivity展示類
* @author孔明
*/
public class ListActivityTestextends ListActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 創建ListView要用的ArrayList
List<String> items = fillArray();
// 創建並設置適配器
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, items);
setListAdapter(adapter);
}
// 創建ListView要用的ArrayList
privateList<String> fillArray() {
List<String> items = newArrayList<String>();
items.add("星期一");
items.add("星期二");
items.add("星期三");
items.add("星期四");
items.add("星期五");
items.add("星期六");
items.add("星期日");
returnitems;
}
// 設置點擊事件
@Override
protected void onListItemClick(ListView l, View v, int position, longid) {
Toast.makeText(this,l.getItemAtPosition(position).toString(),
Toast.LENGTH_LONG).show();
}
}
在上面的代碼中,我們在onCreate方法裏面創建了一個ArrayList來填充數據,並創建了一個Adapter,並將其設置爲ListView的適配器。onListItemClick是列表點擊事件監聽器方法,我們在這個方法中通過Toast顯示點擊的星期幾。運行程序,界面如圖6-6所示:
圖6-6 ListActivity示意圖
在開發Android上的應用程序以及遊戲的時候經常會遇到一些需要進行設置的界面,比如是否靜音,鈴聲選項等等。Android提供了PreferenceActivity這個類來完成這一類的工作,PreferenceActivity可以看做是Preference與Activity的結合體。
首先,我們來創建一個PreferenceActivity,這一步驟非常簡單,我們新建一個類,命名爲PreferenceActivityTest並使這個類繼承自PreferenceActivity,那麼一個PreferenceActivity就創建好了,代碼如下:
代碼清單6-15 PreferenceActivityTest.java:
/**
* PreferenceActivity展示類
* @author孔明
*/
public class PreferenceActivityTestextends PreferenceActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 添加配置界面使用的佈局文件
addPreferencesFromResource(R.xml.pref);
}
}
需要注意的是,我們爲PreferenceActivity指定佈局文件的時候,這個佈局文件一定要在res/xml文件夾中,並且要使用addPreferencesFromResource方法,而不是setContentView方法。
然後,我們再來看一下PreferenceActivity的xml界面文件如何定義,pref.xml的內容如下:
代碼清單6-16 pref.xml:
<?xmlversion="1.0"encoding="utf-8"?>
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 分組 -->
<PreferenceCategory android:title="短信助手">
<!-- checkbox 控件 -->
<CheckBoxPreference
android:key="smsSilence"
android:title="靜音"
android:summaryOn="開啓"
android:summaryOff="關閉"
android:defaultValue="false" />
<PreferenceScreenandroid:title="更多選項">
<CheckBoxPreference
android:key="sms1"
android:title="系統默認鈴聲"
android:summaryOn="開啓"
android:summaryOff="關閉"
android:defaultValue="true" />
<CheckBoxPreference
android:key="sms2"
android:title="自定義鈴聲"
android:summaryOn="使用"
android:summaryOff="停用"
android:defaultValue="true" />
<!-- List 控件 -->
<ListPreference
android:key="list"
android:title="ListPreference"
android:summary="手機品牌"
android:dialogTitle="請選擇品牌"
android:entries="@array/entries_list_preference"
android:entryValues="@array/entriesvalue_list_preference"/>
<!-- edittext控件 -->
<EditTextPreference
android:key="et"
android:title="EditTextPreference"
android:summary="點擊輸入"
android:dialogTitle="輸入你最喜愛的動物的名稱"
android:defaultValue="刀疤鴨" />
</PreferenceScreen>
</PreferenceCategory>
<!-- 分組 -->
<PreferenceCategory android:title="鈴聲設置">
<RingtonePreference
android:key="ring"
android:title="老男孩"
android:summary="選擇鈴聲" />
</PreferenceCategory>
</PreferenceScreen>
首先,整個PreferenceActivity的界面根元素必須是PreferenceScreen,在這個根元素下面,我們可以添加許多選項。也就是說PreferenceScreen是可以嵌套的。當我們點擊子PreferenceScreen的時候,將會打開一個新的界面。PreferenceCategory是用來分組的,一個完整的PreferenceCategory標籤就是一組控件的集合。控件有四種,分別是CheckBoxPreference、ListPreference、EditTextPreference、RingtonePreference,分別有不同的功能。CheckBoxPreference是以複選框的形式給用戶選擇,ListPreference是以列表的形式供用戶選擇,EditTextPreference可以讓用戶輸入一些設置,RingtonePreference使得用戶可以選擇鈴聲。
在Android系統中,我們知道配置都是按照鍵值對的形式存放的,因此android:key就指明瞭這個設置的鍵,也就是寫到Android配置文件中的鍵,而實際的值是需要根據用戶的選擇來決定的。
android:title指的是這個配置將要顯示的名稱,android:summaryOn是配置是true時顯示的內容,同理,android:summaryOff是配置爲false時顯示的內容。android:dialogTitle是顯示的對話框的題目。android:defaultValue是默認狀態下顯示的值。在PreferenceActivity裏面有的配置都將放在data/data/項目包名/shared_prefs下,名字爲“包名_preferences.xml”
運行上面的程序,如圖6-7所示:
圖6-7 PreferenceActivity示意圖
當我們點擊更多選項時,一個新的界面會打開,如圖6-8所示:
圖6-8 PreferenceActivity更多選項示意圖
點擊ListPreference以及EditTextPreference時分別會出現如6-9界面:
圖6-9 ListPreference示意圖
圖6-10 EditTextPreference示意圖
孔明:既然設置的信息都被放到配置文件裏面了,那麼以後我們就可以到配置文件裏讀取這些信息。 |
1.5. Application與Activity
相對於Application來說,Activity更爲大家所熟知,這是因爲在編程過程中,很多時候需要用到Activity,而Application出場的機會很少,但是Application卻非常有用。
首先,對於一個Android應用程序,Application有且只有一個,所以它是一個Android系統的單例模式實現的。在不同的Activity當中使用getApplication方法獲得的都是同一個Application對象,而且Application的生命週期是最長的,跟應用的生命週期相同。通常我們不需要指定一個Application,因爲系統會自定幫助我們創建一個,如果我們需要指定一個Application,那麼我們可以繼承Application創建一個子類,並在manifest的application標籤中進行註冊。
其次,對於一個Android應用來說,Application是一個系統組件,一個Application實例中有多個Activity、Service、ContentProvider或Broadcast Receiver,並且Application實例中保存了與應用主題,資源文件內容等全局性的變量。
最後,一個Android應用程序的啓動過程,總是先啓動Application,然後再啓動Application下的某一個Activity。
從以上的不同點來分析,Application可以用於Activity之間的數據傳遞,在這個過程中Application起到了一個全局變量的作用。除此之外呢,Application還可以作爲數據的緩存來使用,比如在一個與網絡相關的應用當中,我們可以將網絡上的數據先保存到位於Application的緩存當中,這樣每次啓動Activity就不用訪問網絡了,直接從Application裏拿過來就可以用了。當然了,我們也可以使用Application來改變應用的主題等全局性的變量。
張飛:如果Activity傳遞的數據是多個鍵值對的話,該如何使用Intent進行傳遞呢?
劉備:這個問題我知道!首先使用bundle來綁定鍵值對,然後使用intent來傳遞bundle,一個鍵對應一個需要傳遞的值。最後通過鍵將bundle中的值取出來。
張飛:大哥,沒看出來,你這都會呀!
劉備:三弟,你倒說說,我啥不會呀!
張飛:那麼大哥,Activity這麼多傳遞數據的方式,到底哪種好呢?
劉備:每一種都有每一種的特點,需要結合程序需要而定,比如Intent傳遞數據的方法特別適合兩個Activity之間傳遞數據。而使用靜態變量或者Application適合在多個Activity之間進行數據交互。
劉備:大哥威武!
張飛:三弟客氣!