Fragment 相關知識點都在這裏了

簡介

Fragment (簡稱碎片)是 Android 3.0(API 11)提出的。爲了兼容低版本 support-v4 庫中也開發了一套Fragment API 最低兼容到 Android 1.6 的版本。java

過去 support-v4 庫是一個 jar 包,從 24.2.0 版本開始,將 support-v4 庫模塊化爲多個 jar 包。包含 support-fragment、 support-ui、support-media-compat 等。這麼作是爲了減小 APK 包大小,項目中須要用哪一個模塊就引入哪一個模塊。android

// 引入整個 support-v4 庫
compile 'com.android.support:support-v4:24.2.1'
//只引入 support-fragment 庫
compile 'com.android.support:support-fragment:24.2.1'
複製代碼

由於 support 庫是不斷更新的,所以推薦使用 support 庫中的 android.support.v4.app.Fragment,而不要用系統自帶的 android.app.Fragment。若是使用 support 庫的 Fragment,Activity 就必需要繼承 FragmentActivity(AppCompatActivity 是 FragmentActivity 的子類)。git

Fragment 的特色

  • Fragment 是依賴於 Activity 的,不能獨立存在的。
  • 一個 Activity 裏能夠有多個 Fragment。
  • 一個 Fragment 能夠被多個 Activity 重用。
  • Fragment 有本身的生命週期,並能接收輸入事件。
  • 能夠在 Activity 運行時動態地添加或刪除 Fragment。

Fragment 的優點

  • 模塊化(Modularity):咱們沒必要把全部代碼所有寫在 Activity 中,能夠把代碼寫在各自的 Fragment 中。
  • 可重用(Reusability):多個 Activity 能夠重用一個 Fragment。
  • 可適配(Adaptability):根據硬件的屏幕尺寸、屏幕方向,可以方便地實現不一樣的佈局,這樣用戶體驗更好。

生命週期

Fragment 與 Activity 生命週期很類似,與 Activity 同樣,Fragment 也有三種狀態:github

  • Resumed:Fragment 在運行中的 Activity 中可見。
  • Paused:另外一個 Activity 處於最頂層,可是 Fragment 所在的 Activity 並無被徹底覆蓋(頂層的 Activity 是半透明的或不佔據整個屏幕)。
  • Stoped:Fragment 不可見,多是它所在的 Activity 處於 stoped 狀態或是 Fragment 被刪除並添加到後退棧中了,此狀態的 Fragment 仍然存在於內存中。

Fragment 生命週期

Activity 直接影響它所包含的 Fragment 的生命週期,因此對 Activity 的某個生命週期方法的調用也會產生對Fragment 相同方法的調用。例如:當 Activity 的 onPause() 方法被調用時,它所包含的全部的 Fragment 的onPause() 方法都會被調用。bash

Fragment 比 Activity 還要多出幾個生命週期回調方法,這些額外的方法是爲了與 Activity 的交互,以下:app

  • onAttach()

當 Fragment 被加入到 Activity 時調用(在這個方法中能夠得到所在的 Activity)。異步

  • onCreateView()

當 Activity 要獲得 Fragment 的 layout 時,調用此方法,Fragment 在其中建立本身的 layout (界面)。ide

  • onActivityCreated()

當 Activity 的 onCreated() 方法返回後調用此方法。模塊化

  • onDestroyView()

當 Fragment 的 layout 被銷燬時被調用。佈局

  • onDetach()

當 Fragment 被從 Activity 中刪掉時被調用。

一旦 Activity 進入 resumed 狀態(也就是 running 狀態),你就能夠自由地添加和刪除 Fragment 了。所以,只有當 Activity 在 resumed 狀態時,Fragment 的生命週期才能獨立的運轉,其它時候是依賴於 Activity 的生命週期變化的。

使用方式

這裏給出 Fragment 最基本的使用方式。首先,建立繼承 Fragment 的類,名爲 BlankFragment:

public class BlankFragment extends Fragment {
    private static final String ARG_PARAM = "param_key";
    private String mParam;

    public BlankFragment() { }

    public static BlankFragment newInstance(String param) {
        BlankFragment fragment = new BlankFragment();
        Bundle args = new Bundle();
        args.putString(ARG_PARAM, param);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            mParam = getArguments().getString(ARG_PARAM);
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_blank, container, false);
        // View 初始化,findViewById() 等操做
        return view;
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        // 初始化數據,加載數據等...
    }
}
複製代碼

靜態添加

經過 xml 的方式添加,缺點是一旦添加就不能在運行時刪除。

<fragment android:id="@+id/fg_content" android:layout_width="match_parent" android:layout_height="match_parent" android:name="com.jeanboy.text.ui.fragment.BlankFragment" />
複製代碼

動態添加

運行時添加,這種方式比較靈活,所以建議使用這種方式。

這裏只給出動態添加的方式。首先 Activity 須要有一個容器存放 Fragment,通常是 FrameLayout,所以在 Activity 的佈局文件中加入 FrameLayout:

<FrameLayout android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent" />
複製代碼

而後在 onCreate() 中,經過如下代碼將 Fragment 添加進Activity中。

getSupportFragmentManager().beginTransaction()
        .add(R.id.container, BlankFragment.newInstance("hello world"), "f1")
        .commit();
複製代碼

這裏須要注意幾點:

  • 由於咱們使用了support庫的Fragment,所以須要使用 getSupportFragmentManager() 獲取 FragmentManager。

  • add() 是對 Fragment 衆多操做中的一種,還有 remove()replace() 等。

    第一個參數是根容器的 id(FrameLayout 的 id,即 @id/container),第二個參數是 Fragment 對象,第三個參數是 Fragment 的 tag 名,指定 tag 的好處是後續咱們能夠經過:

    Fragment1 frag = getSupportFragmentManager().findFragmentByTag("f1");
    複製代碼

    從 FragmentManager 中查找 Fragment 對象。

  • 在一次事務中,能夠作多個操做,好比同時作 add().remove().replace()

  • commit() 操做是異步的,內部經過 mManager.enqueueAction() 加入處理隊列。

    對應的同步方法爲 commitNow()commit() 內部會有 checkStateLoss() 操做,若是開發人員使用不當(好比 commit() 操做在 onSaveInstanceState() 以後),可能會拋出異常。而 commitAllowingStateLoss() 方法則是不會拋出異常版本的 commit() 方法,可是儘可能使用 commit(),而不要使用 commitAllowingStateLoss()

  • addToBackStack("fname") 是可選的。

    FragmentManager 擁有回退棧(BackStack),相似於 Activity 的任務棧,若是添加了該語句,就把該事務加入回退棧,當用戶點擊返回按鈕,會回退該事務(回退指的是若是事務是 add(frag1),那麼回退操做就是 remove(frag1) );若是沒添加該語句,用戶點擊返回按鈕會直接銷燬 Activity。

Fragment 通訊

Fragment 向 Activity 傳遞數據

首先,在 Fragment中 定義接口,並讓 Activity 實現該接口。

public interface OnFragmentCallback {
    void onCallback(String value);
}
複製代碼

在 Fragment 的 onAttach() 中,將參數 Context 強轉爲 OnFragmentCallback 對象:

@Override
public void onAttach(Context context) {
    super.onAttach(context);
    if (context instanceof OnFragmentCallback) {
        callback = (OnFragmentCallback) context;
    } else {
        throw new RuntimeException(context.toString()
                                   + " must implement OnFragmentCallback");
    }
}
複製代碼

Activity 向 Fragment 傳遞數據

Activity 向 Fragment 傳遞數據比較簡單,獲取 Fragment 對象,並調用 Fragment 的方法便可。好比要將一個字符串傳遞給 Fragment,則在 Fragment 中定義方法:

public void setString(String data) { 
    this.data = data;
}
複製代碼

並在 Activity 中調用 fragment.setString("hello") 便可。

Fragment 之間通訊

因爲 Fragment 之間是沒有任何依賴關係的,所以若是要進行 Fragment 之間的通訊,建議經過 Activity 做爲中介,不要 Fragment 之間直接通訊。

DialogFragment

DialogFragment 是 Android 3.0 提出的,代替了 Dialog,用於實現對話框。它的優勢是:即便旋轉屏幕,也能保留對話框狀態。

若是要自定義對話框樣式,只須要繼承 DialogFragment,並重寫 onCreateView(),該方法返回對話框 UI。這裏咱們舉個例子,實現進度條樣式的圓角對話框。

public class ProgressDialogFragment extends DialogFragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        //消除Title區域
        getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
        //將背景變爲透明
        getDialog().getWindow()
            .setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
        //點擊外部不可取消
        setCancelable(false);
        View root = inflater.inflate(R.layout.fragment_progress_dialog, container);
        return root;
    }

    public static ProgressDialogFragment newInstance() {
        return new ProgressDialogFragment();
    }
}
複製代碼

而後經過下面代碼顯示對話框:

ProgressDialogFragment fragment = ProgressDialogFragment.newInstance();
fragment.show(getSupportFragmentManager(), "tag");//顯示對話框
fragment.dismiss();//關閉對話框
複製代碼

個人 GitHub

github.com/jeanboydev

個人公衆號

歡迎關注個人公衆號,分享各類技術乾貨,各類學習資料,職業發展和行業動態。

Android 波斯灣

技術交流羣

歡迎加入技術交流羣,來一塊兒交流學習。

QQ 技術交流羣

QQ 技術交流羣
相關文章
相關標籤/搜索