一個Activity的使用主要可以分爲Manifest文件、xml佈局文件和Java代碼的書寫三部分。爲了使得邏輯較爲清晰,大致可以採用先寫xml佈局文件,再寫Java代碼,最後在manifest文件中進行註冊的方式,因此本文將按照上述順序對Activity的使用做一些簡單的整理。
本文主體是對《Android第一行代碼》的筆記整理,同時也會盡量的參考官方文檔、技術博客。這裏主要是自己的學習筆記,貼在這邊僅供大家討論和批評。
佈局文件
Activity是用來提供用戶交互的組件,因此在寫Activity之前就應該大致的瞭解自己所要提供的ui,而Activity的ui部分往往是寫在xml文件中,因此先書寫xml佈局文件是順理成章的。
An Activity is an application component that provides a screen with which users can interact in order to do something, such as dial the phone, take a photo, send an email, or view a map.
此時我們往往面臨着兩個問題:
- 可視化編輯好還是代碼形式好?
- 寫在xml中好還是Activity中直接添加好?
//TODO
五種基本佈局
LinearLayout
一個典型的LinearLayout文件如下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
</LinearLayout>
這裏比較重要的值是android:orientation
,默認情況下它的值爲horizontal
,但是建議手動設置它的排列方式。
當排列方向爲
horizontal
時,內部控件就不能將寬度設爲match_parent
,否則該控件就會將水平方向全部佔滿,同理,vertical
的時候也是一樣。因此,爲了避免這樣的錯誤發生,建議手動設置排列方式,同時在內部控件的佈局上要記得考慮外部佈局的排列方式。
顯然,內部控件的標籤應該具備相應的控制屬性,以使得內部控件更加符合LinearLayout的排列規律。
android:layout_gravity
當排列方向爲horizontal時,則只有垂直方向上的對齊方式纔會生效,反之亦成立。
android:layout_weight
控件將按照設置的權值進行分佈。對應方向長度設爲0dp。
應用:
我們可以通過指定部分控件的
layout_weight
來實現更好的效果。比如,在一個左側爲EditText,右側爲發送Button的水平LinearLayout佈局中,將Button的寬度設爲wrap_content
,而將EditText的weight設爲1(任意正數即可),則EditText會佔滿屏幕所有的剩餘空間。一些考慮:
每個控件都可以設置它的width和height兩個屬性,但是weight卻只有一個(而沒有width_weight或者height_weight),這也間接的證明了LinearLayout會忽略指定排列方向上的佈局。
RelativeLayout
此處屬性比較多,但是有一定的規律。
相對於父佈局的四個屬性:
屬性名 | 描述 |
---|---|
android:layout_alignParentTop | 和父佈局的頂端對齊 |
android:layout_alignParentBottom | 和父佈局的底端對齊 |
android:layout_alignParentLeft | 和父佈局的左側對齊 |
android:layout_alignParentRight | 和父佈局的右側對齊 |
注意:
可以同時指定多個屬性,當兩個屬性不衝突時(如左和上),則該控件會和父佈局的左上角對齊;如果兩個屬性衝突,則該控件則矛盾的兩個方向上拉伸。
相對於其他控件的四個屬性:
屬性名 | 描述 |
---|---|
android:layout_above | 將控件置於指定控件之上 |
android:layout_below | 將控件置於指定控件之下 |
android:layout_toLeftOf | 將右邊緣和指定控件的左邊緣對齊 |
android:layout_toRightOf | 將左邊緣和指定控件的右邊緣對齊 |
注意:
此處需要提供指定控件的id,因此id的引用需要出現在定義之後。
此時,這些控件之間互斥(我的左邊是你的右邊,我的上邊是你的下邊),那麼是否存在“我的左邊也是你的左邊”的屬性呢?
相對於其他控件的另外四個屬性:
屬性名 | 描述 |
---|---|
android:layout_alignTop | 將上邊緣和指定控件的上邊緣對齊 |
android:layout_alignBottom | 將下邊緣和指定控件的下邊緣對齊 |
android:layout_alignLeft | 將左邊緣和指定控件的左邊緣對齊 |
android:layout_alignRight | 將右邊緣和指定控件的右邊緣對齊 |
討論:
上述的屬性和相對於父佈局的屬性較爲相似,都是以align開頭,表示將自己的某一邊緣同指定控件的相應邊緣對齊。由此而見,很多屬性的設置意圖都可以從字面意思上得到。
FrameLayout
應用場景相對較少,也沒有任何的定位方式,所有的控件都會擺放在佈局的左上角。
TableLayout
不是很常用,稍微瞭解一下即可。
貼上一段代碼:
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TableRow>
<TextView
android:layout_height="wrap_content"
android:textSize="60sp"
android:text="1_1"/>
<TextView
android:layout_height="wrap_content"
android:textSize="60sp"
android:text="1_2"/>
</TableRow>
<TableRow>
<TextView
android:layout_weight="1"
android:layout_height="wrap_content"
android:textSize="60sp"
android:text="2_1"/>
<TextView
android:layout_weight="1"
android:layout_height="wrap_content"
android:textSize="60sp"
android:text="2_2"/>
</TableRow>
<TableRow>
<Button
android:layout_height="wrap_content"
android:layout_span="2"
android:text="commit"/>
</TableRow>
</TableLayout>
android:layout_width
在每一個TableRow子標籤中,控件是水平排列的(可以認爲是一個LinearLayout?),此時不需要指定
android:layout_width
,但是可以指定android:layout_weight
。指定android:layout_weight
會改變控件在水平方向上的排列,但是不會改變單元格的寬度。android:layout_span
可以指定
android:layout_span="2"
來指定該控件將佔據兩列的寬度。不管span的值如何指定,控件長度將不小於1,不大於所有列的長度。android:strethColumns
需要寫在父標籤中,例如android:strethColumns=”1”,可以達到自適應屏幕寬度的作用,列號從開始。與指定layout_weight不同,它會改變列的實際寬度。
AbsoluteLayout
傳說中的第五個基本佈局,但是官方已經不推薦使用了。
引入佈局
可以在一個佈局文件中引用另外一個佈局文件的佈局,使用方法非常簡單,只需要在需要引用該佈局的地方加上<include layout="@layout/layout_tobe_included" />
即可。這樣可以複用佈局文件,避免重複代碼。
一些常見的控件
TextView
android:gravity
指定文字的對齊方式(推薦),可選值有top、bottom、left、right和center等,可以用”|“來同時指定多個值
android:textSize
- android:textColor
Button
可配置的屬性同TextView差不多。
在較新的版本中,Button中的字符會自動顯示爲大寫,解決方法是設置
android:textAllCaps="false"
。
EditText
android:hint
設置提示文本,只有text爲空時才顯示。
android:maxLines
防止輸入內容行數過多使得控件不斷拉伸,導致界面難看。
ImageView
- android:src
設置圖片的來源,如
android:src="@drawable/ic_launcher"
。
ProgressBar
android:visiblity
可選值爲visible、invisible和gone。gone表示不僅不可見,還不佔用任何屏幕控件。
style
指定ProcessBar的不同樣式。
android:max
給進度條設置最大值。
AlertDialog
對話框應該是在運行時纔會出現,因此主要在Activity代碼中添加。它能夠屏蔽掉其他控件的交互能力。
ProgressDialog
與AlertDialog類似,但是可以顯示一個進度條。
ListView
並不是特別的複雜
自定義控件
需要添加自定義控件的完整類名。
Activity類代碼
生命週期
首先需要管理的是Activity的生命週期。
Activity有四個重要的狀態:
1. active or running:
處於前臺,或者說在back stack的頂部。
2. paused:
失去focus,但是仍然可見。它將保持所有的狀態和成員信息,仍然attach在window manager上,只有在系統內存極低時纔會被銷燬。
3. stopped:
完全被其他Activity遮蓋。仍然保持所有的狀態和成員信息,但是不再可見,並且經常在其他地方需要內存時被銷燬。
4. 當activity處於paused或者stopped狀態時,系統在需要內存時就會請求activity自己finish或者簡單的殺死它的進程。當用戶再次看到這個Activity時,需要重新啓動並且重新獲取它之前的狀態。
在此插入一個生命週期圖:
Activity有三個重要的循環:
* 完整生命週期:
處於onCreate和onDestroy之間。在onCreate方法中做全局的準備工作,在onDestroy方法中釋放資源。
* 可見期:
處於onStart和相應的onStop之間。此時用戶可以看見Activity,但是可能不處於前臺,也不能同用戶進行交互。此時可以持有一些同顯示相關的資源。
* 前臺期:
處於onResume和onPause之前。此時該Activity處於其他所有Activity前面,並且可以與用戶交互。Activity可能在resumed和paused之間頻繁切換,因此它們中的代碼應該儘量保持輕量。
理論的東西講的太多了,還是直接講講生命週期相關的回調函數應該寫些什麼東東吧。
- onCreate(Bundle savedInstanceState)
再第一次被創建時調用,一般可以在這個方法中完成Activity的初始化動作,比如說加載佈局(比如setContentView方法),綁定事件等。 - onPause()
我們通常在這個方法中將一些消耗CPU的資源釋放掉,以及保存一些關鍵數據(通常是交給ContentProvider持有),但是執行速度一定要快,不然會影響到新的棧頂Activity的使用。
注意:
在實現這些生命週期相關的回調函數時,必須首先調用超類的實現。
被回收了怎麼辦?
在onSaveInstanceState(Bundle outState)方法中保存臨時數據,而在onCreate或者onRestoreInstanceState方法中恢復數據。
注意:
onSaveInstanceState並不在生命週期的回調中,因此並非是在所有情況下都會被調用,因此一些數據應該儘量在onPause方法中進行存儲。
與下一個Activity傳遞數據
需要詳細瞭解Intent的用法,此處不贅述。
向下一個Activity傳遞數據
在Intent中放入數據,然後通過startActivity方法或者startActivityForResult方法啓動下一個Activity,下一個Activity被啓動後,通過getIntent方法得到啓動該Activity的Intent,請取出其中的數據。
返回數據給上一個Activity
上一個Activity
需要通過startActivityForResult方法啓動下一個Activity,第一個參數是Intent,第二個參數是請求碼,請求碼在之後可以用來判斷是從哪次請求中返回的數據(不一定只有一種請求)。
此外,需要複寫onActivityResult方法,先根據返回的requestCode判斷請求類型,然後再判斷resultCode是否是正常返回(否則從Intent中取數據可能會導致異常),最後再從Intent中獲取數據。下一個Activity
在需要返回數據時,將數據放於Intent中,然後通過setResult方法返回,該方法第一個參數是返回碼,RESULT_OK應該表示返回成功,而RESULT_CANCELED會在其他情況下被返回(如按下back鍵被按下時)。注意:
數據只會通過Intent被返回一次,而且是在Activity結束之後,因此只能通信一次,整個Activity就像被調用的一個函數一樣。
創建一個菜單
首先需要在menu目錄下創建menu的佈局文件,它的代碼可以如下:
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="@+id/add_item"
android:title="Add" />
<item
android:id="@+id/remove_item"
android:title="Remove" />
</menu>
然後需要在onCreateOptionSMenu方法中創建menu:
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity, menu);
return true;
}
返回true表示允許創建的菜單顯示出來,若爲false則無法顯示。
最後爲了能夠響應菜單事件,需要重寫onOptionsItemSelected方法:
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.add_item:
Toast.makeText(this, "You clicked Add", Toast.LENGTH_SHORT).show();
break;
case R.id.remove_item:
Toast.makeText(this, "You clicked Remove", Toast.LENGTH_SHORT).show();
}
return true;
}
如果希望將菜單點擊事件傳遞下去,繼續觸發其他處理,則返回false;如果認爲已經處理完畢,不需要將事件傳遞下去則返回true。如果採用的是return super.onOptionsItemSelected(item)
,系統缺省返回false。
注意:
現在很多手機已經取消了menu按鍵,此時可以通過openOptionsMenu方法打開menu,也可以通過closeOptionsMenu方法關閉menu。
處理返回按鍵
重寫onBackPressed方法:
public void onBackPressed() {
// do anything you want.
//super.onBackPressed();
}
默認的實現簡單的finish當前的Activity,重寫該方法可以自定義back按鍵的動作。
處理Fragment
這將專門的在”Fragment的使用“中闡述。
Manifest文件
爲了能夠被Context.startActivity()方法啓動,所有的Activity都需要在Manifest文件中註冊。
Device Compatibility
System Permissons
Intent filter
詳細的查看Intent用戶手冊。
* 注意:*
如果Activity支持隱式Intent啓動,則需要添加DEFAULT的category,這是因爲startActivity和startActivityForResult方法都會默認的添加DEFAULT的category。
啓動模式
在<activity>
標籤下指定android:launchMode屬性來選擇啓動模式。不指定則默認爲standerd模式。
- standard
每次啓動一個新的Activity,它就會在back stack中入棧,並處於棧頂的位置。系統不會在乎這個Activity是否已經存在於back stack中,每次啓動都會創建一個新的實例。 - singleTop
如果待啓動Activity已經在back stack棧頂,則直接使用它而不重新創建。 - singleTask
如果待啓動Activity在back stack中(不僅僅是在棧頂),則彈出該Activity之上所有的Activity,否則創建一個新的Activity。 - singleInstance
用來實現其他程序和我們的程序共享這個Activity的實例。因此,在這個模式下會有一個單獨的back stack來管理這個Activity(實際上singleTask模式指定了不同的taskAffinity也會啓動一個新的back stack),不管是哪個應用程序訪問這個活動,都共用同一個back stack。
// TODO