作為Android的四大組件之一,活動最先走進我們的視野,其重要性不言而喻,今天就抽出時間來專門對Android活動一探究竟。
什么是活動
活動即Activity,是一種可以包含用戶界面的組件,Android系統中所有我們能看到的內容都在活動里,它主要用于和用戶進行交互。一個應用程序可以包含零個或多個活動,活動與活動之間可以通過Intent來相互聯系,當一個應用程序中至少有一個活動時,那么就一定存在一個主活動,它就是我們剛打開應用程序時所進入到的那個界面,可以在AndroidManifest.xml中進行設置。
創建活動的步驟
- 1.新建一個活動類,繼承自Activity。
- 2.重寫onCreate方法,并在其中通過setContentView()來加載布局文件。
- 布局文件在app/src/main/res/layout中,文件后綴為.xml。
- 如果要在xml文件中定義一個id,則使用@+id/id_name語法;如果要在xml文件中引用一個id,則使用@id/id_name語法。 - 3.活動注冊,所有活動必須在AndroidManifest.xml中注冊,給主活動指定的label不僅會成為標題欄中的內容,還會成為啟動器(Launcher)中應用程序顯示的名稱。在<activity>標簽的內部我們加入了<intent-filter>標簽,并在這個標簽里添加了
<actionandroid:name="android.intent.action.MAIN"/> <category android:name ="android.intent.category.LAUNCHER" />
這兩句聲明,這樣這個活動將成為主活動,在點擊桌面應用程序的時候首先打開的就是這個活動。如果程序中沒有聲明任何主活動,程序仍然正常安裝,但是無法再啟動器中看到或者打開該程序。
隱藏標題欄
有時為了全屏顯示活動或者顯示開發者自定義的標題欄,需要在活動中不顯示標題欄,這時需要如下代碼
requestWindowFeature(Window.FEATURE_NO_TITLE)
這句代碼一定要在setContentView()之前執行,不然會報錯。
在活動中使用Toast
Toast譯為吐司,是一種簡單靈活的提醒方式,可以把一些短小的消息通知給用戶,并在一段時間后消失,并且不會占據任何屏幕空間。
Toast.makeText(KUActivity.this,"在線功能稍后上線,敬請期待!", Toast.LENGTH_LONG).show();
其中,第一個參數Context,第二個參數要提示的內容,第三個參數為顯示Toast的時間長短,其中LENGTH_LONG(長)和LENGTH_SHORT(短),記得別忘了調用show()方法來將Toast顯示出來。
在活動中使用Menu
- 1.在res中新建menu文件夾:res-->new-->Directory,輸入文件名menu,然后就可以在menu文件夾下新建菜單文件了,后綴為.xml。
- 2.菜單文件內容格式參考如下:
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/menu_refresh"
app:showAsAction="always"
android:title="@string/refresh"/>
</menu> - 3.在活動中重寫onCreateOptionsMenu()方法。
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu, menu);//給當前活動創建菜單,inflate方法第一個參數通過哪個資源文件來創建菜單,第二個參數指定我們的菜單項將添加到哪一個Menu對象中
return true;//允許創建的菜單顯示出來
} - 4.響應菜單事件:重寫onOptionsItemSelected()方法。
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId())
{
case R.id.menu_refresh:
{
//如果該菜單項被選中,則在此執行業務邏輯
}
break;
case ....
} - 注:標題欄的右側有個三點符號,這個就是菜單按鈕,點擊該按鈕們就能看到所有的菜單項了,當點擊某菜單項時,會回調到onOptionsItemSelectd()方法中。
銷毀活動
- Activity中提供了一個finish()方法,在活動中調用該方法即可銷毀當前活動。
Intent啟動活動
Intent是android程序中各個組件之間進行交互的重要方式,它不僅可以指明當前組件想要執行的動作,還可以在不同組件之間傳遞數據。Intent一般可被用于啟動活動、啟動服務以及發送廣播等場景。Intent一般分為顯式Intent和隱式Intent。
- 顯式Intent
(1)Intent有多個構造方法,其中最常接觸的是
Intent(Context packageContext, Class<?>cls)
這個構造函數接收兩個參數,第一個參數Context要求提供一個啟動活動的上下文Context,第二個參數Class則是指定想要啟動的目標活動。
(2)Activity類中提供了一個startActivity()方法,這個方法是專門用于啟動活動的,它接收一個Intent參數。
(3)顯式Intent的示例如下:
Intent intent = new Intent(FirstActivity.this,SecondActivity.class);//在FirstActivity上啟動SecondActivity
intent.putExtra("path",path);//把路徑path保存到“path”中,傳遞給SecondActivity
startActivity(intent);//啟動活動SecondActivity - 隱式Intent
隱式Intent并不明確指出我們想要啟動哪一個活動,而是指定了一系列更為抽象的action(動作)和category(種類)等信息。只有<activity>的<intent-filter>下的<action>和<category>中的內容能夠同時匹配上Intent中指定的action和category時,這個活動才能響應該Intent。
android.intent.category.DEFAULT是一種默認的category,在調用startActivity()方法的時候會自動將這個category添加到Intent中。每個Intent中只能指定一個action,但卻能指定多個category。
隱式Intent的用法示例:
(1)在FirstActivity的<activity></activity>中添加action和category。
<activity
android:name=".FirstActivity">
<intent-filter>
<action android:name="com.example.activitytest.ACTION_START" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="com.example.activitytest.MY_CATEGORY" />
</intent-filter>
</activity>
(2)在SecondActivity活動中,指定要啟動的action和category。
locatVideoBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new
Intent("com.example.activitytest.ACTION_START");
intent.add("com.example.activitytest.MY_CATEGORY");
startActivity(intent);
}
});//由于FirstActivity的action和category能和SecondActivity中Intent中指定的一樣,因此即可在SecondActivity中啟動FirstActivity
(3)使用隱式Intent,我們不僅可以啟動自己程序內的活動,還可以啟動其他程序的活動,這使得Android多個應用程序之間的功能共享成為了可能。例如,如果我想在應用程序中展示一個百度網頁,則只需要調用系統的瀏覽器來打開網頁即可:
Btn1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_VIEW);//Android系統內置動作
intent.setData(Uri.parse("http://www.baidu.com"));//將網址解析成一個Uri對象并將其傳遞到Intent中
startActivity(intent);
}
});
Intent傳遞數據
- 向下一個活動傳遞數據
Intent中提供了一系列putExtra()方法的重載,可以把我們想要傳遞的數據暫存在Intent中,啟動了另一個活動后,只需要把這些數據再從Intent中取出就可以了。
(1)在啟動下一個活動時存儲數據
Btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String data = "hello";
Intent intent = new Intent(FirstActivity.this,SecondActivity.class);
intent.putExtra("extra_data",data);
startActivity(intent);
}
});
(2)在下一個活動中取出數據
public class SecondActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
Intent intent = getIntent();
String data = intent.getStringExtra("extra_data");//取出數據
Log.d("SecondActivity",data);
} - 返回數據給上一個活動
Activity中還有一個startActivityForResult(intent, 1)方法也是用于啟動活動的,但這個方法期望在活動銷毀的時候能夠返回一個結果給上一個活動。請求碼只要是一個唯一值就可以了,這里傳入了1。
(1)在FirstActivity中使用startActivityForResult()方法來啟動SecondActivity。
Btn1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(FirstActivity.this,SecondActivity.class);
startActivityForResult(intent,1);
}
});
(2)在SecondActivity中通過Intent將要返回給FirstActivity的數據保存起來。
Btn2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.putExtra("return_data",“hello FirstActivity”);
setResult(RESULT_OK,intent);
finish();//銷毀當前活動SecondActivity
}
});
setResult()方法專門用于向上一個活動返回數據,它接收兩個參數,第一個參數用于向上一個活動返回處理結果,一般只使用RESULT_OK或RESULT_CANCELED這兩個值,第二個參數則是把帶有數據的Intent傳遞回去。
(3)當SecondActivity被銷毀之后會回調上一個活動的onActivityResult(),因此在FirstActivity中重寫該方法。
@Override
protceted void onActivityResult(int requestCode,int
resultCode,Intent data){
switch(requestCode){
case 1:
if(resultCode==RESULT_OK){
String resultData = data.getStringExtra("return_data");
Log.d("FirstActivity",resultData);
}
}
}
(4)如果用戶在SecondActivity中沒有通過點擊按鈕而是通過Back鍵返回到了FirstActivity,則我們可以通過重寫onBackPressed()方法來解決這個問題,即將返回數據的代碼放在此方法中即可。
@Override
public void onBackPressed() {
Intent intent = new Intent();
intent.putExtra("return_data",“hello FirstActivity”);
setResult(RESULT_OK,intent);
finish();//銷毀當前活動SecondActivity
}
活動的生命周期
在了解活動的生命周期之前,我們還需要再弄清楚兩部分概念:返回棧和活動的狀態。
- 返回棧
Android是使用任務(Task)來管理活動的,一個任務就是一組存放在棧里的活動Activity的集合,這個棧也被稱作返回棧(BackStack)。在返回棧中,活動是按照后進先出的規則進出棧的,每當我們按下Back鍵或調用finish()方法去銷毀一個活動時,處于棧頂的活動會出棧。
從上圖可看出,當從Activity1中啟動Activity2時,Activity1被推到棧底,Activity2變成棧頂,同理,Activity3進棧的過程和Activity2是一樣的,如果我們按back按鈕,則Activity3被彈出,Activity2則被推到棧頂,以此類推。當一直按back,返回到主頁面,則所有的activity被全部彈出,則task不復存在。
其實,task有兩種狀態:Foreground前景和Background背景。當處于Background時,所有的activity都是停止的,當處于Foreground時,則其棧頂的Activity可與用戶進行交互。因此Android是一個多任務的系統,不同任務是可以被互相切換的。
- 活動狀態
每個活動在其生命周期中最多可能會有四種狀態:
1)運行狀態:當一個活動位于返回棧的棧頂時,活動處于運行狀態
- 暫停狀態:當一個活動不再處于棧頂位置,但仍然可見時,這時活動就進入了暫停狀態,處于暫停狀態的活動仍然是完全存活著的
停止狀態:當一個活動不再處于棧頂位置,并且完全不可見的時候,就進入了停止狀態。
銷毀狀態:當一個活動從返回棧中移除后就變成了銷毀狀態
- 活動的生存期
完整生存期:活動在onCreate()方法和onDestroy()方法之間所經歷的,就是完整生存期
可見生存期:活動在onStart()方法和onStop()方法之間所經歷的,就是可見生存期。在可見生存期內,活動對于用戶總是可見的,即便有可能無法和用戶進行交互
前臺生存期:活動在onResume()方法和onPause()方法之間所經歷的,就是前臺生存期。在前臺生存期內,活動總是處于運行狀態的,此時的活動是可以和用戶進行相互的
- 活動的生命周期
- 活動被回收前保存臨時數據
試想,活動A啟動了活動B,假設活動B全部覆蓋了活動A,此時的活動A處于停止狀態,若此時系統內存不足將活動A回收了,那么當我們按Back鍵回到活動A時,此時活動A肯定是要執行onCreate的方法的,這也就意味著之前活動A的數據肯定已經找不到了,那么如何來解決這個問題呢?
其實,Activity中還提供了一個onSaveInstanceState()回調方法,這個方法會保證一定在活動被回收之前調用,因此我們可以通過這個方法來解決活動被回收時臨時數據得不到保存到的問題。
(1)通過onSaveInstanceState(Bundle outState)的Bundle參數保存臨時數據。
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState)
String tempData = "something";
outState.putString("data_key",tempData);
}
(2)在onCreate(Bundle saveInstanceState)方法中,將數據從Bundle參數中取出。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(saveInstanceState != null){
String tempData = savedInstanceState.getString("data_key");
Log.d("MainActivity",tempData);
}
}
(3) Intent還可以結合Bundle一起用于傳遞數據的,首先可以把需要傳遞的數據都保存在Bundle對象中,然后再將Bundle對象存放在Intent里。到了目標活動之后先從Intent中取出Bundle,再從Bundle中一一取出數據。
活動的啟動模式
活動的啟動模式一共有四種,分別是standard、singleTop、singleTask、singleInstance,可以在AndroidManifest.xml中通過<activity>標簽android:launchMode屬性來選擇啟動模式。
-
standard
活動默認的啟動模式,在該模式下,每當啟動一個新的活動,它就會在返回棧中入棧,并處于棧頂的位置。對于使用standard模式的活動,系統不會在乎這個活動是否已經在返回棧中存在,每次啟動都會創建該活動的一個新的實例。
-
singleTop
在該模式下,在啟動活動時如果返回棧的棧頂已經是該活動,則認為可以直接使用它,不會再創建新的活動實例,但如果發現棧頂不是該活動,則創建該活動的新的實例,因此在返回棧中可能會存在該活動的多個實例。
-
singleTask
每次啟動該活動時系統首先會在返回棧中檢查是否存在該活動的實例,如果發現已經存在則直接使用該實例,并把在這個活動之上的所有活動統統出棧,如果沒有發現就會創建一個新的活動實例,因此在返回棧中只會存在該活動的一個實例。
-
singleInstance
指定為singleInstance模式的活動會啟用一個新的返回棧來管理這個活動(其實如果singleTask模式指定了不同的taskAffinity,也會啟動一個新的返回棧),這樣做就解決了共享活動實例的問題,因為不管是哪個應用程序來訪問這個活動,都共用的是同一個返回棧。
List的一點小疑惑
我們在活動中會經常看到這句代碼List list = new ArrayList()
?
List是一個接口,而ListArray是一個類,ListArray繼承并實現了List。所以List不能被構造,但可以向上面那樣為List創建一個引用,而ListArray則可以被構造。
那為什么不是List list = new ArrayList()
?
因為List有多個實現類,可能現在我們用的是ArrayList,也許哪一天就需要換成其它的實現類,如LinkedList或者Vector等等,因此生成List引用會更好一點。
知曉當前在哪個活動
新建一個BaseActivity extends AppCompatActivity,但不需要在AndroidManifest.xml中注冊,讓其成為所有活動的父類
public class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("BaseActivity",getClass().getSimpleNmae());//獲取當前實例的類名,并通過Log打印了出來
}
隨時隨地退出程序
新建一個ActivityCollector類做為活動管理器,在其中通過一個List來暫存活動,然后提供了一個addActivity()方法用于向List中添加一個活動,提供了一個removeActivity()方法用于從List中移除活動,最后提供了一個finishAll()方法用于將List中存儲的活動全部都銷毀掉,無論在哪個地方想退出程序,只需要調用ActivityCollector.finishAll()方法就可以了。
活動的最佳啟動方法
在一個團隊完成一項工程時,為了方便隊友啟動我們編寫的活動界面,可以在我們編寫的活動中新添加一個actionStart()方法,這樣隊友通過該方法就知道啟動該活動所需要的參數類型,從而很方便地在他的活動界面啟動我們編寫的活動。如需要在FirstActivity中啟動SecondActivity,則代碼如下
-
在SecondActivity中添加了一個actionStart()方法
publicstatic void actionStart(Context context, String data1, String data2) { Intentintent = new Intent(context, SecondActivity.class); intent.putExtra("param1",data1); intent.putExtra("param2",data2); context.startActivity(intent); }
-
在FristActivity中調用該方法啟動SecondActivity
SecondActivity.actionStart(FirstActivity.this,"data1", "data2");