Android UI編程—Fragment
Fragment是Activity的介面中的一部分或一種行為。你可以把多個Fragment們組合到一個Activity中來建立一個多面介面並且可以在多個Activity中重用一個Fragment。也可以把Fragment認為模組化的一段Activity,它具有自己的生命週期,接收它自己的事件,並可以在Activity運行時被添加或刪除。
Fragment不能獨立存在,它必須嵌入到activity中,而且Fragment的生命週期直接受所在的Activity的影響。例如:當Activity暫停時,它擁有的所有的Fragment們都暫停了,當Activity銷毀時,它擁有的所有Fragment們都被銷毀。然而,當Activity運行時(在onResum()之後,onPause()之前),你可以單獨地操作每個Fragment,比如添加或刪除它們。當在執行上述針對Fragment的事務時,你可以把事務添加到一個棧中,這個棧被Activity管理,棧中的每一條都是一個Fragment的一次事務。有了這個棧,就可以反向執行Fragment的事務了,這樣就可以在Fragment級支援返回鍵(向後導航)。
當向Activity中添加一個Fragment時,它須置於ViewGroup控制中,並且需定義Fragment自己的介面。你可以在layout xml檔案中聲明Fragment,元素為:;也可以在代碼中建立Fragment,然後把它加入到ViewGroup控制項中。然而,Fragment不一定非要放在Activity的介面中,它可以隱藏在後台為Activity工作。
Fragment要點:
1、Fragment作為Activity介面的一部分組成出現
2、可以在一個Activity中同時出現多個Fragment,並且,一個Fragment也可在多個Activity中使用
3、在Activity運行過程中,可以添加、移除或者替換Fragment(add()、remove()、replace())
設計哲學:
Android從3.0開始引入Fragment,主要是為了支援更動態更靈活的介面設計,比如在平板上的應用。平台機上擁有比手機更大的螢幕空間來組合和互動介面組件們。Fragment使在做這樣的設計時,不需要應付View樹中複雜的變化。通過把Activity的layout分成Fragment,就可以在Activity運行時改變它的樣子,並且可以在Activity的後退棧中儲存這個改變。
例如:寫一個讀新聞的程式,可以用一個fragment顯示標題列表,另一個Fragment顯示選中標題的內容,這兩個Fragment都是在一個Activity上,並排顯示。那麼這兩個Fragment都有自己的生命週期並響應自己感興趣的事件。於是,不需要像手機上那樣一個Activity顯示標題列表,另一個Activity顯示新聞內容;現在可以把兩者放在一個Activity上同時顯示。如:
Fragment必須被寫成可重用的模組。因為Fragment有自己的layout,自己進行事件相應,擁有自己的生命週期和行為,所有可以在多個Activity中包含一個Fragment的不同執行個體。這個對於你的介面在不同的螢幕尺寸下都能使用者完美的體驗尤其重要。比如可以在程式運行大螢幕中時啟動包含很多Fragment的Activity,而在運行於小螢幕時啟動一個包含少量Fragment的Activity。
舉個例子:還是剛才那個讀新聞的程式,當檢測到程式運行於大螢幕時,啟動activity A,將標題列表和新聞內容這個兩個Fragment都放在Activity A中;而當檢測到程式運行於小螢幕時,還是啟動Activity A,但此時A中只有標題列表Fragment,當選擇一個標題時,Activity A啟動Activity B,B中含有新聞內容Fragment。
建立Fragment:
要建立一個Fragment,必須從Fragment或Fragment的衍生類別派出一個新類。Fragment的代碼寫起來有些像Activity。它具有跟Activity一樣的回調方法,比如onCreate()、onStart()、onPause()和onStop()。實際上,如果想把老的程式改為使用Fragment,基本上只需要把Activity的回調方法的代碼移到Fragment中對應的回調方法即可。
通常需要實現以下生命週期函數:
onCreate()——當建立Fragment時系統調用此方法。在其中你必須初始化Fragment的基礎組件們,可參考Activity的說明
onCreateView()——系統在Fragment要畫自己的介面時調用(在正在顯示之前)此方法。這個方法必須返回Fragment的layout的根控制項。如果這個fragment不提供介面,可以返回null。即,Fragment第一次繪製它的使用者介面的時候,系統會調用此方法。為了繪製Fragment的UI,此方法必須返回一個View,這個View是Fragment布局的根View。如果Fragment不提供UI,可以返回null。
onPause()——使用者將要離開Fragment時,系統調用這個方法作為第一個指示(然而它不總是意味著Fragment將被銷毀)。當前面使用者繪畫結束之前,通常應當在這裡提交任何應該持久的變化(因為使用者有可能不會返回)。
Fragment的生命週期:
除了繼承基類Fragment,還有一些子類可以繼承
DialogFragment
顯示一個浮動的對話方塊,用這個類來建立一個對話方塊是使用在Activity類的對話方塊工具方法之外的一個好選擇,因為可以將一個Fragment對話方塊合并到Activity管理的Fragment back stack(回退棧)中,允許使用者返回一個之前曾被摒棄的Fragment。
ListFragment
顯示一個由Adapter(例如SimpleCursorAdapter)管理的項目的列表,類似於ListActivity。它提供一些方法來管理一個List View,例如onListItemClick()回調來處理點擊事件。
PreferenceFragment
顯示一個Preference對象的階層的列表,類似於PreferenceActivity。這個在為應用建立一個設定Activity時有用處
例子一:
AndroidManifest.xml——沒有做任何修改,建立工程預設
fragment1.xml
fragment2.xml
activity_main.xml
Fragment1.java
package com.wxl.fragment;import android.os.Bundle;import android.support.v4.app.Fragment;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;public class Fragment1 extends Fragment {@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {// TODO Auto-generated method stubreturn inflater.inflate(R.layout.fragment1, container,false);}}
Fragment2.java
package com.wxl.fragment;import android.os.Bundle;import android.support.v4.app.Fragment;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;public class Fragment2 extends Fragment {@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {// TODO Auto-generated method stubreturn inflater.inflate(R.layout.fragment2, container,false);}}
MainActivity.java
package com.wxl.fragment;import android.os.Bundle;import android.support.v4.app.FragmentActivity;public class MainActivity extends FragmentActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); }}
注意:
在Activity中靜態使用Fragment的時候,要注意兩個地方:
Fragment引用的包是:import android.support.v4.app.Fragment;而不是import android.app.Fragment;
然後Activity必須是繼承FragmentActivity,引用包是:import android.support.v4.app.FragmentActivity;
:
Logcat列印資訊:(FragmentActivity)
添加使用者介面:
Fragment通常用來作為一個Activity的使用者介面的一部分,並將它的layout提供給Activity。為了給一個Fragment提供一個layout,必須實現onCreateView()回調方法,當到了Fragment繪製它自己的layout的時候,Android系統調用調用它。此方法的實現代碼必須返回一個Fragment的layout的根View。
public class Fragment1 extends Fragment {@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {// TODO Auto-generated method stubreturn inflater.inflate(R.layout.fragment1, container,false);}}傳入onCreateView()的container參數是Fragment layout將被插入的父ViewGroup(來自Activity的layout)。savedInstanceState參數是一個Bundle,如果Fragment是被恢複的,它提供關於Fragment的之前的執行個體資料。
inflater.inflate(R.layout.fragment1, container,false);
參數一:想要載入的layout的resource ID參數二:載入的layout的父ViewGroup。傳入container是很重要的,目的是為了讓系統接受所要載入的layout的根View的layout參數,由它將掛靠的父View指定參數三:布爾值指示在載入期間,展開的layout是否應附著到ViewGroup(第二個參數)。(在這個例子中,指定了false,因為系統已經把展開的layout插入到container,傳入true會在最後的layout中建立一個多月的ViewGroup)。
將Fragment添加到Activity 通常地,Fragment為宿主Activity提供UI的一部分,被作為Activity的整個的一部分被嵌入。有兩種方法可以添加一個Fragment到Activity layout中。
方法一:在Activity的layout檔案中聲明fragment
在這種情況下,可以像為View一樣,為fragment指定layout屬性:
中的android:name屬性指定了在layout中執行個體化的Fragment類。
當系統建立這個Activity layout時,它執行個體化每一個在layout中指定的fragment,並調用每一個上的onCreateView()方法,來擷取每一個fragment的layout。系統將從返回的View直接插入到元素所在的地方。
注意:每一個fragment都需要一個唯一的標識,如果Activity重啟,系統可以用來恢複fragment(並且也可以用來捕獲fragment來處理事務,例如移除它)
有3種方法來為一個fragment提供一個標識:
為android:id屬性提供一個唯一ID為android:tag屬性提供一個唯一字串如果以上2個都沒有提供,系統使用容器view的ID
方法二:編寫代碼將fragment添加到一個已存在的ViewGroup 當Activity啟動並執行任何時候,都可以將fragment添加到Activity layout中。只需要簡單的指定一個需要放置fragment的ViewGroup為了在Activity中操作fragment事務(例如添加、移除、或者替換一個fragment),必須使用來自FragmentTransaction的API 可以按如下方法,從Activity取得一個FragmentTransaction的執行個體
FragmentManager fragmentManager = getSupportFragmentManager(); FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
然後可以使用add()方法添加一個fragment,指定要添加的fragment和要插入的View
Fragment1 fragment = new Fragment1(); fragmentTransaction.add(R.id.main,fragment); fragmentTransaction.commit();
add()第一個參數是fragment要放入的ViewGroup,由resource ID指定。第二個參數是需要添加的fragment。
一旦用FragmentTransaction做了改變,為了使改變生效,必須調用commit()。其中Fragment1是自己繼續Fragment基類或衍生類別的新類。
例子二——根據上述例子一,對上述的activity_main.xml、Fragment1.java、MainActivity.java做了部分修改,修改如下:
Fragment1.java
將原來導包import android.support.v4.app.Fragment;該為import android.app.Fragment;
activity_main.xml
MainActivity.java
package com.wxl.fragment;import android.annotation.SuppressLint;import android.app.FragmentManager;import android.app.FragmentTransaction;import android.os.Bundle;import android.support.v4.app.FragmentActivity;public class MainActivity extends FragmentActivity{ @SuppressLint(NewApi) @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); FragmentManager fragmentManager = getFragmentManager(); FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); Fragment1 fragment1 = new Fragment1(); fragmentTransaction.add(R.id.main_linearLayout1, fragment1); fragmentTransaction.commit(); }}
注意: 本例子fragment2採用在activity_main.xml中直接添加,而fragment1採用在MainActivity.java中使用代碼添加的。 採用xml方式的,即使用方式一添加Fragment的時候使用的是android.support.v4.app.Fragment;所有在Fragment2.java中匯入包是:import android.support.v4.app.Fragment; 採用代碼方式的,即使用方式二添加Fragment的時候使用的是android.app.Fragment;所有在Fragment1.java中匯入包是:import android.app.Fragment; 而在本例子採用了兩種方式,所有在MainActivity.java的Activity需要繼承FragmentActivity(方式一需要使用),而方式無須繼承FragmentActivity。
還得注意:
inflater.inflate(R.layout.fragment1, container,false);
第三個參數必須為false,否則出現以下提示錯誤 MainActivity.java的Activity需要繼承FragmentActivity(方式一需要使用),否則出現以下提示錯誤