什么是Fragment
自從Android 3.0中引入fragments 的概念,根據詞海的翻譯可以譯為:碎片、片段。其目的是為了解決不同屏幕分辯率的動態和靈活UI設計。大屏幕如平板小屏幕如手機,平板電腦的設計使得其有更多的空間來放更多的UI組件,而多出來的空間存放UI使其會產生更多的交互,從而誕生了fragments 。
fragments 的設計不需要你來親自管理view hierarchy 的復雜變化,通過將Activity 的布局分散到frament 中,可以在運行時修改activity 的外觀,並且由activity 管理的back stack 中保存些變化。當一個片段指定了自身的布局時,它能和其他片段配置成不同的組合,在活動中為不同的屏幕尺寸修改布局配置(小屏幕可能每次顯示一個片段,而大屏幕則可以顯示兩個或更多)。
Fragment必須被寫成可重用的模塊。因為fragment有自己的layout,自己進行事件響應,擁有自己的生命周期和行為,所以你可以在多個activity中包含同一個Fragment的不同實例。這對於讓你的界面在不同的屏幕尺寸下都能給用戶完美的體驗尤其重要。
Fragment優點
Fragment可以使你能夠將activity分離成多個可重用的組件,每個都有它自己的生命周期和UI。
Fragment可以輕松得創建動態靈活的UI設計,可以適應於不同的屏幕尺寸。從手機到平板電腦。
Fragment是一個獨立的模塊,緊緊地與activity綁定在一起。可以運行中動態地移除、加入、交換等。
Fragment提供一個新的方式讓你在不同的安卓設備上統一你的UI。
Fragment 解決Activity間的切換不流暢,輕量切換。
Fragment 替代TabActivity做導航,性能更好。
Fragment 在4.2.版本中新增嵌套fragment使用方法,能夠生成更好的界面效果。
Fragment做局部內容更新更方便,原來為了到達這一點要把多個布局放到一個activity里面,現在可以用多Fragment來代替,只有在需要的時候才加載Fragment,提高性能。
可以從startActivityForResult中接收到返回結果,但是View不能。
Fragment的生命周期
你可以把fragment看作是activity的模塊化組件,它擁有自己的生命周期
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級支持“返回”鍵(向后導航)。
顯示Fragment時(跟用戶交互)要調用的核心的生命周期方法如下:
1. 把Fragment對象跟Activity關聯時,調用onAttach(Activity)方法;
2. Fragment對象的初始創建時,調用onCreate(Bundle)方法;
3. onCreateView(LayoutInflater, ViewGroup, Bundle)方法用於創建和返回跟Fragment關聯的View對象;
4. onActivityCreate(Bundle)方法會告訴Fragment對象,它所依附的Activity對象已經完成了Activity.onCreate()方法的執行;
5. onStart()方法會讓Fragment對象顯示給用戶(在包含該Fragment對象的Activity被啟動后);
6. onResume()會讓Fragment對象跟用戶交互(在包含該Fragment對象的Activity被啟恢復后)。
Fragment對象不再使用時,要反向回調的方法:
1. 因為Fragment對象所依附的Activity對象被掛起,或者在Activity中正在執行一個修改Fragment對象的操作,而導致Fragment對象不再跟用戶交互時,系統會調用Fragment對象的onPause()方法;
2. 因為Fragment對象所依附的Activity對象被終止,或者再Activity中正在執行一個修改Fragment對象的操作,而導致Fragment對象不再顯示給用戶時,系統會調用Fragment對象的onStop()方法。
3. onDestroyView()方法用於清除跟Fragment中的View對象關聯的資源;
4. Fragment對象的狀態被最終清理完成之后,要調用onDestroy()方法;
5. 在Fragment對象不再跟它依附的Activity關聯的時候,onDetach()方法會立即被調用
下圖是Fragment的生命周期與Activity的對應關系:
從這個圖上可以看出activity的狀態決定了fragment可能接收到的回調函數。
比如說,當activity接收到它的onCreate()回調函數,那么這個activity中的fragment最多接收到了onActivityCreated()。
當activity處於Resumed狀態時,可以自由地添加和移除fragment,也即是說,只有activity在Resumed狀態時,fragment的狀態可以獨立改變。
但是,當activity離開Resumed狀態,fragment的生命周期被activity控制。
Fragment間信息交互
(1)取得對象
/*
* 點擊該Fragment的button按鈕,將該button的text設置為另一個fragment中Edittext的文本值
*/
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.list, null);
final Button button = (Button) view.findViewById(R.id.confirm);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//通過FragmentManager找到另一個fragment中的edittext對象,並取得text內容
EditText editText = (EditText)(getFragmentManager().findFragmentByTag("left").getView().findViewById(R.id.name));
button.setText(editText.getText().toString());
}
});
return view;
}
(2)通過回調函數
public class MainActivity extends Activity { private FragmentManager manager; private Button button; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { RightFragment rightFrag = (RightFragment) (getFragmentManager().findFragmentByTag("right")); /* * 通過set方法,向其傳遞一個實例化對象,由於rightFrag.set()方法內部執行RightFragment.CallBack.get()方法,完成了參數的傳遞 */ rightFrag.set(new RightFragment.CallBack() { @Override public void get(String str) { button.setText(str); } }); } }); } }
public class RightFragment extends ListFragment { private LoaderManager manager; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); manager = getLoaderManager(); } /* * 點擊該Fragment的button按鈕,將該button的text設置為另一個fragment中Edittext的文本值 */ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.list, null); return view; } /** * 通過調用該方法,接收一個回掉函數對象,callBack.get(str); * @param callBack */ public void set(CallBack callBack) { EditText editText = (EditText) getView().findViewById(R.id.name); callBack.get(editText.getText().toString()); } /* * 回掉接口 */ interface CallBack { public void get(String str); } }
Fragment與Activity信息交互:創建事件回調
一個Fragment的實例總是和包含它的Activity直接相關。
fragment可以通過getActivity()
方法來獲得Activity的實例,然后就可以調用一些例如findViewById()之類的方法。
如:
View listView = getActivity().findViewById(R.id.list);
但是注意調用getActivity()時,fragment必須和activity關聯(attached to an activity),否則將會返回一個null。
相似的,activity也可以獲得一個fragment的引用,從而調用fragment中的方法。
獲得fragment的引用要用FragmentManager,之后可以調用findFragmentById()
或者 findFragmentByTag()
.
比如:
ExampleFragment fragment = (ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment);
一些情況下,可能需要fragment和activity共享事件,一個比較好的做法是在fragment里面定義一個回調接口,然后要求宿主activity實現它。
當activity通過這個接口接收到一個回調,它可以同布局中的其他fragment分享這個信息。
例如,一個新聞顯示應用在一個activity中有兩個fragment,一個fragment A顯示文章題目的列表,一個fragment B顯示文章。
所以當一個文章被選擇的時候,fragment A必須通知activity,然后activity通知fragment B,讓它顯示這篇文章。
這個情況下,在fragment A中聲明一個這樣的接口OnArticleSelectedListener:
public static class FragmentA extends ListFragment { ... // Container Activity must implement this interface public interface OnArticleSelectedListener { public void onArticleSelected(Uri articleUri); } ... }
之后包含這個fragment的activity實現這個OnArticleSelectedListener接口,用覆寫的onArticleSelected()方法將fragment A中發生的事通知fragment B。
為了確保宿主activity實現這個接口,fragment A的onAttach()
方法(這個方法在fragment 被加入到activity中時由系統調用)中通過將傳入的activity強制類型轉換,實例化一個OnArticleSelectedListener對象:
public static class FragmentA extends ListFragment { OnArticleSelectedListener mListener; ... @Override public void onAttach(Activity activity) { super.onAttach(activity); try { mListener = (OnArticleSelectedListener) activity; } catch (ClassCastException e) { throw new ClassCastException(activity.toString() + " must implement OnArticleSelectedListener"); } } ... }
如果activity沒有實現這個接口,fragment將會拋出ClassCastException異常,如果成功了,mListener將會是activity實現OnArticleSelectedListener接口的一個引用,所以通過調用OnArticleSelectedListener接口的方法,fragment A可以和activity共享事件。
比如,如果fragment A是ListFragment的子類,每一次用戶點擊一個列表項目,系統調用fragment中的onListItemClick()
方法,在這個方法中可以調用onArticleSelected()方法與activity共享事件。
public static class FragmentA extends ListFragment { OnArticleSelectedListener mListener; ... @Override public void onListItemClick(ListView l, View v, int position, long id) { // Append the clicked item's row ID with the content provider Uri Uri noteUri = ContentUris.withAppendedId(ArticleColumns.CONTENT_URI, id); // Send the event and Uri to the host activity mListener.onArticleSelected(noteUri); } ... }