Android基礎—Fragment

設計理念

Android3.0(API11)開始引入Fragment,主要是爲了在大屏幕上更好的支持動態和靈活的UI設計。以平板爲例,有足夠大的空間容納更多的組件,管理起來無疑過於麻煩。Fragment能夠分割Activity的layout,而且具備本身的生命週期,從而使得組件獨立管理成爲可能。正由於fragment具備獨立的生命週期,因此能夠定義本身的layout和行爲,從而一個fragment能夠在多個activity中重複調用。因此咱們在設計fragment的時候,要儘可能追求模塊化和可重用性。通常在平板和TV上廣泛使用。android

舉例以下:在平板上,activity使用一個fragment展現文章列表,使用另外一個fragment展現文章內容;在手機上,activity使用一個fragment展現文章列表,點擊跳轉到另外一個activity展現。bash

Fragment模塊化和複用性
Fragment模塊化和複用性

Fragment建立

fragment的生命週期和activity很是相似,都有onCreate()、onStart()、onStop()、onPause()回調方法。fragment通常至少要實現三個回調方法:app

  1. onCreate():fragment建立時調用,主要初始化一些重要的組件
  2. onCreateView():fragment繪製界面時調用(setContentView(id)),若是fragment有界面的話須要返回一個view;若是fragment沒有UI,直接返回空便可
  3. onPause():用戶離開fragment,通常要保存數據(用戶可能不會再回來)

fragment類型

常見的有三種fragment,分別是DialogFragment、ListFragment、PreferenceFragmentide

  1. DialogFragment:展現浮動對話框
  2. ListFragment:展現一列數據
  3. PreferenceFragment:以列表形式展現首選項對象的層級關係

建立fragment

fragment通常具備本身的UI,併成爲activity UI的一部分。爲了給fragment建立UI,必須實現onCreateView()回調方法,並返回一個view(fragment的根layout)。模塊化

若是fragment是ListFragment的子類,onCreateView()默認返回ListView,不須要重寫。佈局

public static class ExampleFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.example_fragment, container, false);
    }
}複製代碼

container是activity的layout,fragment的layout將會被插入其中;savedInstanceState即保存的fragment被異常銷燬前的狀態;inflate()有三個參數:ui

  1. id:flagment的view的資源id
  2. container:activity的layout,fragment的layout將插入其中
  3. false:false表示fragment僅爲container提供layout 參數(動態添加、刪除、替代fragment時使用);true表示container成爲fragment layout的父view(在activity佈局文件中定義fragment使用)

把fragment添加到activity中

通常來講,fragment的layout會被嵌入成爲activity view層級的一部分。有兩種方式能夠把fragment添加到activity的layout中:1.靜態加載;2.動態加載。this

  1. 靜態加載:在activity layout中聲明fragment,代碼以下:spa

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     app:layout_behavior="@string/appbar_scrolling_view_behavior"
     tools:context="com.example.chaos.myapplication.MainActivity"
     tools:showIn="@layout/activity_main">
    
     <fragment
         android:id="@+id/fragment_person_list"
         android:name="com.example.chaos.myapplication.PersonListFragment"
         android:layout_width="0dp"
         android:layout_height="match_parent"
         android:layout_weight="1" />
    
     <fragment
         android:id="@+id/fragment_person_detail"
         android:name="com.example.chaos.myapplication.DetailFragment"
         android:layout_width="0dp"
         android:layout_height="match_parent"
         android:layout_weight="1" />
    </LinearLayout>複製代碼

    系統建立該activity的layout時,會將指定的fragment所有實例化並分別調用他們的onCreateView()回調。而後檢索fragment的layout,並將onCreateView()返回的view取代<fragment>元素直接插入activity的layout中。線程

activity重啓時,須要恢復fragment的數據,這就要求fragment要有個惟一的標識符(不然沒法管理fragment)。有三種方式能夠爲fragment賦予惟一的id

  1. 使用android:id屬性賦予id
  2. 使用android:tag屬性賦予惟一標籤
  3. 若是id和tag都沒提供的話,系統模式使用container view的id

2.. 動態加載:動態添加fragment到ViewGroup中
只要在activity在運行,均可以添加fragment到activity的layout中,只須要指定ViewGroup存放fragment就行。
爲了在activity中操做fragment的事務(add、remove、replace),必須使用FragmentTransaction()。代碼以下:

FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();複製代碼

添加fragment使用add()方法,指定ViewGroup和要添加的fragment,對fragmentTransaction作的任何改動都要執行commit()方法才能生效。

ExampleFragment fragment = new ExampleFragment();
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();複製代碼

添加一個沒有UI的fragment

並不是全部fragment都必須有UI,fragment也能夠用來執行後臺操做,此時,無需UI也能夠。該如何添加一個沒有UI的fragment呢?

首先,須要先經過findFragmentByTag()找到,由於tag是惟一能標識它的(只能經過add(fragment,string)主動設置tag)。

若是findFragmentByTag()返回null的話,須要經過add(fragment,string)爲fragment主動設置tag並添加。

Fragment管理

fragment的管理是由FragmentManager實現的,能夠在activity中經過getFragmentManager()獲取FragmentManager。FragmentManager的功能以下:

  1. 使用findFragmentById()(針對在activity layout提供一個UI的fragment)和findFragmentByTag()(針對沒有UI的fragment)獲取activity中已存在的fragment
  2. 調用popBackStack()將fragment從返回棧彈出(相似於用戶點擊back)
  3. 調用addOnBackStackChangedListener()註冊監聽器監聽返回棧的變化

靜態加載的fragment必定具備id或tag;動態加載的id必定具備container view的id或tag(fragment若是沒有惟一標識符,則沒法進行有效管理),動態加載一共有三個方法:

  1. add(Fragment fragment, String tag):適用於有UI和無UI的fragment
  2. add(@IdRes int containerViewId, Fragment fragment):適用於有UI的fragment
  3. add(@IdRes int containerViewId, Fragment fragment,String tag);:適用於有UI的fragment

因此沒有UI的fragment,只能經過findFragmentByTag()獲取;有UI的fragment能夠經過id或tag獲取。

也可使用FragmentManager打開FragmentTransaction,從而操做fragment的事務,如添加或移除。

Fragment事務處理

一個事務就是一系列變化,事務具備原子性、一致性、隔離性、持久性。可使用FragmentTransaction實現事務,也能夠把事務放到由activity管理的返回棧中,便於用戶返回以前的fragment的狀態.

能夠從FragmentManager獲取FragmentTransaction實例

FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();複製代碼

FragmentTransactioni能夠藉由add()、remove()、replace()實現對事務的操做,並且每次操做必須實現commit()才能生效。

若是想把fragment加入到返回棧,以便用戶退回到fragment原先的狀態的話,須要在commit()以前調用addBackStack()方法:

DetailFragment detailFragment = new DetailFragment();
 fragmentTransaction.add(detailFragment,"");
 fragmentTransaction.addToBackStack(null);
 fragmentTransaction.commit();複製代碼

爲了檢索返回棧中的fragment,必須監聽onBackPressed()事件,不然的話若是棧中存在其餘activity的話,就會直接返回到其餘activity中;若是沒有activity的話,就會直接退出應用。

@Override
    public void onBackPressed() {
        if (getFragmentManager().getBackStackEntryCount() > 0){
            getFragmentManager().popBackStack();
        }else {
            super.onBackPressed();
        }
    }複製代碼

添加改變到Transaction中的順序可有可無,除非:

  1. 最後調用了commit()方法
  2. 添加了多個改變到同一個container中,添加的順序決定了container中view層級的順序

若是沒有調用addBackStack(),當remove一個fragment時,commit以後會馬上銷燬,用戶沒法返回;若是添加到返回棧中,remove一個fragment時,commit以後會處於stop狀態,用戶能夠返回到以前的狀態(resume)

commit()這個操做運行在主線程中,也就意味着調用commit()以後要排序執行,並不會馬上執行事務。若是想馬上執行的話,能夠在commit()以前調用excutePendingTransactions(),不過通常並沒有此必要。

commit()必須在onSaveInstanceState()以前調用,由於在此以後,activity不會保存數據,致使commit()調用會拋出異常;若是容許狀態數據丟失的話,能夠調用 commitAllowingStateLoss()。

與Activity的通訊

雖然fragment做爲對象的實現和activity存在很大不一樣,可是做爲activity的一部分,fragment又和activity緊密聯繫。

  1. 從fragment中調用activity
    View listView = getActivity().findViewById(R.id.list);
  2. 從activity中調用fragment
    DetailFragment detailFragment1 = (DetailFragment) getSupportFragmentManager().findFragmentById(R.id.fragment_person_detail);複製代碼

    爲activity建立接口

    常見的模型:activity包含兩個fragment,左邊fragment顯示標題列表,右邊fragment顯示內容。點擊左側的fragment的標題,更新右側的fragment內容。實現方式就是在左邊fragment建立接口,並在接口中實現傳值方法,activity實現該接口,監聽方法,而後將接受到的值傳給右邊的fragment。

fragment建立接口和方法:

public static class FragmentA extends ListFragment {
    ...
    // Container Activity must implement this interface
    public interface OnArticleSelectedListener {
        public void onArticleSelected(Uri articleUri);
    }
    ...
}複製代碼

把activity轉換成接口,從而實現activity監聽

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");
        }
    }
    ...
}複製代碼

在fragment的onListItemClick事件中調用傳值方法給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); } ... }複製代碼

Fragment生命週期

和activity同樣,fragment也有三種基本狀態:

  1. 運行態:fragment在正在運行的activity中處於可見狀態
  2. 暫停態:另外一個activity位於前臺並獲取焦點,fragment所在的activity仍然可見(前臺activity半透明或沒有所有覆蓋fragment所在activity)
  3. 中止態:fragment所在activity處於stop狀態,或者fragment被remove掉,可是添加到返回棧中了。處於暫停態的fragment仍然存活(系統仍然保留狀態信息和成員變量),可是已再也不可見,且activity被銷燬時,fragment也會被銷燬。

fragment和activity生命週期的一個最大差異在於兩者在返回棧中的存儲。activity的返回棧是由系統管理的;fragment的返回棧是由activity管理的(當fragment被remove時,調用addBackStack())。

fragment若是須要context對象的話,能夠調用getActivity(),可是這個方法要在onAttach()以後、onDetach()以前調用纔有效,不然將會返回null。

fragment和activity生命週期比較
fragment和activity生命週期比較
相關文章
相關標籤/搜索