Android Fragment 使用詳解


  雖然網上有很多關於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>
  <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.

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狀態.

  舉個例子,操作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().

四、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對象並轉換為所定義的接口對象,在需要進行通訊的地方,調用接口的方法即可;


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM