雖然網上有很多關於Fragment的文章,但我這里還是要寫這篇筆記,因為我在編寫程序的過程中發現了一個問題,至今未解決,希望得到大家的幫助;
PS:當我在Fragment中定義一個名為setIndex(int index)的方法之后,運行程序,就會報錯(打印的錯誤信息顯示錯誤為找不到這個Fragment對應的類),但當把這個方法的名稱改為其它的名稱之后,程序即可正常運行,我的api level為17和18;
先看看Fragment的生命周期方法,其實和Activity基本還是相似的,了解Activity的生命周期,再看Fragment的生命周期方法,也就比較容易理解;
先介紹相關概念
一、fragment通常作為宿主activity UI的一部分, 被作為activity整個view hierarchy的一部分被嵌入,相當於一個輕量級的Activity;
(一)、將一個fragment添加到layout,有兩種方法。
1、在Layout文件中,添加一個Fragement對應的節點
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical" > <fragment android:id="@+id/main_mf" android:name="com.dbo4.domain.MyFragement" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>
- 為 android:id 屬性提供一個唯一ID;
- 為 android:tag 屬性提供一個唯一字符串;
- 如果以上2個你都沒有提供, 系統使用容器view的ID.
2、使用FragmentManager將fragment添加到一個已經存在的ViewGroup
當activity運行的任何時候, 都可以將fragment添加到它的layout.只需簡單的指定一個需要放置fragment的ViewGroup(通常為FrameLayout).FragmentManager提供了一個FragmentTransaction的API,以實現在activity中操作fragment事務(例如添加,移除,或代替一個fragment)。
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
然后可以使用 add() 方法添加一個fragment, 指定要添加的fragment, 和要插入的view.
MyFragment fragment = new MyFragment(); fragmentTransaction.add(R.id.xx_viewgroup, fragment); fragmentTransaction.commit();
兩個參數分別是要放入的ViewGroup, 由resource ID指定和需要添加的fragment。為了使改變生效,還須調用commit()提交事務.
(二)、如果需要Fragment顯示菜單,可設置hasOptionsMenu = true;
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setHasOptionsMenu(true); } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); menu.add("Ma").setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); menu.add("Mb").setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); } @Override public boolean onOptionsItemSelected(MenuItem item) { // TODOreturn super.onOptionsItemSelected(item); }
二、FragmentManager
FragmentManager能夠實現管理activity中fragment. 通過調用activity的getFragmentManager()取得它的實例.FragmentManager可以做如下一些事情:
1、使用findFragmentById() (適用於在layout中提供了一個UI的fragment)或findFragmentByTag()(適用於有或沒有UI的fragment)獲取activity中存在的fragment;
2、將fragment從后台堆棧中彈出, 使用 popBackStack() (模擬用戶按下BACK 命令);
3、使用addOnBackStackChangeListener()注冊一個監聽后台堆棧變化的listener.
三、FragmentTransaction
FragmentTransaction提供了對fragment進行添加,移除,替換,以及執行其他操作的api。每一個事務都是同時要執行的一套變化.可以在一個給定的事務中設置你想執行的所有變化,使用諸如 add(), remove(), 和 replace(),然后, 要給activity應用事務, 還必須要調用 commit().
在調用commit()之前, 你可能想調用 addToBackStack(),將事務添加到一個fragment事務的back stack. 這個back stack由activity管理, 並允許用戶通過按下 BACK 按鍵返回到前一個fragment狀態.
ft = fm.beginTransaction();
ft.replace(R.id.cont, new Fragment2());
ft.addToBackStack("f2");
ft.commit();
/************************************/
ft = fm.beginTransaction();
ft.replace(R.id.cont, new Fragment3());
ft.commit();
通過調用 addToBackStack(), replace事務被保存到back stack, 因此用戶可以回退事務,並通過按下BACK按鍵帶回前一個fragment.
執行上面兩個方法執行的Fragment2的生命周期方法為:
onAttach -- onCreate -- onCreateView -- onActivityCreated -- onStart -- onResume --
onPause -- onStop -- onDestoryView --
onCreateView -- onActivityCreated -- onStart -- onResume --
如果添加多個變化到事務(例如add()或remove())並調用addToBackStack(), 然后在調用commit()之前的所有應用的變化會被作為一個單個事務添加到后台堆棧, BACK按鍵會將它們一起回退.當執行一個移除fragment的事務時, 如果沒有調用 addToBackStack(), 那么當事務提交后, 那個fragment會被銷毀,並且用戶不能導航回到它。反之,比如當移除一個fragment時,如果調用了 addToBackStack(), 那么fragment會被停止, 如果用戶導航回來,它將會被恢復.
如果添加多個fragment到同一個容器,那么添加的順序決定了它們在view hierachy中顯示的順序.對於每一個fragment事務, 如果需要添加一個事務動畫, 可以通過在提交事務之前調用setTransition()實現.
實際上,調用 commit() 並不立即執行事務.恰恰相反, 它將事務安排排期, 一旦准備好, 就在activity的UI線程上運行(主線程).如果有必要, 可以在UI線程中調用 executePendingTransactions() 來立即執行由commit()提交的事務. 但這么做通常不必要, 除非事務是其他線程中的job的一個從屬.
只能在activity保存它的狀態(當用戶離開activity)之前使用commit()提交事務.如果試圖在那個點之后提交, 會拋出一個異常.這是因為如果activity需要被恢復, 提交之后的狀態可能會丟失.對於覺得可以丟失提交的狀況, 使用 commitAllowingStateLoss().
四、Fragment之間的通信
Fragment之間通信,我分為兩種情況,一是兩個Fragment同時在一個activity中,二是兩個Fragment不同時存在在前台顯示;
(一)、同時顯示:對於同時添加顯示於activity的情況,比較簡單,下面是一個例子
>>.布局文件
<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" android:orientation="horizontal" tools:context=".MainActivity" > <fragment android:id="@+id/fragment1" android:name="com.fragment.Fragment1" android:layout_width="0dip" android:layout_height="match_parent" android:layout_weight="1" /> <fragment android:id="@+id/fragment2" android:name="com.fragment.Fragment2" android:layout_width="0dip" android:layout_height="match_parent" android:layout_weight="1" /> </LinearLayout>
.布局內容
<Button android:id="@+id/modify" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="修改TextView" /> <!--frag1對應布局內容--> <TextView android:id="@+id/value" android:layout_width="match_parent" android:layout_height="match_parent" /> <!--frag2對應布局內容-->
.Fragment1
public class Fragment1 extends Fragment implements OnClickListener { /** * 當fragment被創建的時候調用此方法,返回當前fragment顯示的內容 */ @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.fig1, null); Button btn = (Button) v.findViewById(R.id.modify); btn.setOnClickListener(this); return v; } @Override public void onClick(View v) { Fragment2 f2 = (Fragment2) getActivity().getFragmentManager().findFragmentById(R.id.fragment2); f2.setText("測試修改TextView值"); } }
.Fragment2
public class Fragment2 extends Fragment { private TextView tv;
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.fig2, null); tv = (TextView) v.findViewById(R.id.value); return v; } public void setText(String str) { tv.setText(str); } }