Fragment是什么?
Fragment是Android3.0后引入的一個新的API贰健,他出現(xiàn)的初衷是為了適應大屏幕的平板電腦胞四, 當然現(xiàn)在他仍然是平板APP UI設計的寵兒,而且我們普通手機開發(fā)也會加入這個Fragment伶椿, 我們可以把他看成一個小型的Activity辜伟,又稱Activity片段!想想导狡,如果一個很大的界面枚赡,我們 就一個布局料皇,寫起界面來會有多麻煩娜膘,而且如果組件多的話是管理起來也很麻煩匕争!而使用Fragment 我們可以把屏幕劃分成幾塊跑杭,然后進行分組,進行一個模塊化的管理惹盼!從而可以更加方便的在 運行過程中動態(tài)地更新Activity的用戶界面枉昏!另外Fragment并不能單獨使用晰奖,他需要嵌套在Activity 中使用蛆楞,盡管他擁有自己的生命周期,但是還是會受到宿主Activity的生命周期的影響,比如Activity 被destory銷毀了播演,他也會跟著銷毀!
靜態(tài)使用Fragment
- 繼承Fragmentt或者它的子類崖瞭,重寫onCreateView決定Fragemnt的布局雌续。
public class FirstFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view=inflater.inflate(R.layout.fragment_first, container, false);
TextView tv_hello= (TextView) view.findViewById(R.id.tv_hello);
tv_hello.setText("MyFirstFragment");
return view;
}
}
布局代碼:
<TextView
android:id="@+id/tv_hello"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="HelloWorld" />
- 在Activity中使用此Fragment核畴,就當和使用普通的View一樣温学。
使用Fragment的Activity的布局代碼:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.lg.www.blog.MainActivity">
<fragment
android:id="@+id/fragment1"
android:name="com.lg.www.blog.FirstFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1" />
</RelativeLayout>
- Fragment是在android3.0版本引入的,如果你使用的是3.0之前的系統(tǒng),需要先導入android-support-v4的jar包才能使用Fragment功能,所在的那個Activity就要繼承FragmentActivity,且Fragment的方法都要使用帶Support的那個(如getSupportFragmentManager()方法);本專題里面所有的Fragment都是沒有兼容3.0之前版本的炎码。
動態(tài)使用Fragment
- 將靜態(tài)使用時的Activity的布局代碼上的fragment刪除辖众。
-
動態(tài)加載流程圖:
- 關鍵代碼:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FirstFragment fragment = new FirstFragment();
getFragmentManager().beginTransaction().add(R.id.activity_main, fragment).commit();
}
}
Fragment的生命周期圖
- Fragment需要嵌套在Activity中使用,當然也可以嵌套到另外一個Fragment中,但這個被嵌套 的Fragment也是需要嵌套在Activity中的,間接地說,Fragment還是需要嵌套在Activity中! 受寄主Activity的生命周期影響,當然他也有自己的生命周期!另外不建議在Fragment里面 嵌套Fragment因為嵌套在里面的Fragment生命周期不可控!
- 官方文檔說創(chuàng)建Fragment時至少需要實現(xiàn)三個方法:onCreate( ),onCreateView( ),OnPause( ); 不過貌似只寫一個onCreateView也是可以的瓢捉;除了onCreateView方法频丘,其他的所有方法如果你重寫了,必須調(diào)用父類對于該方法的實現(xiàn)泡态。
- 停止狀態(tài)的fragment仍然活著(所有狀態(tài)和成員信息被系統(tǒng)保持著),然而,它對用戶不再可見,并且如果activity被干掉,他也會被干掉.
- 走一趟生命周期圖:
①Activity加載Fragment的時候,依次調(diào)用下面的方法: onAttach -> onCreate -> onCreateView -> onActivityCreated -> onStart ->onResume
②當我們弄出一個懸浮的對話框風格的Activity,或者其他,就是讓Fragment所在的Activity可見,但不獲得焦點 onPause
③當對話框關閉,Activity又獲得了焦點: onResume
④當我們替換Fragment,并調(diào)用addToBackStack()將他添加到Back棧中 onPause -> onStop -> onDestoryView B!注意,此時的Fragment還沒有被銷毀哦!!!
⑤當我們按下鍵盤的回退鍵某弦,F(xiàn)ragment會再次顯示出來: onCreateView -> onActivityCreated -> onStart -> onResume
⑥如果我們替換后,在事務commit之前沒有調(diào)用addToBackStack()方法將 Fragment添加到back棧中的話;又或者退出了Activity的話,那么Fragment將會被完全結(jié)束, Fragment會進入銷毀狀態(tài) onPause -> onStop -> onDestoryView -> onDestory -> onDetach
Fragment管理與Fragment事務
- Fragment常用的三個類:
android.app.Fragment 主要用于定義Fragment
android.app.FragmentManager 主要用于在Activity中操作Fragment
android.app.FragmentTransaction 保證一些列Fragment操作的原子性桐汤,熟悉事務這個詞,一定能明白靶壮。 - 獲取FragmentManage的方式:
getFragmentManager() 怔毛,v4中,getSupportFragmentManager - 主要的操作:都是FragmentTransaction的方法
FragmentTransaction transaction = fm.benginTransatcion();//開啟一個事務
- transaction.add()
往Activity中添加一個Fragment - transaction.remove()
從Activity中移除一個Fragment腾降,如果被移除的Fragment沒有添加到回退棧(回退棧后面會詳細說)拣度,這個Fragment實例將會被銷毀。 - transaction.replace()
使用另一個Fragment替換當前的,實際上就是remove()抗果,然后add()的合體 - transaction.hide()
隱藏當前的Fragment筋帖,僅僅是設為不可見,并不會銷毀 - transaction.show()
顯示之前隱藏的Fragment - transaction.detach()
會將view從UI中移除,和remove()不同,此時fragment的狀態(tài)依然由FragmentManager維護窖张。 - transaction.attach()
重建view視圖幕随,附加到UI上并顯示蚁滋。
transatcion.commit()//提交一個事務宿接,在一個事務開啟到提交可以進行多個的添加、移除辕录、替換等操作睦霎。
- 值得注意的是:如果你喜歡使用Fragment,一定要清楚這些方法走诞,哪個會銷毀視圖副女,哪個會銷毀實例,哪個僅僅只是隱藏蚣旱,這樣才能更好的使用它們碑幅。
a、比如:我在FragmentA中的EditText填了一些數(shù)據(jù)塞绿,當切換到FragmentB時沟涨,如果希望會到A還能看到數(shù)據(jù),則適合你的就是hide和show异吻;也就是說裹赴,希望保留用戶操作的面板,你可以使用hide和show诀浪,當然了不要使勁在那new實例棋返,進行下非null判斷。
b雷猪、再比如:我不希望保留用戶操作睛竣,你可以使用remove(),然后add()求摇;或者使用replace()這個和remove,add是相同的效果射沟。
c、remove和detach有一點細微的區(qū)別月帝,在不考慮回退棧的情況下躏惋,remove會銷毀整個Fragment實例,而detach則只是銷毀其視圖結(jié)構(gòu)嚷辅,實例并不會被銷毀簿姨。那么二者怎么取舍使用呢?如果你的當前Activity一直存在,那么在不希望保留用戶操作的時候扁位,你可以優(yōu)先使用detach准潭。
d、在Activity的onCreate方法里面調(diào)用transaction.add() 方法添加Fragment時進行如下處理:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(savedInstanceState == null) {
//只有當savedInstanceState == null才調(diào)用transaction.add() 方法
transaction.add(...) ;
}
}
這樣做的目的是為了避免重復實例化一個相同的新的Fragment并重復添加到Activity上域仇,因為當某些原因(屏幕旋轉(zhuǎn)刑然,長時間處于后臺等)Activity重新調(diào)用onCreate恢復頁面時,它會從savedInstanceState中取出原來保存的數(shù)據(jù)(包括Fragment的實例)進行恢復暇务,這個時候就不必重新new了泼掠,Android系統(tǒng)會自動把保存的數(shù)據(jù)取出并還原的。
Fragment回退棧
類似與Android系統(tǒng)為Activity維護一個任務棧垦细,我們也可以通過Activity維護一個回退棧來保存每次Fragment事務發(fā)生的變化择镇。如果你將Fragment任務添加到回退棧,當用戶點擊后退按鈕時括改,將看到上一次的保存的Fragment腻豌。一旦Fragment完全從后退棧中彈出,用戶再次點擊后退鍵嘱能,則退出當前Activity吝梅。
- 添加到回退棧的方法:FragmentTransaction.addToBackStack(String);
eg:FragmentTransaction.addToBackStack(null); - 根Fragment(第一個添加到Activity的Fragment)一般不添加到回退棧當中,因為如果根Fragment綁定的Activity上面沒有任何UI控件的話惹骂,點擊Back鍵將返回上一次的保存的Fragment苏携,這時候由于這個Fragment已經(jīng)是最根部Fragment了,就會返回到空白的Activity界面了析苫。
- 使用replace方法切換Fragment時(replace是remove和add的合體)兜叨,如果不添加事務到回退棧,前一個Fragment實例會被銷毀衩侥。調(diào)用FragmentTransaction.addToBackStack(null)將當前的事務添加到回退棧后国旷,前一個Fragment實例不會被銷毀,但是視圖層次依然會被銷毀茫死,即會調(diào)用前一個Fragment的onDestoryView方法和替換Fragment的onCreateView方法跪但。
Fragment與Activity的交互
- 組件獲取
- Fragment獲得Activity中的組件: getActivity().findViewById(R.id.list);
- Fragment中獲得Context的方法:getActivity()峦萎;
- Activity獲得Fragment中的組件(根據(jù)id和tag都可以):
getFragmentManager().findFragmentById(R.id.fragment1);
getFragmentManager().findFragmentByTag("fragment1") ;
- 數(shù)據(jù)傳遞
①Activit傳遞數(shù)據(jù)給Fragment:
在Activity中創(chuàng)建Bundle數(shù)據(jù)包,調(diào)用Fragment實例的setArguments(bundle) 從而將Bundle數(shù)據(jù)包傳給Fragment,然后Fragment中調(diào)用getArguments獲得 Bundle對象,然后進行解析就可以了屡久。
②Fragment傳遞數(shù)據(jù)給Activity
在Fragment中定義一個內(nèi)部回調(diào)接口,再讓包含該Fragment的Activity實現(xiàn)該回調(diào)接口, Fragment就可以通過回調(diào)接口傳數(shù)據(jù)了,其實就是接口回調(diào)。
Step 1:定義一個回調(diào)接口:(Fragment中)
/*接口*/
public interface CallBack{
/*定義一個獲取信息的方法*/
public void getResult(String result);
}
Step 2:接口回調(diào)(Fragment中)
/*本地私有回調(diào)接口爱榔,調(diào)用出通過回調(diào)接口方法傳入*/
private CallBack mCallback;
/*設置回調(diào)接口*/
public void getData(CallBack callBack){
this.mCallback=callBack;
}
/*將需要傳遞給Activity數(shù)據(jù)的地方將數(shù)據(jù)msg傳遞過去*/
mCallback.getResult(msg);
Step 3:使用接口回調(diào)方法讀數(shù)據(jù)(Activity中)
/* 使用接口回調(diào)的方法獲取數(shù)據(jù) */
fragment.getData(new FirstFragment.CallBack() {
@Override
public void getResult(String result) {
//將回傳數(shù)據(jù)進行處理
}
});
- 在Fragment定義接口的抽象方法時,你要傳什么類型的數(shù)據(jù)參數(shù)就設置什么類型;
③Fragment與Fragment之間的數(shù)據(jù)互傳
其實這很簡單,找到要接受數(shù)據(jù)的fragment對象,直接調(diào)用setArguments傳數(shù)據(jù)進去就可以了 通常的話是replace時,即fragment跳轉(zhuǎn)的時候傳數(shù)據(jù)的,那么只需要在初始化要跳轉(zhuǎn)的Fragment 后調(diào)用他的setArguments方法傳入數(shù)據(jù)即可!
如果是兩個Fragment需要即時傳數(shù)據(jù),而非跳轉(zhuǎn)的話,就需要先在Activity獲得f1傳過來的數(shù)據(jù), 再傳到f2了,就是以Activity為媒介被环。
使用Fragment.setArguments(Bundle bundle)來傳遞參數(shù)
首先我們來看下AndroidStudio提供的創(chuàng)建BlankFragment的工廠方法代碼:
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
* @param param1 Parameter 1.
* @param param2 Parameter 2.
* @return A new instance of fragment BlankFragment.
*/
// TODO: Rename and change types and number of parameters
public static BlankFragment newInstance(String param1, String param2) {
BlankFragment fragment = new BlankFragment();
Bundle args = new Bundle();
args.putString(ARG_PARAM1, param1);
args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
return fragment;
}
從中我們可以看到官方建議我們用這種方法創(chuàng)建Fragment,這樣可以傳遞參數(shù)給Fragment详幽。但是為什么不推薦我們使用帶參的構(gòu)造方法傳遞參數(shù)呢筛欢?
原因就是因為當設備配置參數(shù)發(fā)生變化浸锨,如橫豎屏切換時,系統(tǒng)會重新恢復(會從savedInstanceState中取出原來保存的數(shù)據(jù)版姑,包括Activity柱搜、Fragment的實例進行恢復)創(chuàng)建Activity,同時也會重新構(gòu)建它所管理的Fragment(默認調(diào)用無參的那個構(gòu)造方法進行創(chuàng)建)剥险,原先的Fragment構(gòu)造方法傳遞過來的參數(shù)將會全部丟失聪蘸,但是通過Fragment.setArguments(Bundle bundle)方法設置的bundle會通過savedInstanceState保留下來。所以盡量使用Fragment.setArguments(Bundle bundle)方式來傳遞參數(shù)表制。