http://blog.csdn.net/lmj623565791/article/details/37970961
1、Fragment的產生與介紹
Android運行在各種各樣的設備中,有小屏幕的手機,超大屏的平板甚至電視。針對屏幕尺寸的差距,很多情況下,都是先針對手機開發一套App,然后拷貝一份,修改布局以適應平板神馬超級大屏的。難道無法做到一個App可以同時適應手機和平板么,當然了,必須有啊。Fragment的出現就是為了解決這樣的問題。你可以把Fragment當成Activity的一個界面的一個組成部分,甚至Activity的界面可以完全有不同的Fragment組成,更帥氣的是Fragment擁有自己的生命周期和接收、處理用戶的事件,這樣就不必在Activity寫一堆控件的事件處理的代碼了。更為重要的是,你可以動態的添加、替換和移除某個Fragment。
2、Fragment的生命周期
Fragment必須是依存與Activity而存在的,因此Activity的生命周期會直接影響到Fragment的生命周期。官網這張圖很好的說明了兩者生命周期的關系:

可以看到Fragment比Activity多了幾個額外的生命周期回調方法:
onAttach(Activity)
當Fragment與Activity發生關聯時調用。
onCreateView(LayoutInflater, ViewGroup,Bundle)
創建該Fragment的視圖
onActivityCreated(Bundle)
當Activity的onCreate方法返回時調用
onDestoryView()
與onCreateView想對應,當該Fragment的視圖被移除時調用
onDetach()
與onAttach相對應,當Fragment與Activity關聯被取消時調用
注意:除了onCreateView,其他的所有方法如果你重寫了,必須調用父類對於該方法的實現,
3、靜態的使用Fragment
這是使用Fragment最簡單的一種方式,把Fragment當成普通的控件,直接寫在Activity的布局文件中。步驟:
1、繼承Fragment,將相應的布局文件和Fragment綁定,代碼編寫控制邏輯
2、主布局文件里面添加已經控制邏輯的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" > <fragment android:id="@+id/id_fragment_title" android:name="com.example.androidexpriment.TitleFragment" android:layout_width="fill_parent" android:layout_height="45dp" /> <fragment android:layout_below="@id/id_fragment_title" android:id="@+id/id_fragment_content" android:name="com.example.androidexpriment.ContentFragment" android:layout_width="fill_parent" android:layout_height="fill_parent" /> </RelativeLayout>
是不是把Fragment當成普通的View一樣聲明在Activity的布局文件中,然后所有控件的事件處理等代碼都由各自的Fragment去處理,瞬間覺得Activity好干凈有木有~~代碼的可讀性、復用性以及可維護性是不是瞬間提升了。
4、Fragment家族常用的API
Fragment常用的三個類:
android.app.Fragment 主要用於定義Fragment
android.app.FragmentManager 主要用於在Activity中操作Fragment
android.app.FragmentTransaction 保證一些列Fragment操作的原子性,熟悉事務這個詞,一定能明白~
a、獲取FragmentManage的方式:
getFragmentManager() // v4中,getSupportFragmentManager
b、主要的操作都是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
detach()
會將view從UI中移除,和remove()不同,此時fragment的狀態依然由FragmentManager維護。
attach()
重建view視圖,附加到UI上並顯示。
transatcion.commit()//提交一個事務
注意:常用Fragment的哥們,可能會經常遇到這樣Activity狀態不一致:State loss這樣的錯誤。主要是因為:commit方法一定要在Activity.onSaveInstance()之前調用。
上述,基本是操作Fragment的所有的方式了,在一個事務開啟到提交可以進行多個的添加、移除、替換等操作。
值得注意的是:如果你喜歡使用Fragment,一定要清楚這些方法,哪個會銷毀視圖,哪個會銷毀實例,哪個僅僅只是隱藏,這樣才能更好的使用它們。
a、比如:我在FragmentA中的EditText填了一些數據,當切換到FragmentB時,如果希望會到A還能看到數據,則適合你的就是hide和show;也就是說,希望保留用戶操作的面板,你可以使用hide和show,當然了不要使勁在那new實例,進行下非null判斷。
b、再比如:我不希望保留用戶操作,你可以使用remove(),然后add();或者使用replace()這個和remove,add是相同的效果。
c、remove和detach有一點細微的區別,在不考慮回退棧的情況下,remove會銷毀整個Fragment實例,而detach則只是銷毀其視圖結構,實例並不會被銷毀。那么二者怎么取舍使用呢?如果你的當前Activity一直存在,那么在不希望保留用戶操作的時候,你可以優先使用detach。
5、動態的使用Fragment
上面已經演示了,最簡單的使用Fragment的方式~下面介紹如何動態的添加、更新、以及刪除Fragment
activity_main.xml
<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" android:background="#ccc444"> <FrameLayout android:id="@+id/id_content" android:layout_width="fill_parent" android:layout_height="fill_parent" > </FrameLayout> </RelativeLayout>
MainActivity.java
package com.example.androidexpriment; import android.app.Activity; import android.app.FragmentManager; import android.app.FragmentTransaction; import android.os.Bundle; import android.view.Window; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.activity_main); FragmentManager fm = getFragmentManager(); FragmentTransaction tx = fm.beginTransaction(); tx.add(R.id.id_content, new FragmentOne(),"ONE"); tx.commit(); } }
很簡單,直接將FragmentOne添加到布局文件中的FrameLayout中,注意這里並沒有調用FragmentTransaction.addToBackStack(String),而是將back按鈕的控制邏輯放在fragment中執行
下面是FragmentOne
fragment_one.xml
<?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:background="#ffffff" android:orientation="vertical" > <Button android:id="@+id/id_fragment_one_btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="3dp" android:layout_gravity="center"/> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#333333" android:textSize="20sp" /> </LinearLayout>
FragmentOne.java
package com.example.androidexpriment; import android.app.Fragment; import android.app.FragmentManager; import android.app.FragmentTransaction; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.Button; public class FragmentOne extends Fragment implements OnClickListener { private Button mBtn; @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; } @Override public void onClick(View v) { FragmentTwo fTwo = new FragmentTwo(); FragmentManager fm= getFragmentManager(); FragmentTransaction tx= fm.beginTransaction(); tx.replace(R.id.id_content, fTwo, "TWO"); tx.addToBackStack(null); tx.commit(); } }
我們在點擊FragmentOne中的按鈕時,使用了replace方法,如果你看了前一篇博客,一定記得replace是remove和add的合體,並且如果不添加事務到回退棧,前一個Fragment實例會被銷毀。這里很明顯,我們調用tx.addToBackStack(null);將當前的事務添加到了回退棧,所以FragmentOne實例不會被銷毀,但是視圖層次依然會被銷毀,即會調用onDestoryView和onCreateView,證據就是:仔細看上面的效果圖,我們在跳轉前在文本框輸入的內容,在用戶Back得到第一個界面的時候不見了。
插曲:replace()方法可以通過remove,add結合達到相同的效果。
tx.remove(fm.findFragmentById(R.layout.fragment_one));//error // tx.remove(getTargetFragment());//error tx.remove(fm.findFragmentByTag("ONE"));//right tx.add(R.id.id_content,fTwo);
使用前兩種方法都會報空指針的錯誤。
java.lang.NullPointerException: Attempt to write to field 'int android.app.Fragment.mNextAnim' on a null object reference

一開始想通過查找源碼的方式解決,在BackStackRecord.java中發現幾種操作方法,mNextAnim的賦值主要在
f.mNextAnim = op.enterAnim;
f.mNextAnim = op.exitAnim;
1 public void run() { 2 if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Run: " + this); 3 4 if (mAddToBackStack) { 5 if (mIndex < 0) { 6 throw new IllegalStateException("addToBackStack() called after commit()"); 7 } 8 } 9 10 bumpBackStackNesting(1); 11 12 Op op = mHead; 13 while (op != null) { 14 switch (op.cmd) { 15 case OP_ADD: { 16 Fragment f = op.fragment; 17 f.mNextAnim = op.enterAnim; 18 mManager.addFragment(f, false); 19 } break; 20 case OP_REPLACE: { 21 Fragment f = op.fragment; 22 if (mManager.mAdded != null) { 23 for (int i=0; i<mManager.mAdded.size(); i++) { 24 Fragment old = mManager.mAdded.get(i); 25 if (FragmentManagerImpl.DEBUG) Log.v(TAG, 26 "OP_REPLACE: adding=" + f + " old=" + old); 27 if (f == null || old.mContainerId == f.mContainerId) { 28 if (old == f) { 29 op.fragment = f = null; 30 } else { 31 if (op.removed == null) { 32 op.removed = new ArrayList<Fragment>(); 33 } 34 op.removed.add(old); 35 old.mNextAnim = op.exitAnim; 36 if (mAddToBackStack) { 37 old.mBackStackNesting += 1; 38 if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of " 39 + old + " to " + old.mBackStackNesting); 40 } 41 mManager.removeFragment(old, mTransition, mTransitionStyle); 42 } 43 } 44 } 45 } 46 if (f != null) { 47 f.mNextAnim = op.enterAnim; 48 mManager.addFragment(f, false); 49 } 50 } break; 51 case OP_REMOVE: { 52 Fragment f = op.fragment; 53 f.mNextAnim = op.exitAnim; 54 mManager.removeFragment(f, mTransition, mTransitionStyle); 55 } break; 56 case OP_HIDE: { 57 Fragment f = op.fragment; 58 f.mNextAnim = op.exitAnim; 59 mManager.hideFragment(f, mTransition, mTransitionStyle); 60 } break; 61 case OP_SHOW: { 62 Fragment f = op.fragment; 63 f.mNextAnim = op.enterAnim; 64 mManager.showFragment(f, mTransition, mTransitionStyle); 65 } break; 66 case OP_DETACH: { 67 Fragment f = op.fragment; 68 f.mNextAnim = op.exitAnim; 69 mManager.detachFragment(f, mTransition, mTransitionStyle); 70 } break; 71 case OP_ATTACH: { 72 Fragment f = op.fragment; 73 f.mNextAnim = op.enterAnim; 74 mManager.attachFragment(f, mTransition, mTransitionStyle); 75 } break; 76 default: { 77 throw new IllegalArgumentException("Unknown cmd: " + op.cmd); 78 } 79 } 80 81 op = op.next; 82 } 83 84 mManager.moveToState(mManager.mCurState, mTransition, 85 mTransitionStyle, true); 86 87 if (mAddToBackStack) { 88 mManager.addBackStackState(this); 89 } 90 }
依照報錯信息查找——>FragmentManager.execPendingActions 1454

注意源碼中的FragmentManager是個抽象類,真正發揮作用的是FragmentManagerImpl
final class FragmentManagerImpl extends FragmentManager
在Fragment中,FragmentManager fm= getFragmentManager();
在源碼中
FragmentManagerImpl mFragmentManager;
final public FragmentManager getFragmentManager() { return mFragmentManager; }
同樣FragmentTransaction也是個抽象類
final class BackStackRecord extends FragmentTransaction implements FragmentManager.BackStackEntry, Runnable { }
FragmentTransaction tx= fm.beginTransaction();其實源碼中是這樣的
@Override public FragmentTransaction beginTransaction() { return new BackStackRecord(this); }
為什么
f.mNextAnim = NULL
源碼扯的很深,報錯其實很好理解,你只有曾經add過的Fragment,你才可以remove。而在主Activity tx.add(R.id.id_content, new FragmentOne(),"ONE");,通過布局文件找fragment肯定是不可取的,因為一個布局可以例化出多個fragmetn,所以為了能找到創建的Fragment,推薦加上Tag。
接下來FragmentTwo
布局文件:
<?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" > <Button android:id="@+id/id_fragment_two_btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" /> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:textSize="20sp" android:textStyle="bold" /> </LinearLayout>
package com.example.androidexpriment;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
public class FragmentTwo extends Fragment implements OnClickListener
{
private Button mBtn ;
@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)
{
FragmentThree fThree = new FragmentThree();
FragmentManager fm = getFragmentManager();
FragmentTransaction tx = fm.beginTransaction();
tx.hide(this);
tx.add(R.id.id_content , fThree, "THREE");
// tx.replace(R.id.id_content, fThree, "THREE");
tx.addToBackStack(null);
tx.commit();
}
}
這里點擊時,我們沒有使用replace,而是先隱藏了當前的Fragment,然后添加了FragmentThree的實例,最后將事務添加到回退棧。這樣做的目的是為了給大家提供一種方案:如果不希望視圖重繪該怎么做,請再次仔細看效果圖,我們在FragmentTwo的EditText填寫的內容,用戶Back回來時,數據還在~~~
最后FragmentThree就是簡單的Toast了:
package com.example.androidexpriment; import android.app.Fragment; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.Button; import android.widget.Toast; public class FragmentThree extends Fragment implements OnClickListener { private Button mBtn; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_three, container, false); mBtn = (Button) view.findViewById(R.id.id_fragment_three_btn); mBtn.setOnClickListener(this); return view; } @Override public void onClick(View v) { Toast.makeText(getActivity(), " i am a btn in Fragment three", Toast.LENGTH_SHORT).show(); } }
好了,經過上面的介紹,應該已經知道Fragment回退棧是怎么一回事了,以及hide,replace等各自的應用的場景。
這里極其注意一點:上面的整體代碼不具有任何參考價值,純粹為了顯示回退棧,在后面講解了Fragment與Activity通信以后,會重構上面的代碼!
2、Fragment與Activity通信
因為所有的Fragment都是依附於Activity的,所以通信起來並不復雜,大概歸納為:
a、如果你Activity中包含自己管理的Fragment的引用,可以通過引用直接訪問所有的Fragment的public方法
b、如果Activity中未保存任何Fragment的引用,那么沒關系,每個Fragment都有一個唯一的TAG或者ID,可以通過getFragmentManager.findFragmentByTag()獲得任何Fragment實例,然后進行操作。
c、在Fragment中可以通過getActivity得到當前綁定的Activity的實例,然后進行操作。
注:如果在Fragment中需要Context,可以通過調用getActivity(),如果該Context需要在Activity被銷毀后還存在,則使用getActivity().getApplicationContext()。
3、Fragment與Activity通信的最佳實踐
因為要考慮Fragment的重復使用,所以必須降低Fragment與Activity的耦合,而且Fragment更不應該直接操作別的Fragment,畢竟Fragment操作應該由它的管理者Activity來決定。
下面我通過兩種方式的代碼,分別重構,FragmentOne和FragmentTwo的點擊事件,以及Activity對點擊事件的響應:
首先看FragmentOne
package com.example.androidexpriment; import android.app.Fragment; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.Button; public class FragmentOne extends Fragment implements OnClickListener { private Button mBtn; /** * 設置按鈕點擊的回調 * @author zhy * */ 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(); } } }
可以看到現在的FragmentOne不和任何Activity耦合,任何Activity都可以使用;並且我們聲明了一個接口,來回調其點擊事件,想要管理其點擊事件的Activity實現此接口就即可。可以看到我們在onClick中首先判斷了當前綁定的Activity是否實現了該接口,如果實現了則調用。
再看FragmentTwo
package com.example.androidexpriment; import android.app.Fragment; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.Button; public class FragmentTwo extends Fragment implements OnClickListener { private Button mBtn ; private FTwoBtnClickListener fTwoBtnClickListener ; public interface FTwoBtnClickListener { void onFTwoBtnClick(); } //設置回調接口 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不僅需要實現該接口,還必須顯示調用mFTwo.setfTwoBtnClickListener(this)。
最后看Activity :
package com.example.androidexpriment; import com.example.androidexpriment.FragmentOne.FOneBtnClickListener; import com.example.androidexpriment.FragmentTwo.FTwoBtnClickListener; import android.app.Activity; import android.app.FragmentManager; import android.app.FragmentTransaction; import android.os.Bundle; import android.view.Window; 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); requestWindowFeature(Window.FEATURE_NO_TITLE); 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 按鈕點擊時的回調 */ @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 按鈕點擊時的回調 */ @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(); } }
代碼重構結束,與開始的效果一模一樣。
仔細品味下兩類寫法的差別,第一種是在Fragment把實現邏輯都寫死啦,顯然這種代碼就沒什么復用價值,而第二種只是在Fragment了定義了一種抽象的操作,即可以響應點擊事件,至於點擊事件是什么,在主activity里面決定。
上面兩種通信方式都是值得推薦的,隨便選擇一種自己喜歡的。Activity擔任的是Fragment間類似總線一樣的角色,應當由它決定Fragment如何操作。另外雖然Fragment不能響應Intent打開,但是Activity可以,Activity可以接收Intent,然后根據參數判斷顯示哪個Fragment。
package com.example.androidexpriment; import android.app.Fragment; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.Button; /*public class FragmentOne extends Fragment implements OnClickListener{ private Button mbtn; public interface FOneBtnClickListener{ void onFOneBtnClick(); } //得到該Fragment對應的Activity @Override public void onClick(View v) { if(getActivity() instanceof FOneBtnClickListener) { ((FOneBtnClickListener)getActivity()).onFOneBtnClick(); } } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_one,null); mbtn= (Button) (view).findViewById(R.id.id_fragment_one_btn); mbtn.setOnClickListener(this); return view; } }*/ public class FragmentOne extends Fragment implements OnClickListener{ private Button mbtn; private FOneBtnClickListener foneBtnClickListener; public interface FOneBtnClickListener{ void onFOneBtnClick(); } public void setFoneBtnClickListenr(FOneBtnClickListener f){ foneBtnClickListener = f; } @Override public void onClick(View v) { // TODO Auto-generated method stub foneBtnClickListener.onFOneBtnClick(); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_one, null); mbtn = (Button) view.findViewById(R.id.id_fragment_one_btn); mbtn.setOnClickListener(this); return view; } }
4、如何處理運行時配置發生變化
運行時配置發生變化,最常見的就是屏幕發生旋轉,如果你不知道如何處理屏幕變化可以參考:Android 屏幕旋轉 處理 AsyncTask 和 ProgressDialog 的最佳方案
這里提一下:很多人覺得強制設置屏幕的方向就可以了,但是有一點,當你的應用被至於后台(例如用戶點擊了home),長時間沒有返回的時候,你的應用也會被重新啟動。比如上例:如果你把上面的例子你至於FragmentThree界面,然后處於后台狀態,長時間后你會發現當你再次通過home打開時,上面FragmentThree與FragmentOne疊加在一起,這就是因為你的Activity重新啟動,在原來的FragmentThree上又繪制了一個FragmentOne。
好了,下面看一段代碼:
Activity:
package com.example.androidexpriment; import android.app.Activity; import android.app.FragmentManager; import android.app.FragmentTransaction; import android.os.Bundle; import android.util.Log; import android.view.Window; public class MainActivity extends Activity { private FragmentOne mFOne; private static final String TAG = "FragmentOne"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.e(TAG, savedInstanceState+""); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.activity_main); /* if(savedInstanceState == null) { mFOne = new FragmentOne(); FragmentManager fm = getFragmentManager(); FragmentTransaction tx = fm.beginTransaction(); tx.add(R.id.id_content, mFOne, "ONE"); tx.commit(); }*/ mFOne = new FragmentOne(); FragmentManager fm = getFragmentManager(); FragmentTransaction tx = fm.beginTransaction(); tx.add(R.id.id_content, mFOne, "ONE"); tx.commit(); } }
Fragment
package com.example.androidexpriment; import android.app.Fragment; import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.Button; public class FragmentOne extends Fragment { private static final String TAG = "FragmentOne"; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { Log.e(TAG, "onCreateView"); View view = inflater.inflate(R.layout.fragment_one, container, false); return view; } @Override public void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); Log.e(TAG, "onCreate"); } @Override public void onDestroyView() { // TODO Auto-generated method stub super.onDestroyView(); Log.e(TAG, "onDestroyView"); } @Override public void onDestroy() { // TODO Auto-generated method stub super.onDestroy(); Log.e(TAG, "onDestroy"); } }
很簡單的代碼,當你運行之后,不斷的旋轉屏幕,你會發現每旋轉一次屏幕,后台log會打印出兩套生命周期的回調。相當與有兩個Fragment

這是為什么呢,因為當屏幕發生旋轉,Activity發生重新啟動,默認的Activity中的Fragment也會跟着Activity重新創建;這樣造成當旋轉的時候,本身存在的Fragment會重新啟動,然后當執行Activity的onCreate時,又會再次實例化一個新的Fragment,這就是出現的原因。兩個
那么如何解決呢:
其實通過檢查onCreate的參數Bundle savedInstanceState就可以判斷,當前是否發生Activity的重新創建,代碼上面已經有啦。
