概述
最近寫項(xiàng)目的時(shí)候, 用到了fragment, 就想著系統(tǒng)學(xué)習(xí)一下,實(shí)現(xiàn)完還要整理下,畢竟技術(shù)這種東西,稍微時(shí)間長(zhǎng)點(diǎn)就會(huì)忘記.
設(shè)計(jì)原理
Android 在 Android 3.0(API 級(jí)別 11)中引入了片段,主要是為了給大屏幕(如平板電腦)上更加動(dòng)態(tài)和靈活的 UI 設(shè)計(jì)提供支持。由于平板電腦的屏幕比手機(jī)屏幕大得多,因此可用于組合和交換 UI 組件的空間更大。利用片段實(shí)現(xiàn)此類設(shè)計(jì)時(shí),您無需管理對(duì)視圖層次結(jié)構(gòu)的復(fù)雜更改。 通過將 Activity 布局分成片段,您可以在運(yùn)行時(shí)修改 Activity 的外觀,并在由 Activity 管理的返回棧中保留這些更改。
您應(yīng)該將每個(gè)片段都設(shè)計(jì)為可重復(fù)使用的模塊化 Activity 組件。也就是說,由于每個(gè)片段都會(huì)通過各自的生命周期回調(diào)來定義其自己的布局和行為,您可以將一個(gè)片段加入多個(gè) Activity,因此,您應(yīng)該采用可復(fù)用式設(shè)計(jì),避免直接從某個(gè)片段直接操縱另一個(gè)片段。 這特別重要,因?yàn)槟K化片段讓您可以通過更改片段的組合方式來適應(yīng)不同的屏幕尺寸。 在設(shè)計(jì)可同時(shí)支持平板電腦和手機(jī)的應(yīng)用時(shí),您可以在不同的布局配置中重復(fù)使用您的片段,以根據(jù)可用的屏幕空間優(yōu)化用戶體驗(yàn)。 例如,在手機(jī)上,如果不能在同一 Activity 內(nèi)儲(chǔ)存多個(gè)片段,可能必須利用單獨(dú)片段來實(shí)現(xiàn)單窗格 UI。

例如 — 仍然以新聞應(yīng)用為例 — 在平板電腦尺寸的設(shè)備上運(yùn)行時(shí),該應(yīng)用可以在 Activity A 中嵌入兩個(gè)片段。 不過,在手機(jī)尺寸的屏幕上,沒有足以儲(chǔ)存兩個(gè)片段的空間,因此Activity A 只包括用于顯示文章列表的片段,當(dāng)用戶選擇文章時(shí),它會(huì)啟動(dòng)Activity B,其中包括用于閱讀文章的第二個(gè)片段。 因此,應(yīng)用可通過重復(fù)使用不同組合的片段來同時(shí)支持平板電腦和手機(jī),如圖 1 所示。
Fragment生命周期
Fragment生命周期和其與Activity比較


左邊是fragment的生命周期, 右邊是于Activity的比較
Fragment的生命周期方法
- onAttach() : fragment于activity關(guān)聯(lián)時(shí)調(diào)用
- onCreate() : 系統(tǒng)創(chuàng)建Fragment所調(diào)用的方法, 注意該方法調(diào)用的時(shí)候,activity還在創(chuàng)建中.因此,activity中的相關(guān)視圖結(jié)構(gòu)還未被創(chuàng)建完成.如果想使用activity中的某些資源,要在onActivityCreated中獲取.
- onCreateView() : 系統(tǒng)會(huì)在首次繪制fragment用戶界面時(shí)調(diào)用此方法, 期望返回的view是fragment的布局視圖, 也可以返回null
繪制方法是
public View onCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
View view = inflate.inflate(R.layout.fragment1, container, false);
return view;
}
關(guān)于inflater方法我還有很多不理解的地方,以后會(huì)繼續(xù)學(xué)習(xí)整理 * onActivityCreated() : 表明activity已經(jīng)完成了自身的onCreate()方法, 即Activity已經(jīng)創(chuàng)建完成, activity的資源也已經(jīng)全部可用. * onStart() : 調(diào)用這個(gè)方法時(shí),就表明Fragment對(duì)用戶可見了, fragment的onStart是與Activity的onStart綁定的 * onResume() : 與Activity的onResume類似, 調(diào)用這個(gè)方法時(shí),表明fragment對(duì)用戶可見, 當(dāng)然fragment的onResume方法時(shí)基于Activity 的onResume方法的. 當(dāng)onResume方法結(jié)束后,就可以與用戶交互了 * onPause() : 與Activity的onPause綁定,并且作用也是類似 * onStop() : 與Activity的onStop綁定, 并且類似,表明fragment不在對(duì)用戶可見 * onDestroyView() : 當(dāng)之前在onCreateView中創(chuàng)建的視圖與fragment分離時(shí)調(diào)用, 如果下一次fragment想展示,就會(huì)創(chuàng)建一個(gè)新的視圖, 該方法在onStop之后, onDestroy之前調(diào)用.無論onCreateView是否返回非空視圖,都會(huì)調(diào)用該方法. 它會(huì)在視圖狀態(tài)保存后, 在它被父視圖移除之前被調(diào)用 * onDestroy() : 當(dāng)這個(gè)fragment不再使用時(shí)調(diào)用. * onDetach() : 取消fragment與activity的關(guān)聯(lián), 調(diào)用完后,不再擁有視圖層次結(jié)構(gòu),資源也都將被釋放.
Fragment的使用
靜態(tài)方式
靜態(tài)方式添加就是在布局文件中添加fragment
新建一個(gè)項(xiàng)目LearnFragment, 添加一個(gè)布局first_fragment.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#009688">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="This is first fragment"
android:gravity="center"/>
</LinearLayout>
然后創(chuàng)建繪制該視圖的java類
package com.example.hao.learnfragment.fragments;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.example.hao.learnfragment.R;
/**
* Created by hao on 17-4-20.
*/
public class FirstFragment extends Fragment {
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.first_fragment, container, false);
return view;
}
}
如法炮制,再創(chuàng)建second_fragment.xml和SecondFragment類, 然后修改MainActivity的布局文件activity_main,xml文件
<?xml version="1.0" encoding="utf-8"?>
<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="vertical">
<fragment
android:id="@+id/id_first_fragment"
android:name="com.example.hao.learnfragment.fragments.FirstFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
tools:layout="@layout/first_fragment" />
<fragment
android:id="@+id/id_second_fragment"
android:name="com.example.hao.learnfragment.fragments.SecondFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
tools:layout="@layout/second_fragment" />
</LinearLayout>
因?yàn)槲覀兪褂玫臅r(shí)v4下的Fragment, 所以MainActivity應(yīng)該繼承v4的FragmentActivity, 但是,API22之后就強(qiáng)制使用AppCompatActivity類,而該類已經(jīng)繼承了FragmentActivity, 故對(duì)于MainActivity不需要修改
感受下效果圖

動(dòng)態(tài)添加
當(dāng)然我們也可以通過動(dòng)態(tài)添加的方式在代碼中添加fragment, 這樣做的好處是能夠在Activity運(yùn)行期間隨時(shí)將fragment添加到Activity布局中;
如果想在Activity中執(zhí)行對(duì)fragment的操作,比如添加,移除或替換等, 可以分為幾個(gè)步驟
- 獲取FragmentManager實(shí)例, v4包中的獲取方法通過getSupportFragmentManager方法
- 開啟事務(wù), 通過FragmentManager實(shí)例的beginTransaction方法
- 向事務(wù)中添加容器id和fragment
- 調(diào)用commit方法使更改生效
動(dòng)態(tài)添加代碼為
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
ExampleFragment fragment = new ExampleFragment();
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();
還是修改上面的例子, activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<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="vertical">
<Button
android:id="@+id/id_button_first_fragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="First Fragment"/>
<Button
android:id="@+id/id_button_second_fragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Second Fragment"/>
<FrameLayout
android:id="@+id/frame_layout"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
然后再修改下MainActivity中的代碼
package com.example.hao.learnfragment;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import com.example.hao.learnfragment.fragments.FirstFragment;
import com.example.hao.learnfragment.fragments.SecondFragment;
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button firstFragment = (Button)findViewById(R.id.id_button_first_fragment);
firstFragment.setOnClickListener(this);
Button secondFragment = (Button)findViewById(R.id.id_button_second_fragment);
secondFragment.setOnClickListener(this);
}
@Override
public void onClick(View v) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
switch (v.getId()){
case R.id.id_button_first_fragment:
Fragment firstFragment = new FirstFragment();
fragmentTransaction.add(R.id.frame_layout, firstFragment);
break;
case R.id.id_button_second_fragment:
SecondFragment secondFragment = new SecondFragment();
fragmentTransaction.add(R.id.frame_layout, secondFragment);
break;
default:
}
fragmentTransaction.commit();
}
}
展示圖

后記
本部分主要記錄了Fragment的設(shè)計(jì)原理,生命周期,以及基本使用方法, 還有很多關(guān)于Fragment的知識(shí)并沒有說到,希望自己能夠多加練習(xí)關(guān)于Fragment的使用, 也期待能夠?qū)W習(xí)后續(xù)的知識(shí),繼續(xù)整理,前進(jìn).