Android讓Fragment載入到Activity中

生命壹號的部落格發表於2014-09-20

Android上的介面展示都是通過Activity實現的,Activity實在是太常用了。但是Activity也有它的侷限性,同樣的介面在手機上顯示可能很好看,在平板上就未必了,因為平板的螢幕非常大,手機的介面放在平板上可能會有過分被拉長、控制元件間距過大等情況。這個時候更好的體驗效果是在Activity中嵌入”小Activity”,然後每個”小Activity”又可以擁有自己的佈局。因此,我們今天的主角Fragment登場了。

一、Fragment初探:

Fragment是activity的介面中的一部分或一種行為。你可以把多個Fragment們組合到一個activity中來建立一個多面介面,並且你可以在多個activity中重用一個Fragment。你可以把Fragment認為模組化的一段activity,它具有自己的生命週期,接收它自己的事件,並可以在activity執行時被新增或刪除。

Fragment不能獨立存在,它必須嵌入到activity中,而且Fragment的生命週期直接受所在的activity的影響。例如:當activity暫停時,它擁有的所有的Fragment們都暫停了,當activity銷燬時,它擁有的所有Fragment們都被銷燬。然而,當activity執行時(在onResume()之後,onPause()之前),你可以單獨地操作每個Fragment,比如新增或刪除它們。當你在執行上述針對Fragment的事務時,你可以將事務新增到一個棧中,這個棧被activity管理,棧中的每一條都是一個Fragment的一次事務。有了這個棧,就可以反向執行Fragment的事務,這樣就可以在Fragment級支援“返回”鍵(向後導航)。

當向activity中新增一個Fragment時,它須置於ViewGroup控制元件中,並且需定義Fragment自己的介面。你可以在layoutxml檔案中宣告Fragment,元素為:<fragment>;也可以在程式碼中建立Fragment,然後把它加入到ViewGroup控制元件中。然而,Fragment不一定非要放在activity的介面中,它可以隱藏在後臺為actvitiy工作。

設計的哲學:

為了讓介面可以在平板上更好地展示,Android在3.0版本引入了Fragment(碎片)功能,通過官方文件中的這張圖片可以很明顯地看到Fragment的好處:

注:左邊為平板,右邊為手持裝置。

二、Fragment的生命週期:

因為Fragment必須嵌入在Acitivity中使用,所以Fragment的生命週期和它所在的Activity是密切相關的。

如果Activity是暫停狀態,其中所有的Fragment都是暫停狀態;如果Activity是stopped狀態,這個Activity中所有的Fragment都不能被啟動;如果Activity被銷燬,那麼它其中的所有Fragment都會被銷燬。

但是,當Activity在活動狀態,可以獨立控制Fragment的狀態,比如加上或者移除Fragment。

當這樣進行fragment transaction(轉換)的時候,可以把fragment放入Activity的back stack中,這樣使用者就可以進行返回操作。

使用Fragment時,需要繼承Fragment或者Fragment的子類(DialogFragment, ListFragment, PreferenceFragment, WebViewFragment),所以Fragment的程式碼看起來和Activity的類似。

每當建立一個Fragment時,首先新增以下三個回撥方法:

  • onCreate():系統在建立Fragment的時候呼叫這個方法,這裡應該初始化相關的元件,一些即便是被暫停或者被停止時依然需要保留的東西。
  • onCreateView():當第一次繪製Fragment的UI時系統呼叫這個方法,該方法將返回一個View,如果Fragment不提供UI也可以返回null。注意,如果繼承自ListFragment,onCreateView()預設的實現會返回一個ListView,所以不用自己實現。
  • onPause():當使用者離開Fragment時第一個呼叫這個方法,需要提交一些變化,因為使用者很可能不再返回來。

將Fragment載入到Activity當中有兩種方式:

  • 方式一:新增Fragment到Activity的佈局檔案當中
  • 方式二:在Activity的程式碼中動態新增Fragment

第一種方式雖然簡單但靈活性不夠。新增Fragment到Activity的佈局檔案當中,就等同於將Fragment及其檢視與activity的檢視繫結在一起,且在activity的生命週期過程中,無法切換fragment檢視。

第二種方式比較複雜,但也是唯一一種可以在執行時控制fragment的方式(載入、移除、替換)。

下面將分別介紹一下。

三、在Activity的佈局檔案中新增Fragment(不推薦)

平板的模擬器引數如下:

然後新建一個工程檔案。然後繼續如下步驟:

(1)新建檔案fragment_hello.xml和HelloFragment.java:

fragment_hello.xml程式碼如下:(即Fragment的佈局檔案)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <EditText 
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="請輸入內容"/>
    <RatingBar
        android:id="@+id/ratingBar1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</LinearLayout>

HelloFragment.java程式碼如下:

1 package com.example.m01_fragment01;
 2 
 3 import android.app.Fragment;
 4 import android.os.Bundle;
 5 import android.view.LayoutInflater;
 6 import android.view.View;
 7 import android.view.ViewGroup;
 8 
 9 public class HelloFragment extends Fragment {
10     
11     @Override
12     public void onCreate(Bundle savedInstanceState) {
13         super.onCreate(savedInstanceState);
14     }
15     
16     @Override
17     public View onCreateView(LayoutInflater inflater, ViewGroup container,
18             Bundle savedInstanceState) {
19 View view = inflater.inflate(R.layout.fragment_hello, null); // View android.view.LayoutInflater.inflate(int resource, ViewGroup root) 20 return view; 21     }
22     
23     @Override
24     public void onPause() {
25         super.onPause();
26     }
27 }

重點在於第19和20行,通過inflate()方法將自定義的fragment的佈局載入進來。

19行程式碼中,第二個引數中,如果佈局沒有根,那就用null。

注:上方程式碼中,因為我們的程式是面對Android 4.0以上版本的,所以匯入Fragment的包時,選擇第一個:android.app.Fragment

(2)將Fragment新增到Activity的佈局中:

修改activity_main.xml的程式碼如下:

1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2     xmlns:tools="http://schemas.android.com/tools"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent"
 5     tools:context=".MainActivity" >
 6 
 7     <fragment
 8 android:id="@+id/fragment_hello" 9 android:name="com.example.m01_fragment02.HelloFragment"
10         android:layout_width="wrap_content"
11         android:layout_height="wrap_content" />
12 </LinearLayout>

08行和09行是關鍵。其中android:name屬性填上你自己建立的fragment的完整類名。如下圖:

當系統建立這個Activity的佈局檔案時,系統會例項化每一個fragment,並且呼叫它們的onCreateView()方法,來獲得相應fragment的佈局,並將返回值插入fragment標籤所在的地方。

執行之後,效果如下:

實際上,這種方式在開發中並不推薦,我們來介紹另外一種方法。

四、在activity程式碼中新增fragment:

【例項】點選左側fragment中的按鈕,彈出右側的fragment。新建一個工程檔案,然後步驟如下:

(1)將activity_main的佈局分為兩部分:左邊佔1/4,右邊佔3/4。修改activity_main.xml的程式碼如下:

<LinearLayout xmlns: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"
    tools:context=".MainActivity"
    android:orientation="horizontal" >

    <LinearLayout
        android:id="@+id/left"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:layout_weight="1"
        android:background="#00BFFF" >

        <Button 
            android:id="@+id/button1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"            
            android:text="顯示"/>

    </LinearLayout>

    <LinearLayout
         android:id="@+id/right"       
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="3"
        android:background="#00FFFF"
        android:orientation="vertical" >

    </LinearLayout>

</LinearLayout>

上方程式碼中,一個LinearLayout代表一個Fragment的容器,記得要給每個fragment加一個容器的id。上方程式碼的佈局效果如下:

既然兩個fragment的空間都分配好了,接下來右邊的Fragment寫出來。

(2)新建檔案fragment_right.xml和RightFragment.java:

fragment_right.xml程式碼如下:(新增一個文字和按鈕)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns: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/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="新聞內容" />

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button" />

</LinearLayout>

RightFragment.java程式碼如下:

package com.example.m01_fragment03;

import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class RightFragment extends Fragment {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_right, null);
        return view;
    }

    @Override
    public void onPause() {
        super.onPause();
    }
}

緊接著,我們修改上方onCreateView()方法中的程式碼,實現點選按鈕,能夠彈出吐司:

1 public View onCreateView(LayoutInflater inflater, ViewGroup container,
 2             Bundle savedInstanceState) {
 3         View view = inflater.inflate(R.layout.fragment_right, null);
 4 Button button = (Button)view.findViewById(R.id.button2); 5 button.setOnClickListener(new OnClickListener() { 6 @Override 7 public void onClick(View v) { 8 Toast.makeText(getActivity(), "我是fragment", Toast.LENGTH_SHORT).show();  9             }
10         });
11         return view;
12     }

第04行程式碼:有一個單詞view不要忘了。

第08行程式碼:第一個引數一定是getActivity,以此來獲得父類的Activity

(3)在activity程式碼中新增fragment:

點選MainActivity中左側的按鈕,彈出右側的Fragment,

MainActivity.java的監聽器部分的程式碼如下:

 1         button.setOnClickListener(new OnClickListener() {
 2             
 3             @Override
 4             public void onClick(View v) {
 5 
 6 //步驟一:新增一個FragmentTransaction的例項 7 FragmentManager fragmentManager =getFragmentManager(); 8 FragmentTransaction transaction = fragmentManager.beginTransaction(); 9 10 //步驟二:用add()方法加上Fragment的物件rightFragment 11 RightFragment rightFragment = new RightFragment(); 12 transaction.add(R.id.right, rightFragment); 13 14 //步驟三:呼叫commit()方法使得FragmentTransaction例項的改變生效 15  transaction.commit();          
16             }
17         });

記住上面的三個步驟。

第12行程式碼是整個程式的核心。add()方法裡的第一個引數是容器檢視資源ID,而不是layout。容器檢視資源ID有兩個作用:

  • 告知FragmentManager,fragment檢視應該出現在activity檢視的什麼地方
  • 是FragmentManager佇列中fragment的唯一識別符號

執行後,效果如下:

點選左側的按鈕後,出現右側的介面。點選右側的按鈕,彈出吐司。效果如下:

當然,這個程式碼還不夠成熟,因為還涉及到了生命週期沒有處理。我們將在下一章節中進行講解。

相關文章