基本介紹
1团甲、Fragment是依賴于Activity的,不能獨(dú)立存在黍聂。
2躺苦、一個(gè)Activity可以有多個(gè)Fragment。
3产还、一個(gè)Fragment可以被多個(gè)Activity重用匹厘。
4、Fragment有自己的生命周期脐区,并能接收輸入事件愈诚。
5、我們能在Activity運(yùn)行時(shí)動(dòng)態(tài)添加或刪除Fragment牛隅。
生命周期
通過這張圖可以看到Fragment的生命周期比Activity多幾個(gè)炕柔。
- onAttach(Activity)
當(dāng)Fragment與Activity發(fā)生關(guān)聯(lián)時(shí)調(diào)用 - onCreateView(LayoutInflater, ViewGroup, Bundle)
創(chuàng)建該Fragment視圖 - onActivityCreated(Bundle)
當(dāng)Activity的onCreate方法返回時(shí)調(diào)用 - onDestoryView()
與onCreateView想對(duì)應(yīng),當(dāng)該Fragment的視圖被移除時(shí)調(diào)用 - onDetach()
與onAttach相對(duì)應(yīng)倔叼,當(dāng)Fragment與Activity關(guān)聯(lián)被取消時(shí)調(diào)用
注意:除了OnCreateView()汗唱,其他所有方法如果你重寫了宫莱,必須調(diào)用父類對(duì)該方法是實(shí)現(xiàn)(super方法)丈攒。
靜態(tài)使用Fragment
這是使用Fragment最簡(jiǎn)單的一種方式,把Fragment當(dāng)成普通的控件,直接寫在Activity的布局中巡验。步驟:
1际插、 繼承Fragment,重寫onCreateView方法显设,填入布局
2框弛、在Activity中聲明此Fragment,就相當(dāng)普通控件一樣
下面展示一個(gè)例子(將Fragment作為Activity的布局)
Fragment的布局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center"
android:text="使用Fragment做主面板"
android:textSize="20sp"
android:textStyle="bold" />
</LinearLayout>
類直接繼承Fragment
public class ContentFragment extends Fragment{
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState){
return inflater.inflate(R.layout.fragment_content, container, false);
}
}
MainActivity
public class MainActivity extends Activity{
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
}
}
Activity布局文件
<RelativeLayout 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" >
<fragment
android:id="@+id/id_fragment_title"
android:name="com.application.demo.TitleFragment"
android:layout_width="match_parent"
android:layout_height="45dp" />
<fragment
android:layout_below="@id/id_fragment_title"
android:id="@+id/id_fragment_content"
android:name="com.application.demo.ContentFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
就是把Fragment當(dāng)成普通控件一樣在Activity中聲明捕捂,然后所有事件都交由Fragment處理瑟枫。
動(dòng)態(tài)使用Fragment
包括動(dòng)態(tài)添加、更新指攒、刪除Fragment
<RelativeLayout 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" >
<include
android:id="@+id/id_ly_bottombar"
android:layout_width="match_parent"
android:layout_height="55dp"
android:layout_alignParentBottom="true"
layout="@layout/bottombar" />
<FrameLayout
android:id="@+id/id_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@id/id_ly_bottombar" />
</RelativeLayout>
MainActivity代碼
public class MainActivity extends Activity implements OnClickListener{
private LinearLayout mTabWeixin;
private LinearLayout mTabFriend;
private ContentFragment mWeixin;
private FriendFragment mFriend;
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
// 初始化控件和聲明事件
mTabWeixin = (LinearLayout) findViewById(R.id.tab_bottom_weixin);
mTabFriend = (LinearLayout) findViewById(R.id.tab_bottom_friend);
mTabWeixin.setOnClickListener(this);
mTabFriend.setOnClickListener(this);
// 設(shè)置默認(rèn)的Fragment
setDefaultFragment();
}
private void setDefaultFragment(){
SupportFragmentManager fm = getSupportFragmentManager();
FragmentTransaction transaction = fm.beginTransaction();
mWeixin = new ContentFragment();
transaction.replace(R.id.id_content, mWeixin);
transaction.commit();
}
@Override
public void onClick(View v){
SupportFragmentManager fm = getSupportFragmentManager();
// 開啟Fragment事務(wù)
FragmentTransaction transaction = fm.beginTransaction();
switch (v.getId()){
case R.id.tab_bottom_weixin:
if (mWeixin == null){
mWeixin = new ContentFragment();
}
// 使用當(dāng)前Fragment的布局替代id_content的控件
transaction.replace(R.id.id_content, mWeixin);
break;
case R.id.tab_bottom_friend:
if (mFriend == null){
mFriend = new FriendFragment();
}
transaction.replace(R.id.id_content, mFriend);
break;
}
// transaction.addToBackStack();
// 事務(wù)提交
transaction.commit();
}
}
Fragment常用api:
- 獲取FragmentManager的方式(v4包)慷妙,getSupportFragmentManager()
- 主要的操作方法都是FragmentTransaction的方法
FragmentTransaction transaction = fm.benginTransatcion();//開啟一個(gè)事務(wù) - transaction.add()允悦;//往Activity中添加一個(gè)Fragment
- transaction.remove()膝擂;//從Activity中移除一個(gè)Fragment,如果被移除的Fragment沒有添加到回退棧(回退棧后面會(huì)詳細(xì)說)隙弛,這個(gè)Fragment實(shí)例將會(huì)被銷毀
- transaction.replace()架馋;//使用另一個(gè)Fragment替換當(dāng)前的,實(shí)際上就是remove()然后add()的合體
- transaction.hide()全闷;//隱藏當(dāng)前的Fragment叉寂,僅僅是設(shè)為不可見,并不會(huì)銷毀
- transaction.show()总珠; //顯示之前隱藏的Fragment
- detach()办绝;//會(huì)將view從UI中移除,和remove()不同,此時(shí)fragment的狀態(tài)依然由FragmentManager維護(hù)
- attach();//重建view視圖姚淆,附加到UI上并顯示
- transatcion.commit(); //提交一個(gè)事務(wù)
注意:常用Fragment的哥們孕蝉,可能會(huì)經(jīng)常遇到這樣Activity狀態(tài)不一致:
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1341)
at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1352)
at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:595)
at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:574)
該異常出現(xiàn)的原因是:commit()在onSaveInstanceState()后調(diào)用。首先腌逢,onSaveInstanceState()在onPause()之后降淮,onStop()之前調(diào)用。onRestoreInstanceState()在onStart()之后搏讶,onResume()之前佳鳖,所以commit方法一定要在Activity.onSaveInstance()之前調(diào)用
為了更好的使用Fragment,一定要清楚這些方法媒惕,哪個(gè)會(huì)銷毀視圖系吩,哪個(gè)會(huì)銷毀實(shí)例,哪個(gè)僅僅只是隱藏妒蔚。
比如:
- 我在FragmentA中的EditText填了一些數(shù)據(jù)穿挨,當(dāng)切換到FragmentB時(shí)月弛,如果希望回到A還能看到數(shù)據(jù),則適合你的就是hide和show科盛;也就是說帽衙,希望保留用戶操作的面板,你可以使用hide和show贞绵,當(dāng)然了不要使勁在那new實(shí)例厉萝,進(jìn)行下非null判斷。
- 我不希望保留用戶操作榨崩,你可以使用remove()谴垫,然后add();或者使用replace()這個(gè)和remove,add是相同的效果
- remove和detach有一點(diǎn)細(xì)微的區(qū)別母蛛,在不考慮回退棧的情況下弹渔,remove會(huì)銷毀整個(gè)Fragment實(shí)例,點(diǎn)擊back鍵后會(huì)銷毀Activity溯祸,而detach則只是銷毀其視圖結(jié)構(gòu)肢专,實(shí)例并不會(huì)被銷毀。那么二者怎么取舍使用呢焦辅?如果你的當(dāng)前Activity一直存在博杖,那么在不希望保留用戶操作的時(shí)候,你可以優(yōu)先使用detach筷登!
回退棧理解
類似與Android系統(tǒng)為Activity維護(hù)一個(gè)任務(wù)棧剃根,我們也可以通過Activity維護(hù)一個(gè)回退棧來保存每次Fragment事務(wù)發(fā)生的變化。如果你將Fragment任務(wù)添加到回退棧前方,當(dāng)用戶點(diǎn)擊后退按鈕時(shí)狈醉,將看到上一次的保存的Fragment。一旦Fragment完全從后退棧中彈出惠险,用戶再次點(diǎn)擊后退鍵苗傅,則退出當(dāng)前Activity。
如何添加一個(gè)Fragment事務(wù)到回退棧:
- 事務(wù)添加這個(gè)方法 addToBackStack(String sr)
下面通過例子說明一下班巩,功能如下:共有三個(gè)Fragment:F1, F2, F3渣慕,F(xiàn)1在初始化時(shí)就加入Activity,點(diǎn)擊F1中的按鈕跳轉(zhuǎn)到F2抱慌,點(diǎn)擊F2的按鈕跳轉(zhuǎn)到F3逊桦,點(diǎn)擊F3的按鈕回退到F1。
1抑进、在Activity的onCreate()中强经,將F1加入Activity中,并將F1添加到回退棧:
getSupportFragmentManager().beginTransaction()
.add(R.id.container, f1, "f1")
.addToBackStack(Fragment1.class.getSimpleName())
.commit();
F1按鈕的onClick()內(nèi)容如下寺渗,將F2添加到回退棧:
getFragmentManager().beginTransaction()
.replace(R.id.container, f2, "f2")
.addToBackStack(Fragment2.class.getSimpleName())
.commit();
F2按鈕的onClick()如下匿情,將F3添加到回退棧:
getFragmentManager().beginTransaction()
.replace(R.id.container, f3, "f3")
.addToBackStack(Fragment3.class.getSimpleName())
.commit();
F3按鈕的onClick()如下:
getFragmentManager().popBackStack(Fragment2.class.getSimpleName(),
FragmentManager.POP_BACK_STACK_INCLUSIVE);
這樣就回退到F1了兰迫,上面出現(xiàn)了一個(gè)popBackStack,這個(gè)是干什么用的呢码秉?下面來說一下:
1、addToBackStack()是加入回退棧鸡号,那么與之對(duì)應(yīng)的是什么呢转砖?就是上面提到的popBackStack()。他有幾種變化:
- popBackStack():將回退棧的棧頂彈出鲸伴,并回退該事務(wù)府蔗。
- popBackStack(String name, int flag):name為addToBackStack(String name)的參數(shù),通過name能找到回退棧的特定元素汞窗,flag可以為0或者FragmentManager.POP_BACK_STACK_INCLUSIVE姓赤,0表示只彈出該元素以上的所有元素,POP_BACK_STACK_INCLUSIVE表示彈出包含該元素及以上的所有元素仲吏。這里說的彈出所有元素包含回退這些事務(wù)不铆。
- popBackStack()是異步執(zhí)行的,是丟到主線程的MessageQueue執(zhí)行裹唆,popBackStackImmediate()是同步版本誓斥。
通過上面的一些例子我們大致對(duì)這個(gè)回退棧有了一些了解。
Fragment與Activity通信
因?yàn)閒ragment是依賴于Activity存在的许帐,通信并不復(fù)雜劳坑,大概歸納為:
1、如果你Activity中包含自己管理的Fragment的引用成畦,可以通過引用直接訪問所有的Fragment的public方法
2距芬、如果Activity中未保存任何Fragment的引用,那么沒關(guān)系循帐,每個(gè)Fragment都有一個(gè)唯一的TAG或者ID,可以通過getFragmentManager.findFragmentByTag()或者findFragmentById()獲得任何Fragment實(shí)例框仔,然后進(jìn)行操作。
3拄养、在Fragment中可以通過getActivity得到當(dāng)前綁定的Activity的實(shí)例存和,然后進(jìn)行操作。
*Fragment與Activity通信最佳實(shí)踐
因?yàn)橐紤]Fragment的重復(fù)使用衷旅,所以必須降低Fragment與Activity的耦合捐腿,而且Fragment更不應(yīng)該直接操作別的Fragment,畢竟Fragment操作應(yīng)該由它的管理者Activity來決定柿顶。
下面通過兩種方式實(shí)現(xiàn)fragment和Activity通信方式:
1茄袖、先看FragmentOne代碼:
public class FragmentOne extends Fragment implements OnClickListener{
private Button mBtn;
/**
* 設(shè)置按鈕點(diǎn)擊的回調(diào)
*/
public interface FOneBtnClickListener{
void onFOneBtnClick();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState){
View view = inflater.inflate(R.layout.fragment_one, container, false);
mBtn = (Button) view.findViewById(R.id.id_fragment_one_btn);
mBtn.setOnClickListener(this);
return view;
}
/**
* 交給宿主Activity處理,如果它希望處理
*/
@Override
public void onClick(View v){
if (getActivity() instanceof FOneBtnClickListener){
((FOneBtnClickListener) getActivity()).onFOneBtnClick();
}
}
}
可以看到現(xiàn)在的FragmentOne不和任何Activity耦合嘁锯,任何Activity都可以使用宪祥;并且我們聲明了一個(gè)接口聂薪,來回調(diào)其點(diǎn)擊事件,想要管理其點(diǎn)擊事件的Activity實(shí)現(xiàn)此接口就即可蝗羊〔匕模可以看到我們?cè)趏nClick中首先判斷了當(dāng)前綁定的Activity是否實(shí)現(xiàn)了該接口,如果實(shí)現(xiàn)了則調(diào)用耀找。
2翔悠、再看FragmentTwo:
public class FragmentTwo extends Fragment implements OnClickListener{
private Button mBtn ;
private FTwoBtnClickListener fTwoBtnClickListener ;
public interface FTwoBtnClickListener{
void onFTwoBtnClick();
}
//設(shè)置回調(diào)方法
public void setfTwoBtnClickListener(FTwoBtnClickListener fTwoBtnClickListener){
this.fTwoBtnClickListener = fTwoBtnClickListener;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState){
View view = inflater.inflate(R.layout.fragment_two, container, false);
mBtn = (Button) view.findViewById(R.id.id_fragment_two_btn);
mBtn.setOnClickListener(this);
return view ;
}
@Override
public void onClick(View v){
if(fTwoBtnClickListener != null){
fTwoBtnClickListener.onFTwoBtnClick();
}
}
}
與FragmentOne很類似,但是我們提供了setListener這樣的方法野芒,意味著Activity不僅需要實(shí)現(xiàn)該接口蓄愁,還必須顯示調(diào)用mFTwo.setfTwoBtnClickListener(this)。
3狞悲、最后看MainActivity
public class MainActivity extends Activity implements FOneBtnClickListener,
FTwoBtnClickListener{
private FragmentOne mFOne;
private FragmentTwo mFTwo;
private FragmentThree mFThree;
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mFOne = new FragmentOne();
FragmentManager fm = getFragmentManager();
FragmentTransaction tx = fm.beginTransaction();
tx.add(R.id.id_content, mFOne, "ONE");
tx.commit();
}
/**
* FragmentOne 按鈕點(diǎn)擊時(shí)的回調(diào)
*/
@Override
public void onFOneBtnClick(){
if (mFTwo == null){
mFTwo = new FragmentTwo();
mFTwo.setfTwoBtnClickListener(this);
}
FragmentManager fm = getFragmentManager();
FragmentTransaction tx = fm.beginTransaction();
tx.replace(R.id.id_content, mFTwo, "TWO");
tx.addToBackStack(null);
tx.commit();
}
/**
* FragmentTwo 按鈕點(diǎn)擊時(shí)的回調(diào)
*/
@Override
public void onFTwoBtnClick(){
if (mFThree == null){
mFThree = new FragmentThree();
}
FragmentManager fm = getFragmentManager();
FragmentTransaction tx = fm.beginTransaction();
tx.hide(mFTwo);
tx.add(R.id.id_content, mFThree, "THREE");
// tx.replace(R.id.id_content, fThree, "THREE");
tx.addToBackStack(null);
tx.commit();
}
}
上面兩種方法都能實(shí)現(xiàn)操作撮抓,到這里就結(jié)束了。
好文閱讀
https://mp.weixin.qq.com/s/dUuGSVhWinAnN9uMiBaXgw
https://blog.csdn.net/lmj623565791/article/details/37970961