前言:除了安卓四大組件(
activity、service、content provider、broadcast receiver)之外,還有一個(gè)最重要的知識(shí)點(diǎn)是:fragment。
1. Fragment 是什么?
Fragment 也叫碎片,片段(相當(dāng)于 迷你的 Activity ,或者是 Activity 的模塊化的組件)。是 Google 在 Android 3.0 引入的,主要為了給大屏幕的 UI 提供支持的。
實(shí)際開(kāi)發(fā)中兩個(gè)作用:
- 相當(dāng)于海報(bào)的功能,可以到處粘貼。就是把一部分業(yè)務(wù)邏輯和 UI 封裝在一起,方便靈活的用在各個(gè)地方。
- 適配 Pad 和手機(jī)。只用一套代碼適配,方便重用,提高程序的復(fù)用性和可維護(hù)性。
Fragment 可以理解為一個(gè)迷你的 Activity 或者是 Activity 的模塊化的組件,它有自己的生命周期與顯示界面,我們可以利用多個(gè) Fragment 嵌套在 Activity 達(dá)到以下的功能,如適配平板,或適配橫豎屏幕,或者在程序運(yùn)行的過(guò)程中動(dòng)態(tài)的更改我們的 UI 界面。如下圖:

上圖是顯示應(yīng)用運(yùn)行在手機(jī)情況下,從一個(gè)列表頁(yè)面跳轉(zhuǎn)到詳細(xì)頁(yè)面的例子。如果我們不使用 Fragment 的情況,當(dāng)應(yīng)用運(yùn)行在平板上面的情況,就只能顯示放大版的手機(jī)顯示的界面,為了利用平板的特性,Android 在 3.0 版本中提供了Fragment 技術(shù),我們就可以將列表與內(nèi)容以組件的方式插入,在手機(jī)上分屏顯示兩個(gè)內(nèi)容,在平板上面就可以左右顯示兩個(gè)內(nèi)容。
你可以將片段視為 Activity 的模塊化組成部分,它具有自己的生命周期,能接收自己的輸入事件,并且你可以在 Activity 運(yùn)行時(shí)添加或刪除片段(有點(diǎn)像你可以在不同 Activity 中重復(fù)使用的“子 Activity ”)。
片段必須始終嵌入在 Activity 中,其生命周期直接受宿主 Activity 生命周期的影響。
2. 如何使用 Fragment
2.1 創(chuàng)建 Fragment
如果需要使用 Fragment 的話,需繼承 Fragment 類,目前有兩個(gè) Fragment 類:
-
import android.support.v4.app.Fragment提供的向下兼容類(支持3.0以下版本) -
import android.app.Fragment提供3.0以上版本的類(只支持3.0以上的版本)
繼承 Fragment 至少需實(shí)現(xiàn)以下方法:
-
onCreateView()系統(tǒng)會(huì)在片段首次繪制其用戶界面時(shí)調(diào)用此方法,并將創(chuàng)建的UI界面返回。
import android.support.v4.app.Fragment;
public class FirstFragment extends Fragment {
@Nullable
@Override
/* 系統(tǒng)會(huì)在片段首次繪制其用戶界面時(shí)調(diào)用此方法,并將創(chuàng)建的UI界面返回 */
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
/* 返回一個(gè)view,把布局返回去 */
View view = inflater.inflate(R.layout.fragment_first, null);
return view;
}
}
2.2 使用 Fragment
方式1:在 Activity 的布局文件中添加一個(gè) Fragment 控件
<!-- 使用 XML 添加 fragment 子模塊 -->
<fragment android:name="com.example.testfragment.FirstFragment"
android:id="@+id/abc"
android:layout_width="match_parent"
android:layout_height="match_parent" />
android:name 屬性指定要在布局中實(shí)例化的 Fragment 類。注意如果是在 xml 中聲明的 Fragment 的話,必須給這個(gè) Fragment 設(shè)置 id 或者 tag。
方式2:動(dòng)態(tài)添加一個(gè) Fragment 到 Activity 指定的控件內(nèi)部
FirstFragment firstFragment = new FirstFragment();
// 獲取到低版本兼容的 FragmentManager( getFragmentManager(); 支持3.0以上版本 )
FragmentManager fm = getSupportFragmentManager();
// 1.開(kāi)啟一個(gè) Fragment 事務(wù)(即可對(duì) Fragment 進(jìn)行操作)
FragmentTransaction transaction = fm.beginTransaction();
// 2.添加一個(gè) Fragment 到 Activity 指定的控件內(nèi)部(每調(diào)用一次 add 方法,就添加一次,會(huì)重復(fù)疊加)
// (參數(shù):布局容器 FrameLayout 的 ID,fragment 對(duì)象,動(dòng)態(tài)添加時(shí)需要設(shè)置一個(gè) id/tag)
transaction.add(R.id.content, firstFragment, "abc");
// 3.提交(對(duì) Fragment 進(jìn)行任何操作都必須提交)
transaction.commit();
2.3 傳值給 Fragment
- 通過(guò)Fragment的方法、或者回調(diào)進(jìn)行傳值
- 給 Fragment 傳遞參數(shù)
setArguments
// 頁(yè)面之間的傳值:Bundle是傳遞數(shù)據(jù)的集合
Bundle bundle = new Bundle();
bundle.putString("title", "Fragment的標(biāo)題");
// bundle.putCharSequence("title", title);
// 通過(guò) Bundle 把參數(shù)從 Activity 中傳給 Fragment
firstFragment.setArguments(bundle);
- 在 Fragment 中接收參數(shù)
getArguments
/* 接收傳遞的參數(shù) */
Bundle bundle = getArguments();
String title = bundle.getString("title");
3. Fragment 的生命周期

創(chuàng)建的生命周期:
-
onAttach:Fragment 開(kāi)始與 Activity 關(guān)聯(lián)。 -
onCreate:系統(tǒng)會(huì)在創(chuàng)建片段時(shí)調(diào)用此方法。你應(yīng)該在實(shí)現(xiàn)內(nèi)初始化您想在片段暫?;蛲V购蠡謴?fù)時(shí)保留的必需片段組件。 -
onCreateView:系統(tǒng)會(huì)在片段首次繪制其用戶界面時(shí)調(diào)用此方法。 要想為你的片段繪制 UI,你從此方法中返回的 View 必須是片段布局的根視圖。如果片段未提供 UI,你可以返回 null。 -
onActivityCreated:Activity onCreate 完成的回調(diào)。 -
onSaveInstanceState:移除的生命周期,處理 Fragment 需要保存數(shù)據(jù)的方法。 -
onDestroyView:將 Fragment 的 View 試圖從UI中移除。 -
onDetach:將 Fragment 與 Activity 取消關(guān)聯(lián)。
4. Fragment 的操作 FragmentTransaction
我們可以使用 FragmentTransaction 來(lái)對(duì) Fragment 進(jìn)行操作,如 add、replace、attach、detach、remove、show、hide。
4.1 add() 方法
作用:將一個(gè) Fragment 添加到一個(gè)頁(yè)面,可以重復(fù)疊加
add(int containerViewId, Fragment fragment, String tag)
- containerViewId:將 Fragment 加入的 ViewGroup 的 id
- fragment:被操作的 Fragment
- tag:被操作的 Fragment 的標(biāo)示,我們操作成功后可以使用這個(gè) tag 找到相應(yīng)的 Fragment
FragmentTransaction transaction = fm.beginTransaction();
transaction.add(R.id.content, firstFragment, "abc");
transaction.commit();
4.2 replace() 方法
作用:將容器內(nèi)的 Fragemnt 移除后,再進(jìn)行添加
replace(int containerViewId, Fragment fragment, String tag)
- containerViewId:將 Fragment 替換的 ViewGroup 的 id
- fragment:被操作的 Fragment
- tag:被操作的 Fragment 的標(biāo)示,我們操作成功后可以使用這個(gè) tag 找到相應(yīng)的 Fragment
FragmentTransaction transaction = fm.beginTransaction();
transaction.replace(R.id.content, firstFragment, "abc");
transaction.commit();
4.3 detach() 方法
作用:將一個(gè) Fragment 從 UI 上面解綁
Fragment fragment = fm.findFragmentByTag("abc");
FragmentTransaction transaction = fm.beginTransaction();
transaction.detach(fragment);
- 注意解綁并不是刪除,我們還可以通過(guò)其他方法把解綁的Fragment重新綁定到UI上面。
4.4 attach() 方法
作用:將一個(gè) Fragment 重新綁定到 UI
FragmentManager fm = getSupportFragmentManager();
Fragment fragment = fm.findFragmentByTag("abc");
FragmentTransaction transaction = fm.beginTransaction();
transaction.attach(fragment);
transaction.commit();
4.5 remove() 方法
作用:將一個(gè) Fragment 從 Activity 刪除
FragmentManager fm = getSupportFragmentManager();
Fragment fragment = fm.findFragmentByTag("abc");
FragmentTransaction transaction = fm.beginTransaction();
transaction.remove(fragment);
transaction.commit();
4.6 show() 方法
作用:顯示一個(gè) Fragment(不會(huì)調(diào)用任何生命周期方法,常用這個(gè))
FragmentManager fm = getSupportFragmentManager();
Fragment fragment = fm.findFragmentByTag("abc");
FragmentTransaction transaction = fm.beginTransaction();
transaction.show(fragment);
transaction.commit();
4.7 hide() 方法
作用:隱藏一個(gè) Fragment(不會(huì)調(diào)用任何生命周期方法,常用這個(gè))
FragmentManager fm = getSupportFragmentManager();
Fragment fragment = fm.findFragmentByTag("abc");
FragmentTransaction transaction = fm.beginTransaction();
transaction.hide(fragment);
transaction.commit();
4.8 commit() 和 commitAllowingStateLoss() 方法的區(qū)別
Activity 在以下的操作下容易引起回收并觸發(fā) onSaveInstanceState()
- 按下 HOME 鍵
- 按下電源按鍵(關(guān)閉屏幕顯示)時(shí)
- 屏幕方向切換
- Activity 跳轉(zhuǎn)的時(shí)候
以上幾種情況下不能進(jìn)行狀態(tài)的提交,如果提交的話會(huì)出現(xiàn)以下錯(cuò)誤
Can not perform this action after onSaveInstanceState
出現(xiàn)這個(gè)錯(cuò)誤的時(shí)候,我們有兩個(gè)解決方法:
- 不在 onSaveInstanceState() 后調(diào)用 commit() 方法
- 使用 commitAllowingStateLoss() 方法
為了防止在 onSaveInstanceState 方法中調(diào)用 commit 會(huì)報(bào)錯(cuò),我們使用 commitAllowingStateLoss,可以防止?fàn)顟B(tài)丟失報(bào)錯(cuò)!
5. Fragment 與返回鍵
默認(rèn)情況下,F(xiàn)ragment 是不會(huì)響應(yīng)返回鍵的。如果需要做到類似Activity回退到上一個(gè)界面這樣的效果,必須將 FragmentTransaction 加入返回棧。
FragmentTransaction transaction = fm.beginTransaction();
transaction.add(R.id.content, firstFragment, "abc" + index);
// 加入返回任務(wù)棧,可通過(guò)系統(tǒng)返回按鈕控制返回
transaction.addToBackStack(null); // 任務(wù)名可為空,也可通過(guò)任務(wù)名控制多層返回
addToBackStack(String name);
name 參數(shù)代表這次 FragmentTransion 的名稱,我們可以根據(jù)這 個(gè)名稱找到相應(yīng)的操作,并進(jìn)行回退動(dòng)作。也可以傳null,代表不記錄該次操作的名稱。
如果我們將某次操作加入回退棧的話,我們有以下幾種方式進(jìn)行回退:
- 使用返回鍵系統(tǒng)自動(dòng)回退到上一次操作前的狀態(tài)。
- 使用 popBackStack 回退到指定的操作的狀態(tài)。
popBackStack()回退到上一次操作前的狀態(tài) 。
popBackStack(String name, int flags)回退到某次 name 的操作狀態(tài)。
flag 為0表示回退到任務(wù) name 的操作狀態(tài)的這一步。
flag 為POP_BACK_STACK_INCLUSIVE表示回退到任務(wù) name 的操作狀態(tài)的上一步。
popBackStack(int id, int flags) 回退某個(gè) id 的操作狀態(tài),id 為 commit() 返回的。
@Override
public void onBackPressed() {
Log.i("11", "點(diǎn)擊了系統(tǒng)返回按鈕");
super.onBackPressed();
FragmentManager fm = getSupportFragmentManager();
/* 返回到上一頁(yè) */
// fm.popBackStack();
/* 返回到指定頁(yè)(參數(shù):加入返回任務(wù)棧的任務(wù)名, 標(biāo)記) */
fm.popBackStack("abc", 0);
}
6. Fragment 動(dòng)畫(huà)
默認(rèn)情況下 Fragment 顯示和隱藏是不顯示動(dòng)畫(huà)的,不過(guò) FragmentTransaction 提供了三種顯示的動(dòng)畫(huà)的方式:
-
setTransition:使用系統(tǒng)提供的默認(rèn)顯示動(dòng)畫(huà) -
setCustomAnimations:使用自定義動(dòng)畫(huà)。 -
setTransition (int transit):可以使用系統(tǒng)提供的默認(rèn)動(dòng)畫(huà),可供選擇的有 TRANSIT_NONE,TRANSIT_FRAGMENT_OPEN,or TRANSIT_FRAGMENT_CLOSE -
setCustomAnimations (int enter, int exit, int popenter, int popexit):能夠使用自定義動(dòng)畫(huà)。
enter 設(shè)置Fragment 進(jìn)入動(dòng)畫(huà)
exit 設(shè)置Fragment 退出動(dòng)畫(huà)
popenter popback 后回滾狀態(tài)后上一個(gè) Fragment 的動(dòng)畫(huà)
popexit popback 后回滾狀態(tài)后當(dāng)前的Fragment的動(dòng)畫(huà)
7. Fragment 間的交互(通信)
使用回調(diào)的方式,讓 FragmentActivity 充當(dāng)中間交互的橋梁。
先定義一個(gè)接口(一種規(guī)范,這里相當(dāng)于 FragmentActivity 的基類,方便多個(gè)不同的 Activity 使用),再在 FragmentActivity 中實(shí)現(xiàn)接口中定義的方法。
1》定義一個(gè)接口(OneOnClickListener.java)
package net.cbi360.testfragment;
public interface OneOnClickListener {
public void OneOnClick(int index);
}
2》實(shí)現(xiàn)接口的方法
public class CommonActivity extends FragmentActivity implements OneOnClickListener {
TwoFragment two;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_common);
FragmentManager fm = getSupportFragmentManager();
OneFragment one = (OneFragment)fm.findFragmentByTag("one");
one.setOneOnClickListener(this);
two = (TwoFragment)fm.findFragmentByTag("two");
}
public void OneOnClick(int index) {
String msg = "點(diǎn)擊了" + index + "按鈕";
Log.i("CommonActivity", msg);
two.setMessage(msg);
}
}
public class OneFragment extends Fragment {
OneOnClickListener activity;
int index = 0;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (null != savedInstanceState) {
// 恢復(fù)參數(shù)的值
index = savedInstanceState.getInt("index");
}
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_one, null);
Button button = (Button)view.findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//Log.i("OneFragment", "點(diǎn)擊了按鈕");
index++;
activity.OneOnClick(index);
}
});
return view;
}
// public void setActivity(CommonActivity activity) {
// this.activity = activity;
//
// }
// 解耦(減少關(guān)聯(lián)性,方便不同activity重用)
public void setOneOnClickListener(OneOnClickListener activity) {
this.activity = activity;
}
// @Override
// public void onActivityCreated(@Nullable Bundle savedInstanceState) {
// super.onActivityCreated(savedInstanceState);
// // 也可以從生命周期方法中拿到 Activity
// activity = (OneOnClickListener)getActivity();
// }
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
// 保存參數(shù)的值
outState.putInt("index", index);
}
}
- 解決頁(yè)面翻轉(zhuǎn)后,數(shù)據(jù)丟失問(wèn)題
/* 這個(gè)方法的作用是:當(dāng)頁(yè)面的狀態(tài)/參數(shù)值丟失時(shí)我們來(lái)進(jìn)行保存(緩存頁(yè)面參數(shù)的值) */
@Override
// 當(dāng)頁(yè)面翻轉(zhuǎn)時(shí)/按下Home鍵時(shí)/按下電源鍵時(shí)/Activity跳轉(zhuǎn)時(shí),會(huì)觸發(fā)下面這個(gè)方法
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
// 保存值
outState.putInt("index", index);
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 獲取保存的值(注意:先判斷一下是否有上次緩存的信息,有的話再獲?。? if (null != savedInstanceState) {
index = savedInstanceState.getInt("index");
}
}