Activity的使用

一個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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章