Android Fragment 使用詳解

  雖然網上有不少關於Fragment的文章,但我這裏仍是要寫這篇筆記,由於我在編寫程序的過程當中發現了一個問題,至今未解決,但願獲得你們的幫助;
  PS:當我在Fragment中定義一個名爲setIndex(int index)的方法以後,運行程序,就會報錯(打印的錯誤信息顯示錯誤爲找不到這個Fragment對應的類),但當把這個方法的名稱改成其它的名稱以後,程序便可正常運行,個人api level爲17和18;android

先看看Fragment的生命週期方法,其實和Activity基本仍是類似的,瞭解Activity的生命週期,再看Fragment的生命週期方法,也就比較容易理解;

編程

先介紹相關概念api

1、fragment一般做爲宿主activity UI的一部分, 被做爲activity整個view hierarchy的一部分被嵌入,至關於一個輕量級的Activity;dom

(一)、將一個fragment添加到layout,有兩種方法。
一、在Layout文件中,添加一個Fragement對應的節點
ide

<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>
  <fragment> 中的 android:name 屬性指定了在layout中具體的Fragment類.  當系統建立這個layout時, 它實例化每個在layout中指定的fragment,並調用每個fragment的onCreateView()方法,來獲取每個fragment的layout. 系統將從fragment返回的View直接插入到<fragment>元素所在的地方. 
  須要注意的是,每個fragment都須要一個惟一的標識, 若是activity重啓,系統能夠用來恢復fragment(而且也能夠用來捕獲fragment來處理事務,例如移除它.)  
  有3種方法來爲一個fragment提供一個標識:
  • 爲 android:id 屬性提供一個惟一ID;
  • 爲 android:tag 屬性提供一個惟一字符串;
  • 若是以上2個你都沒有提供, 系統使用容器view的ID.

二、使用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;
this

    @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);
    }

2、FragmentManagerspa

FragmentManager可以實現管理activity中fragment. 經過調用activity的getFragmentManager()取得它的實例.FragmentManager能夠作以下一些事情:
一、使用findFragmentById() (適用於在layout中提供了一個UI的fragment)或findFragmentByTag()(適用於有或沒有UI的fragment)獲取activity中存在的fragment;
二、將fragment從後臺堆棧中彈出, 使用 popBackStack() (模擬用戶按下BACK 命令);
三、使用addOnBackStackChangeListener()註冊一個監聽後臺堆棧變化的listener.

3、FragmentTransaction
  FragmentTransaction提供了對fragment進行添加,移除,替換,以及執行其餘操做的api。每個事務都是同時要執行的一套變化.能夠在一個給定的事務中設置你想執行的全部變化,使用諸如 add(), remove(), 和 replace(),而後, 要給activity應用事務, 還必需要調用 commit().
  在調用commit()以前, 你可能想調用 addToBackStack(),將事務添加到一個fragment事務的back stack. 這個back stack由activity管理, 並容許用戶經過按下 BACK 按鍵返回到前一個fragment狀態.

  舉個例子,操做1在將layout中R.id.cont替換Fragment2,並在後臺堆棧中保留以前的狀態;操做2將R.id.cont替換爲Fragment3,但並不保存狀態。

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().

4、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); } }
在"接收消息"的Fragment中定義public方法,在"發送消息"的Fragment中經過 getActivity().getFragmentManager().findFragmentById(id)查找到Fragment並傳送消息;
(二)、不一樣時顯示
  好比說,當前Activity的一個頁面某部份內容爲Fragment1,點擊Fragment1中的某一個按鈕,須要將Fragment所在區域顯示爲Fragment2,同時須要給Fragment2傳參數過去,這時經過findFragmentById就不能達到要求了;
  這時就須要用到接口編程,定義一個接口並讓Activity去實現這個接口,在Fragment中經過getActivity()獲取Fragment所依託的Activity對象並轉換爲所定義的接口對象,在須要進行通信的地方,調用接口的方法便可;
相關文章
相關標籤/搜索